react-native-elements#ButtonGroup TypeScript Examples

The following examples show how to use react-native-elements#ButtonGroup. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ClientListScreen.tsx    From react-native-network-client with Apache License 2.0 5 votes vote down vote up
export default function ClientListScreen({
    navigation,
    route,
}: ClientListScreenProps) {
    const [clients, setClients] = useState<NetworkClientItem[]>([]);

    useEffect(() => {
        if (!clients.length) {
            createTestClients().then((testClients) =>
                setClients([...clients, ...testClients])
            );
        }
    }, []);

    useEffect(() => {
        if (route.params?.createdClient) {
            setClients([...clients, route.params.createdClient]);
        }
    }, [route.params?.createdClient]);

    const deleteClient = (clientIndex: number) => {
        setClients(clients.filter((_, index) => clientIndex !== index));
    };

    const renderItem = ({
        item,
        index,
    }: {
        item: NetworkClientItem;
        index: number;
    }) => (
        <ClientListItem
            index={index}
            item={item}
            deleteClient={deleteClient}
            navigate={navigation.navigate}
        />
    );

    const buttons = [
        {
            title: "Add API Client",
            action: () => navigation.navigate("CreateAPIClient"),
        },
        {
            title: "Add WebSocket Client",
            action: () => navigation.navigate("CreateWebSocketClient"),
        },
    ];

    const onButtonPress = (index: number) => buttons[index].action();

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <FlatList
                data={clients}
                renderItem={renderItem}
                keyExtractor={networkClientKeyExtractor}
                testID="client_list.scroll_view"
            />
            <ButtonGroup
                buttons={buttons.map((button) => button.title)}
                onPress={onButtonPress}
            />
        </SafeAreaView>
    );
}
Example #2
Source File: FilePickerButtonGroup.tsx    From react-native-network-client with Apache License 2.0 4 votes vote down vote up
FilePickerButtonGroup = (props: FilePickerButtonGroupProps) => {
    const hasPhotoLibraryPermissions = async () => {
        if (Platform.OS === "android") return true;

        let result = await check(PERMISSIONS.IOS.PHOTO_LIBRARY);
        if (result === RESULTS.GRANTED || result === RESULTS.LIMITED) {
            return true;
        } else if (
            result !== RESULTS.BLOCKED &&
            result !== RESULTS.UNAVAILABLE
        ) {
            await request(PERMISSIONS.IOS.PHOTO_LIBRARY);
            hasPhotoLibraryPermissions();
        }

        return false;
    };

    const pickFile = async () => {
        const hasPermission = await hasPhotoLibraryPermissions();
        if (hasPermission) {
            try {
                const result = await DocumentPicker.pickSingle({
                    type: [DocumentPicker.types.allFiles],
                    copyTo: "cachesDirectory",
                });

                const file = { ...result, uri: result.fileCopyUri };
                props.onFilePicked(file);
            } catch (err) {
                if (DocumentPicker.isCancel(err as Error)) {
                    // User cancelled the picker, exit any dialogs or menus and move on
                } else {
                    throw err;
                }
            }
        }
    };

    const pickImage = async () => {
        const hasPermission = await hasPhotoLibraryPermissions();
        if (hasPermission) {
            launchImageLibrary({ quality: 1, mediaType: "photo" }, (result) => {
                if (result.assets) {
                    const file = {
                        name: result.assets[0].fileName,
                        type: result.assets[0].type,
                        size: result.assets[0].fileSize,
                        uri: result.assets[0].uri,
                    };
                    props.onFilePicked(file);
                }
            });
        }
    };

    const attachFile = async (fileContent: FileContent) => {
        const file: File = await createNativeFile(fileContent);
        props.onFilePicked(file);
    };
    const attachSampleImage = async () => {
        await attachFile(sampleImage);
    };
    const attachSampleText = async () => {
        await attachFile(sampleText);
    };

    const buttons = [
        { title: "Select Image", onPress: pickImage, android: true },
        { title: "Select File", onPress: pickFile, android: true },
        { title: "Attach Image", onPress: attachSampleImage, android: false },
        { title: "Attach Text", onPress: attachSampleText, android: false },
    ];

    const onButtonPress = (index: number) => buttons[index].onPress();

    return (
        <ButtonGroup
            buttons={buttons
                .filter((b) => (Platform.OS === "android" ? b.android : true))
                .map((button) => button.title)}
            onPress={onButtonPress}
            disabled={props.disabled}
        />
    );
}
Example #3
Source File: P12Inputs.tsx    From react-native-network-client with Apache License 2.0 4 votes vote down vote up
P12Inputs = (props: P12InputsProps) => {
    const [url, setUrl] = useState("");

    const hasPhotoLibraryPermissions = async () => {
        const permission =
            Platform.OS === "ios"
                ? PERMISSIONS.IOS.PHOTO_LIBRARY
                : PERMISSIONS.ANDROID.READ_EXTERNAL_STORAGE;
        let result = await check(permission);
        if (result === RESULTS.GRANTED || result === RESULTS.LIMITED) {
            return true;
        } else if (
            result !== RESULTS.BLOCKED &&
            result !== RESULTS.UNAVAILABLE
        ) {
            await request(permission);
            hasPhotoLibraryPermissions();
        }

        console.log(result);

        return false;
    };

    const pickCertificate = async () => {
        const hasPermission = await hasPhotoLibraryPermissions();
        if (hasPermission) {
            try {
                const result = await DocumentPicker.pickSingle({
                    type: [DocumentPicker.types.allFiles],
                });

                if (result.fileCopyUri) {
                    props.onSelectP12(result.fileCopyUri);
                }
            } catch (err) {
                if (DocumentPicker.isCancel(err as Error)) {
                    // User cancelled the picker, exit any dialogs or menus and move on
                } else {
                    throw err;
                }
            }
        }
    };

    const downloadCertificate = async () => {
        const file: File = await downloadToNativeFile(url, clientCertP12);
        props.onSelectP12(`${file.uri}`);
    };

    const clearP12Configuration = () => {
        props.onSelectP12("");
        props.onPasswordChange(undefined);
    };

    const buttons = [
        { title: "Select", onPress: pickCertificate },
        { title: "Download", onPress: downloadCertificate },
    ];

    const onButtonPress = (index: number) => buttons[index].onPress();

    const rightIcon = (
        <View style={{ width: 250 }}>
            {Boolean(props.path) ? (
                <View style={{ flexDirection: "column", height: 50 }}>
                    <View style={{ flexDirection: "row" }}>
                        <View style={{ flex: 1 }}>
                            <Text
                                numberOfLines={2}
                                ellipsizeMode="middle"
                                testID="p12_inputs.path.text"
                            >
                                {props.path}
                            </Text>
                        </View>
                        <Button
                            type="clear"
                            icon={<Icon name="cancel" size={24} />}
                            onPress={clearP12Configuration}
                        />
                    </View>
                    <Input
                        placeholder="password"
                        value={props.password}
                        onChangeText={props.onPasswordChange}
                        autoCapitalize="none"
                        testID="p12_inputs.password.input"
                    />
                </View>
            ) : (
                <Input
                    placeholder="Download URL"
                    onChangeText={setUrl}
                    autoCapitalize="none"
                    testID="p12_inputs.download_url.input"
                />
            )}
        </View>
    );

    return (
        <>
            <Input
                placeholder={props.title}
                disabled={true}
                style={{
                    fontWeight: "bold",
                    fontSize: 17,
                    opacity: 1,
                }}
                inputContainerStyle={{
                    borderColor: "rgba(255,255,255,0)",
                }}
                rightIcon={rightIcon}
                labelStyle={{ flex: 12, flexWrap: "wrap" }}
            />
            {Boolean(!props.path) ? (
                <View
                    style={{
                        flex: 1,
                        alignItems: "flex-end",
                        marginTop: -30,
                        paddingRight: 4,
                        paddingBottom: 10,
                    }}
                >
                    <ButtonGroup
                        containerStyle={{ width: 200 }}
                        buttons={buttons.map((button) => button.title)}
                        onPress={onButtonPress}
                    />
                </View>
            ) : (
                <View style={{ height: 20 }} />
            )}
        </>
    );
}
Example #4
Source File: RetryPolicyConfiguration.tsx    From react-native-network-client with Apache License 2.0 4 votes vote down vote up
RetryPolicyConfiguration = (props: RetryPolicyConfigurationProps) => {
    const [statusCodesText, setStatusCodesText] = useState(
        props.statusCodes?.join(",") || ""
    );

    const onLinearPress = () =>
        onCheckBoxPress(
            props.policyType === Constants.RETRY_TYPES.LINEAR_RETRY
                ? undefined
                : Constants.RETRY_TYPES.LINEAR_RETRY
        );
    const onExponentialPress = () =>
        onCheckBoxPress(
            props.policyType === Constants.RETRY_TYPES.EXPONENTIAL_RETRY
                ? undefined
                : Constants.RETRY_TYPES.EXPONENTIAL_RETRY
        );
    const onCheckBoxPress = (policyType?: RetryTypes) => {
        props.onTypeSelected(policyType);
    };
    const linearRetryChecked =
        props.policyType === Constants.RETRY_TYPES.LINEAR_RETRY;
    const exponentialRetryChecked =
        props.policyType === Constants.RETRY_TYPES.EXPONENTIAL_RETRY;

    const buttonMethods = ["get", "post", "put", "patch", "delete"];
    const [selectedMethodsIndex, setSelectedMethodsIndex] = useState<number[]>([
        0,
        1,
    ]);

    if (props.setRetryMethods !== undefined) {
        useEffect(() => {
            const selectedMethods = selectedMethodsIndex.map(
                (index) => buttonMethods[index]
            );
            props.setRetryMethods!(selectedMethods);
        }, [selectedMethodsIndex]);
    }

    const PolicyTypeCheckbox = () => (
        <View style={{ flex: 1, flexDirection: "row" }}>
            <CheckBox
                title={`Linear [${linearRetryChecked}]`}
                checked={linearRetryChecked}
                onPress={onLinearPress}
                iconType="ionicon"
                checkedIcon="ios-checkmark-circle"
                uncheckedIcon="ios-checkmark-circle"
                iconRight
                containerStyle={{
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: "transparent",
                }}
            />
            <CheckBox
                title={`Exponential [${exponentialRetryChecked}]`}
                checked={exponentialRetryChecked}
                onPress={onExponentialPress}
                iconType="ionicon"
                checkedIcon="ios-checkmark-circle"
                uncheckedIcon="ios-checkmark-circle"
                iconRight
                containerStyle={{
                    padding: 0,
                    borderWidth: 0,
                    backgroundColor: "transparent",
                }}
            />
        </View>
    );

    const updateStatusCodes = () => {
        const statusCodes = statusCodesText
            .split(",")
            .map((code) => {
                return parseInt(code.trim());
            })
            .filter((code) => !isNaN(code));

        props.setStatusCodes(statusCodes);
    };

    return (
        <>
            <Input
                placeholder="Retry Policy"
                disabled={true}
                style={{ fontWeight: "bold", fontSize: 17, opacity: 1 }}
                containerStyle={{ height: 50 }}
                inputContainerStyle={{
                    borderColor: "rgba(255,255,255,0)",
                }}
                labelStyle={{ flex: 12, flexWrap: "wrap", height: 100 }}
            />

            <PolicyTypeCheckbox />

            {props.policyType && (
                <NumericInput
                    title="Retry limit"
                    value={props.retryLimit}
                    onChange={props.setRetryLimit}
                    minValue={0}
                    testID="retry_policy_configuration.retry_limit.input"
                />
            )}

            {props.policyType === Constants.RETRY_TYPES.LINEAR_RETRY && (
                <NumericInput
                    title="Retry interval"
                    value={props.retryInterval}
                    onChange={props.setRetryInterval}
                    minValue={1}
                    testID="retry_policy_configuration.retry_interval.input"
                />
            )}
            {props.policyType === Constants.RETRY_TYPES.EXPONENTIAL_RETRY && (
                <>
                    <NumericInput
                        title="Exponential backoff base"
                        value={props.exponentialBackoffBase}
                        onChange={props.setExponentialBackoffBase}
                        minValue={2}
                        testID="retry_policy_configuration.exponential_backoff_base.input"
                    />
                    <NumericInput
                        title="Exponential backoff scale"
                        value={props.exponentialBackoffScale}
                        onChange={props.setExponentialBackoffScale}
                        minValue={0}
                        valueType="real"
                        step={0.1}
                        testID="retry_policy_configuration.exponential_backoff_scale.input"
                    />
                </>
            )}

            {props.policyType && (
                <>
                    <Input
                        placeholder="Retry Status Codes"
                        disabled={true}
                        style={{ fontWeight: "bold", fontSize: 17, opacity: 1 }}
                        containerStyle={{ height: 50 }}
                        inputContainerStyle={{
                            borderColor: "rgba(255,255,255,0)",
                        }}
                    />

                    <Input
                        value={statusCodesText}
                        onChangeText={setStatusCodesText}
                        onBlur={updateStatusCodes}
                    />

                    {props.setRetryMethods && (
                        <>
                            <Input
                                placeholder="Retry Methods"
                                disabled={true}
                                style={{
                                    fontWeight: "bold",
                                    fontSize: 17,
                                    opacity: 1,
                                }}
                                containerStyle={{ height: 50 }}
                                inputContainerStyle={{
                                    borderColor: "rgba(255,255,255,0)",
                                }}
                            />

                            <ButtonGroup
                                selectedIndexes={selectedMethodsIndex}
                                // @ts-ignore
                                onPress={setSelectedMethodsIndex}
                                buttons={buttonMethods}
                                selectMultiple
                            />
                        </>
                    )}
                </>
            )}
        </>
    );
}
Example #5
Source File: APIClientUploadScreen.tsx    From react-native-network-client with Apache License 2.0 4 votes vote down vote up
APIClientUploadScreen = ({ route }: APIClientUploadScreenProps) => {
    const {
        item: { client },
    } = route.params;

    const [state, setState] = useState<UploadState>({
        endpoint: "/files",
        progress: 0,
    });

    const methods = ["POST", "PUT", "PATCH"];
    const [methodIndex, setMethodIndex] = useState<number>(0);
    const [multipart, setMultipart] = useState<boolean>(false);
    const [multipartFileKey, setMultipartFileKey] = useState("");
    const [multipartData, setMultipartData] = useState<Record<string, string>>(
        {}
    );
    const [skipBytes, setSkipBytes] = useState<number>(0);

    const setRequest = (request?: ProgressPromise<ClientResponse>) =>
        setState((state) => ({ ...state, request }));
    const setEndpoint = (endpoint: string) => setState({ ...state, endpoint });
    const setFile = (file: File) => setState((state) => ({ ...state, file }));
    const setProgress = (progress: number) =>
        setState((state) => ({ ...state, progress }));
    const setStatus = (status?: UploadStatus) => {
        setState((state) => ({ ...state, status }));
    };
    const [response, setResponse] = useState<ClientResponse>();
    const [responseSuccessVisible, setResponseSuccessVisible] = useState(false);
    const [error, setError] = useState<ClientResponseError>();
    const [responseErrorVisible, setResponseErrorVisible] = useState(false);

    const setStateFromResponse = (response: ClientResponse) => {
        if (response.ok) {
            resetState();
        } else {
            setState((state) => ({
                ...state,
                request: undefined,
                progress: state.progress === 1 ? 0 : state.progress,
                status: undefined,
            }));
        }
    };

    useEffect(() => {
        if (multipart) {
            setEndpoint("/api/files/multipart");
        }
    }, [multipart]);

    const resetState = () =>
        setState((state) => ({
            ...state,
            request: undefined,
            file: undefined,
            progress: 0,
            status: undefined,
        }));

    const upload = async () => {
        setStatus(UploadStatus.UPLOADING);
        setRequest(undefined);

        const reqOptions: UploadRequestOptions = {};

        if (multipart) {
            // Multipart should always send the file key
            reqOptions["multipart"] = {
                fileKey: multipartFileKey,
            };

            // If there is additional data, add it
            if (Object.keys(multipartData).length) {
                reqOptions["multipart"]["data"] = multipartData;
            }
        }

        // Add the following if they're not the defaults
        if (skipBytes > 0) reqOptions["skipBytes"] = skipBytes;
        if (methodIndex > 0) reqOptions["method"] = methods[methodIndex];

        const request = client.upload(
            state.endpoint,
            state.file!.uri!,
            reqOptions
        );
        setRequest(request);

        request.progress!((fractionCompleted: number) => {
            setProgress(fractionCompleted);
        })
            .then((response: ClientResponse) => {
                setStateFromResponse(response);
                setResponse(response);
                setError(undefined);
                setResponseSuccessVisible(true);
                setResponseErrorVisible(false);
            })
            .catch((error: ClientResponseError) => {
                setStatus(UploadStatus.FAILED);
                setResponse(undefined);
                setError(error);
                setResponseSuccessVisible(false);
                setResponseErrorVisible(true);
            });
    };

    const cancelUpload = () => {
        if (state.request) {
            state.request.cancel!();
        }
    };

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <ScrollView testID="api_client_upload.scroll_view">
                <Input
                    label="Endpoint"
                    placeholder="/upload"
                    value={state.endpoint}
                    onChangeText={setEndpoint}
                    autoCapitalize="none"
                    testID="api_client_upload.endpoint.input"
                />
                <ButtonGroup
                    onPress={setMethodIndex}
                    selectedIndex={methodIndex}
                    buttons={methods}
                    containerStyle={{ flex: 1 }}
                />
                <FilePickerButtonGroup
                    onFilePicked={setFile}
                    disabled={Boolean(state.status)}
                />
                <CheckBox
                    title={`Send as Multi-part? [${multipart}]`}
                    checked={multipart}
                    onPress={() =>
                        multipart ? setMultipart(false) : setMultipart(true)
                    }
                    iconType="ionicon"
                    checkedIcon="ios-checkmark-circle"
                    uncheckedIcon="ios-checkmark-circle"
                    iconRight
                    textStyle={{ flex: 1 }}
                />
                {multipart && (
                    <>
                        <Input
                            label="Multi-part file key"
                            placeholder="file"
                            value={multipartFileKey}
                            onChangeText={setMultipartFileKey}
                            autoCapitalize="none"
                            testID="api_client_upload.multipart_key.input"
                        />
                        <AddMultipart onMultipartsChanged={setMultipartData} />
                    </>
                )}
                {!multipart && (
                    <NumericInput
                        title="Skip Bytes: "
                        value={skipBytes}
                        onChange={setSkipBytes}
                        minValue={0}
                        testID="api_client_request.skip_bytes.input"
                    />
                )}

                <ProgressiveFileUpload
                    file={state.file}
                    progress={state.progress}
                />

                <ResponseSuccessOverlay
                    response={response}
                    visible={responseSuccessVisible}
                    setVisible={setResponseSuccessVisible}
                />
                <ResponseErrorOverlay
                    error={error}
                    visible={responseErrorVisible}
                    setVisible={setResponseErrorVisible}
                />

                <UploadButton
                    fileUri={state.file?.uri}
                    endpoint={state.endpoint}
                    status={state.status}
                    upload={upload}
                    cancelUpload={cancelUpload}
                    resetState={resetState}
                />
            </ScrollView>
        </SafeAreaView>
    );
}
Example #6
Source File: GenericClientRequestScreen.tsx    From react-native-network-client with Apache License 2.0 4 votes vote down vote up
GenericClientRequestScreen = ({
    route,
}: GenericClientRequestScreenProps) => {
    const {
        item: { client },
    } = route.params;

    const [url, setUrl] = useState("");
    const [selectedMethodIndex, setSelectedMethodIndex] = useState(0);
    const [timeoutInterval, setTimeoutInterval] = useState(30000);
    const [body, setBody] = useState('{"login_id":"","password":""}');
    const [requestHeaders, setRequestHeaders] = useState<Header[]>([]);
    const [response, setResponse] = useState<ClientResponse>();
    const [responseSuccessVisible, setResponseSuccessVisible] = useState(false);
    const [error, setError] = useState<ClientResponseError>();
    const [responseErrorVisible, setResponseErrorVisible] = useState(false);
    const [
        retryPolicyConfiguration,
        setRetryPolicyType,
        setRetryLimit,
        setRetryInterval,
        setExponentialBackoffBase,
        setExponentialBackoffScale,
        setStatusCodes,
        setRetryMethods,
    ] = useRetryPolicyConfiguration();

    const methods = Object.keys(METHODS);

    const makeRequest = async () => {
        const method = methods[selectedMethodIndex];
        const headers = parseHeaders(requestHeaders);
        let options: RequestOptions = {
            headers,
            timeoutInterval,
            retryPolicyConfiguration,
        };

        const canIncludeBody = ![METHODS.HEAD, METHODS.GET].includes(
            method as METHODS
        );
        if (canIncludeBody && body.length) {
            try {
                options.body = JSON.parse(body);
            } catch (e) {
                const error = e as Error;
                Alert.alert("Error parsing Body", error.message, [{ text: "OK" }], {
                    cancelable: false,
                });
                return;
            }
        }

        try {
            let clientMethod;
            switch (method) {
                case METHODS.HEAD:
                    clientMethod = client.head;
                    break;
                case METHODS.GET:
                    clientMethod = client.get;
                    break;
                case METHODS.PUT:
                    clientMethod = client.put;
                    break;
                case METHODS.POST:
                    clientMethod = client.post;
                    break;
                case METHODS.PATCH:
                    clientMethod = client.patch;
                    break;
                case METHODS.DELETE:
                    clientMethod = client.delete;
                    break;
                default:
                    throw new Error("Invalid request method");
            }
            var response = await clientMethod(url, options);
            setResponse(response);
            setError(undefined);
            setResponseSuccessVisible(true);
            setResponseErrorVisible(false);
        } catch (error) {
            setResponse(undefined);
            setError(error as ClientResponseError);
            setResponseSuccessVisible(false);
            setResponseErrorVisible(true);
        }
    };

    return (
        <SafeAreaView style={{ flex: 1 }}>
            <ScrollView
                style={{
                    backgroundColor: "#fff",
                    borderRadius: 5,
                    margin: 10,
                }}
                testID="generic_client_request.scroll_view"
            >
                <ButtonGroup
                    onPress={setSelectedMethodIndex}
                    selectedIndex={selectedMethodIndex}
                    buttons={methods}
                    containerStyle={{ height: 50 }}
                />
                <Input
                    label="URL"
                    placeholder="https://google.com"
                    value={url}
                    onChangeText={setUrl}
                    autoCapitalize="none"
                    testID="generic_client_request.url.input"
                />
                <AddHeaders onHeadersChanged={setRequestHeaders} />
                {methods[selectedMethodIndex] !== METHODS.GET && (
                    <Input
                        label="Body"
                        placeholder='{"username": "johndoe"}'
                        value={body}
                        onChangeText={setBody}
                        autoCapitalize="none"
                        testID="generic_client_request.body.input"
                    />
                )}
                <NumericInput
                    title="Timeout Interval (ms)"
                    value={timeoutInterval}
                    onChange={setTimeoutInterval}
                    minValue={0}
                    step={5000}
                    testID="generic_client_request.timeout_interval.input"
                />
                <RetryPolicyConfiguration
                    policyType={retryPolicyConfiguration.type}
                    onTypeSelected={setRetryPolicyType}
                    retryLimit={retryPolicyConfiguration.retryLimit}
                    setRetryLimit={setRetryLimit}
                    retryInterval={retryPolicyConfiguration.retryInterval}
                    setRetryInterval={setRetryInterval}
                    exponentialBackoffBase={
                        retryPolicyConfiguration.exponentialBackoffBase
                    }
                    setExponentialBackoffBase={setExponentialBackoffBase}
                    exponentialBackoffScale={
                        retryPolicyConfiguration.exponentialBackoffScale
                    }
                    setExponentialBackoffScale={setExponentialBackoffScale}
                    statusCodes={retryPolicyConfiguration.statusCodes}
                    setStatusCodes={setStatusCodes}
                    retryMethods={retryPolicyConfiguration.retryMethods}
                    setRetryMethods={setRetryMethods}
                />
                <ResponseSuccessOverlay
                    response={response}
                    visible={responseSuccessVisible}
                    setVisible={setResponseSuccessVisible}
                />
                <ResponseErrorOverlay
                    error={error}
                    visible={responseErrorVisible}
                    setVisible={setResponseErrorVisible}
                />
            </ScrollView>
            <Button
                title="Request"
                onPress={makeRequest}
                disabled={!url.length}
                containerStyle={{ padding: 5 }}
            />
        </SafeAreaView>
    );
}
Example #7
Source File: Food.tsx    From save-food with MIT License 4 votes vote down vote up
Food = ({ navigation }: Props) => {
	const { useSubscribe } = useSettingsContext()
	const settings = useSubscribe((s) => s.settings)
	const translations = useSubscribe((s) => ({ ...s.translations.Food, ...s.translations.common }))

	const [savedData, setSavedData] = useState<WastedFood>(initialData)
	const [templateData, setTemplateData] = useState<WastedFood>(initialData)
	const [loading, setLoading] = useState(true)
	const [saveFoodBeforeModalHide, setSaveFoodBeforeModalHide] = useState(false)
	const [hasChanges, setHasChanges] = useState(false)
	const [showModal, setShowModal] = useState(false)
	const [modalType, setModalType] = useState<ModalType>('id')
	const [modalContent, setModalContent] = useState<ReactNode>()
	const [controls, setControls] = useState<InputsControl>({
		name: {
			label: translations.foodNameLabel,
			required: true,
			characterRestriction: 70,
		},
		quantity: {
			label: translations.quantityLabel,
			keyboardType: 'numeric',
			required: true,
			characterRestriction: 5,
			number: true,
			positiveNumber: true,
			precision: 0,
		},
		price: {
			label: translations.priceLabel,
			keyboardType: 'numeric',
			required: true,
			characterRestriction: 7,
			number: true,
			positiveNumber: true,
			precision: 2,
		},
	})

	const correctPrice = !!(savedData.price && !controls.price.error)
	const quantitySuffixes = [translations.grams, translations.milliliters]

	const setFood = () => {
		const savedData = {
			id: navigation.getParam('id', undefined),
			image: navigation.getParam('image', undefined),
			name: navigation.getParam('name', ''),
			quantity: navigation.getParam('quantity', 0),
			price: navigation.getParam('price', 0),
			paid: navigation.getParam('paid', 0),
			productQuantity: navigation.getParam('productQuantity', 1),
			quantitySuffixIndex: navigation.getParam('quantitySuffixIndex', 1),
			percentage: navigation.getParam('percentage', 100),
			selected: navigation.getParam('selected', 1),
		}

		setSavedData(savedData)
		setTemplateData(savedData)
		setLoading(false)
	}

	const QuantitySuffixButtons = () => {
		const [value, setValue] = useState(savedData.quantitySuffixIndex)

		return (
			<ButtonGroup
				onPress={(index) => {
					setSavedData({
						...savedData,
						quantitySuffixIndex: index,
					})
					setValue(index)
				}}
				selectedIndex={value}
				buttons={quantitySuffixes}
				selectedButtonStyle={styles.selectedButtonStyle}
			/>
		)
	}

	const setContent = (type: ModalType) => {
		if (type === 'discardChanges') {
			setModalContent(
				<Text style={styles.discardChanges}>{translations.discardChangesDescription}</Text>,
			)
		} else {
			setModalContent(
				<View style={styles.modalContentWrapper}>
					<Input
						inputConfig={controls[type]}
						translations={translations}
						value={templateData[type]}
						changed={(value, control) => {
							setTemplateData({
								...templateData,
								[type]: value,
							})
							setControls({
								...controls,
								[type]: control,
							})
						}}
					/>
					{type === 'quantity' && <QuantitySuffixButtons />}
				</View>,
			)
		}

		setShowModal(true)
		setModalType(type)
	}

	const saveChange = () => {
		if (modalType === 'discardChanges') {
			return
		}

		if (checkValidation(controls[modalType], templateData[modalType] ?? '')) {
			const preparedData = prepareData(
				{ ...savedData, [modalType]: templateData[modalType] },
				controls,
			)

			setTemplateData(preparedData)
			setSavedData(preparedData)

			setHasChanges(true)
			setShowModal(false)
		}
	}

	const changePercentageHandler = (percent: number) => {
		setSavedData({ ...savedData, percentage: +percent.toFixed(0) })
		setHasChanges(true)
	}

	const cancelChange = () => {
		if (modalType === 'discardChanges') {
			return
		}

		setTemplateData(savedData)
		setShowModal(false)
	}

	const toggleModal = (type?: ModalType) => {
		if (!showModal && type) {
			setContent(type)
		} else {
			setShowModal(false)
		}
	}

	const takePhoto = (uri: string) => {
		setSavedData({
			...savedData,
			image: uri,
		})
		setHasChanges(true)
	}

	const toggleCamera = async () => {
		const { status } = await Camera.requestPermissionsAsync()

		if (status === 'granted') {
			navigation.navigate('Camera', {
				buttonTitle: translations.takePhoto,
				takePhoto,
			})
		} else {
			showErrorMessage('permissionError')
		}
	}

	const showErrorMessage = (type: 'priceError' | 'permissionError') => {
		if (type === 'priceError') {
			const message: MessageOptions = {
				message: translations.noPriceTitle,
				description: translations.noPriceDescription,
				type: 'warning',
				icon: { icon: 'warning', position: 'left' },
				duration: 2500,
			}

			showMessage(message)
		}
		if (type === 'permissionError') {
			const message: MessageOptions = {
				message: translations.permissionErrorTitle,
				description: translations.permissionErrorCamera,
				type: 'danger',
				icon: { icon: 'danger', position: 'left' },
				duration: 2500,
			}

			showMessage(message)
		}
	}

	const exitHandler = () => {
		if (hasChanges && correctPrice) {
			setContent('discardChanges')
		} else {
			navigation.goBack()
		}
	}

	const getModalButtons = (): ModalButtonType[] => {
		if (modalType === 'discardChanges') {
			return [
				{
					text: translations.yes,
					onPress: () => {
						setShowModal(false)
						navigation.goBack()
					},
				},
				{
					text: translations.save,
					onPress: () => {
						setSaveFoodBeforeModalHide(true)
						setShowModal(false)
					},
				},
				{ text: translations.cancel, onPress: () => setShowModal(false) },
			]
		}
		return [
			{ text: translations.save, onPress: saveChange },
			{ text: translations.cancel, onPress: cancelChange },
		]
	}

	const checkErrorHandler = () => {
		if (!correctPrice) {
			showErrorMessage('priceError')
		}
	}

	const saveFoodHandler = async () => {
		await saveFood(savedData)
		navigation.replace('List')
	}

	useEffect(() => {
		setFood()
	}, [])

	if (loading) {
		return <Spinner size={64} />
	}

	return (
		<Background>
			<Header
				leftComponent={<Icon onPress={exitHandler} variant='backIcon' />}
				centerComponent={
					<Text style={styles.headerTitle}>
						{savedData.id ? translations.editFood : translations.newFood}
					</Text>
				}
			/>

			<Modal
				visible={showModal}
				toggleModal={toggleModal}
				title={translations[modalType]}
				buttons={getModalButtons()}
				onModalHide={async () => {
					if (saveFoodBeforeModalHide) {
						await saveFoodHandler()
					}
				}}
			>
				{modalContent}
			</Modal>

			<View style={styles.contentWrapper}>
				<ScrollView>
					<View style={styles.imageContainer}>
						<TouchableOpacity onPress={toggleCamera}>
							<View style={styles.imageWrapper}>
								<Image style={styles.image} source={getImage(savedData.image)} />

								<View style={styles.tapImage}>
									<Text style={styles.tapImageText}>{translations.tapToChange}</Text>
								</View>
							</View>
						</TouchableOpacity>
					</View>

					<View style={styles.infoWindowsContainer}>
						<InfoWindow
							color1={whiteColor}
							color2={orangeGradient}
							title={translations.name}
							value={
								!savedData.name || savedData.name === '' ? translations.noData : savedData.name
							}
							onPress={() => toggleModal('name')}
						/>
						<InfoWindow
							color1={whiteColor}
							color2={orangeGradient}
							title={translations.quantity}
							value={
								savedData.quantity
									? `${+savedData.quantity} ${getQuantitySuffix(savedData.quantitySuffixIndex)}`
									: '0'
							}
							onPress={() => toggleModal('quantity')}
						/>
						<InfoWindow
							color1={whiteColor}
							color2={redGradient}
							title={translations.price}
							value={`${savedData.price ? +savedData.price : 0} ${settings.currency}`}
							onPress={() => toggleModal('price')}
						/>

						<View style={styles.sliderContainer}>
							<Text style={styles.percentInfo}>{translations.percentInfo}</Text>
							<Slider
								style={styles.slider}
								thumbStyle={styles.sliderThumbStyle}
								thumbTintColor={blackColor}
								minimumTrackTintColor={darkColor}
								maximumTrackTintColor={grayColor}
								minimumValue={1}
								maximumValue={100}
								value={savedData.percentage}
								onValueChange={(value: number) => changePercentageHandler(value)}
							/>
							<Text style={styles.percent}>{savedData.percentage}%</Text>
						</View>
					</View>

					<View style={styles.saveButtonContainer}>
						<TouchableOpacity onPress={checkErrorHandler}>
							<Button
								buttonStyle={styles.saveButton}
								titleStyle={styles.saveButtonTitle}
								disabled={!correctPrice}
								type='outline'
								title={translations.save}
								onPress={saveFoodHandler}
							/>
						</TouchableOpacity>
					</View>
				</ScrollView>
			</View>
		</Background>
	)
}
Example #8
Source File: Home.tsx    From wuhan2020-frontend-react-native-app with MIT License 4 votes vote down vote up
function Map() {
  const { data, refresh, timeout } = useContext(DataContext);
  const [selectedIndex, setIndex] = useState(0);
  const filter = filterList[selectedIndex];
  const [refreshing, setRefreshing] = useState(false);

  let mapData = data || [];

  const total = {
    confirmedCount: 0,
    curedCount: 0,
    deadCount: 0,
  };

  const webviewRef = useRef(null);

  useEffect(function() {
    if (webviewRef.current) {
      webviewRef.current.setOption({
        title: {
          text: `疫情地图${titleMap[filter]}`,
        },
        tooltip: {
          trigger: 'item',
          formatter: '{b}',
        },
        visualMap: {
          pieces: piecesMap[filter],
          showLabel: true,
          realtime: true,
          inRange: {
            color: ['yellow', 'red'],
          },
        },
        series: [
          {
            name: '中国',
            type: 'map',
            map: 'china',
            selectedMode: 'single', //multiple多选
            itemStyle: {
              normal: {
                label: {
                  show: false,
                  textStyle: {
                    color: '#231816',
                  },
                },
                areaStyle: { color: '#B1D0EC' },
                color: '#B1D0EC',
                borderColor: '#bbb',
              },
              emphasis: {
                label: {
                  show: false,
                  textStyle: {
                    color: '#fa4f04',
                  },
                },
              },
            },
            data: mapData,
          },
        ],
      });
    }
  });

  const onRefresh = useCallback(() => {
    setRefreshing(true);

    refresh();
    wait(2000).then(() => setRefreshing(false));
  }, [refreshing, refresh]);

  if (data) {
    let formatted = [];

    data.getAreaStat.forEach(entry => {
      total.confirmedCount = total.confirmedCount + entry.confirmedCount;
      total.curedCount = total.curedCount + entry.curedCount;
      total.deadCount = total.deadCount + entry.deadCount;

      formatted = formatted.concat([
        {
          name: entry.provinceShortName,
          value: entry[filter],
        },
      ]);
    });

    mapData = formatted;
  }

  const option = {
    title: {
      text: `疫情地图${titleMap[filter]}`,
    },
    tooltip: {
      trigger: 'item',
      formatter: '{b}',
    },
    visualMap: {
      pieces: piecesMap[filter],
      showLabel: true,
      realtime: true,
      inRange: {
        color: ['yellow', 'red'],
      },
    },
    series: [
      {
        name: '中国',
        type: 'map',
        map: 'china',
        selectedMode: 'single', //multiple多选
        itemStyle: {
          normal: {
            label: {
              show: false,
              textStyle: {
                color: '#231816',
              },
            },
            areaStyle: { color: '#B1D0EC' },
            color: '#B1D0EC',
            borderColor: '#bbb',
          },
          emphasis: {
            label: {
              show: false,
              textStyle: {
                color: '#fa4f04',
              },
            },
          },
        },
        data: mapData,
      },
    ],
  };

  if (timeout) {
    return (
      <SafeAreaView style={{ flex: 1 }}>
        <View
          style={{ justifyContent: 'center', alignItems: 'center', flex: 1 }}>
          <Text
            style={{
              color: colors.primary,
              fontSize: 18,
              paddingVertical: 20,
            }}>
            数据载入失败?
          </Text>
          <Button type="outline" onPress={refresh} title="点击重试" />
        </View>
      </SafeAreaView>
    );
  }

  return (
    <SafeAreaView style={{ flex: 1 }}>
      <ScrollView
        style={{
          flex: 1,
        }}
        refreshControl={
          <RefreshControl
            tintColor="pink"
            refreshing={refreshing}
            onRefresh={onRefresh}
          />
        }>
        <View style={{ backgroundColor: 'white' }}>
          {mapData.length ? (
            <View style={{ flex: 1 }}>
              <View style={{ height: 300 }}>
                <ECharts
                  ref={webviewRef}
                  option={option}
                  backgroundColor="#fcfcfc"
                />
              </View>
              <ButtonGroup
                selectedButtonStyle={styles.buttonGroup}
                onPress={setIndex}
                selectedIndex={selectedIndex}
                buttons={[
                  `确诊 (${total.confirmedCount})`,
                  `治愈 (${total.curedCount})`,
                  `致死 (${total.deadCount})`,
                ]}
                containerStyle={{ height: 50 }}
              />
              {data && data.getTimelineService && (
                <Timeline data={data.getTimelineService} />
              )}
              {data && data.getIndexRecommendList && (
                <RecommendationList data={data.getIndexRecommendList} />
              )}
            </View>
          ) : (
            <View style={{ flex: 1, width, height }}>
              <Loader
                size="large"
                color="red"
                style={{ marginTop: height / 3 }}
              />
            </View>
          )}
        </View>
      </ScrollView>
    </SafeAreaView>
  );
}