diff --git a/apps/native/app/src/lib/show-picker.ts b/apps/native/app/src/lib/show-picker.ts index 3daf676cac9b..20a41ca0167f 100644 --- a/apps/native/app/src/lib/show-picker.ts +++ b/apps/native/app/src/lib/show-picker.ts @@ -1,3 +1,4 @@ +import { dynamicColor } from '@ui' import { ActionSheetIOS } from 'react-native' import DialogAndroid from 'react-native-dialogs' import { uiStore } from '../stores/ui-store' @@ -54,6 +55,21 @@ interface ShowPickerResponse { selectedItem?: ShowPickerItem } +const parseAndroidAction = (action: string) => { + switch (action) { + case 'actionPositive': + return 'positive' + case 'actionNegative': + return 'negative' + case 'actionNeutral': + return 'neutral' + case 'actionDismiss': + return 'dismiss' + default: + return 'select' + } +} + export function showPicker( options: ShowPickerOptions, ): Promise { @@ -139,3 +155,28 @@ export function showPicker( } return Promise.resolve({ action: 'neutral' }) } + +export function showAndroidPrompt( + title: string, + content?: string, + options?: DialogAndroid.OptionsPrompt, +): Promise { + const theme = uiStore.getState().theme! + + return DialogAndroid.prompt(title, content, { + positiveColor: theme.color.blue400, + negativeColor: theme.color.red600, + widgetColor: theme.color.blue400, + contentColor: theme.shade.foreground, + backgroundColor: theme.shade.background, + neutralColor: theme.shade.foreground, + titleColor: theme.shade.foreground, + ...options, + }).then(({ action, text, ...rest }: any) => { + return { + action: parseAndroidAction(action), + text, + ...rest, + } + }) +} diff --git a/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx b/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx index 728a3e5310bd..598d50120634 100644 --- a/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx +++ b/apps/native/app/src/screens/vehicles/components/mileage-cell.tsx @@ -42,7 +42,7 @@ export function MileageCell({ backgroundColor: dynamicColor({ light: editable ? '#FFFCE0' : dynamicColor.theme.color.blueberry100, dark: editable - ? '#FFFCE0' + ? dynamicColor.theme.shades.dark.shade400 : dynamicColor.theme.shades.dark.shade100, }), gap: 4, diff --git a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx index aea157b4b206..36f1ab977047 100644 --- a/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx +++ b/apps/native/app/src/screens/vehicles/vehicle-mileage.screen.tsx @@ -13,6 +13,7 @@ import { Navigation, NavigationFunctionComponent, } from 'react-native-navigation' + import externalLinkIcon from '../../assets/icons/external-link.png' import { GetVehicleDocument, @@ -25,7 +26,9 @@ import { } from '../../graphql/types/schema' import { createNavigationOptionHooks } from '../../hooks/create-navigation-option-hooks' import { useBrowser } from '../../lib/useBrowser' +import { showAndroidPrompt } from '../../lib/show-picker' import { MileageCell } from './components/mileage-cell' +import { isAndroid, isIos } from '../../utils/devices' const { getNavigationOptions, useNavigationOptions } = createNavigationOptionHooks(() => ({ @@ -177,66 +180,109 @@ export const VehicleMileageScreen: NavigationFunctionComponent<{ }) }, [id, input, parseMileage, postMileage, intl]) - const onEdit = useCallback(() => { - return Alert.prompt( - intl.formatMessage({ id: 'vehicle.mileage.promptEditTitle' }), - undefined, - [ - { - isPreferred: true, - onPress(value) { - const mileage = parseMileage(value, true) - const internalId = data[0].internalId - if (!mileage) { - return - } - if (!internalId) { - return Alert.alert( - intl.formatMessage({ id: 'vehicle.mileage.errorTitle' }), - intl.formatMessage({ - id: 'vehicle.mileage.errorFailedToUpdate', - }), - ) - } - updateMileage({ - variables: { - input: { - mileage: mileage.toString(), - permno: id, - internalId: Number(internalId), - }, - }, - }).then((res) => { - if (res.data?.vehicleMileagePut?.mileage !== String(mileage)) { - Alert.alert( - intl.formatMessage({ id: 'vehicle.mileage.errorTitle' }), - intl.formatMessage({ - id: 'vehicle.mileage.errorFailedToUpdate', - }), - ) - } else { - Alert.alert( - intl.formatMessage({ id: 'vehicle.mileage.successTitle' }), - intl.formatMessage({ id: 'vehicle.mileage.successMessage' }), - ) - } - }) + const onEditMileageSubmit = useCallback( + (mileageInput: string | undefined) => { + const mileage = parseMileage(mileageInput, true) + const internalId = data[0].internalId + if (!mileage) { + return + } + if (!internalId) { + return Alert.alert( + intl.formatMessage({ id: 'vehicle.mileage.errorTitle' }), + intl.formatMessage({ + id: 'vehicle.mileage.errorFailedToUpdate', + }), + ) + } + updateMileage({ + variables: { + input: { + mileage: mileage.toString(), + permno: id, + internalId: Number(internalId), }, - text: intl.formatMessage({ id: 'vehicle.mileage.promptEditButton' }), - style: 'default', }, + }) + .then((res) => { + if (res.data?.vehicleMileagePut?.mileage !== String(mileage)) { + Alert.alert( + intl.formatMessage({ id: 'vehicle.mileage.errorTitle' }), + intl.formatMessage({ + id: 'vehicle.mileage.errorFailedToUpdate', + }), + ) + } else { + Alert.alert( + intl.formatMessage({ id: 'vehicle.mileage.successTitle' }), + intl.formatMessage({ + id: 'vehicle.mileage.successMessage', + }), + ) + } + }) + .catch(() => { + Alert.alert( + intl.formatMessage({ id: 'vehicle.mileage.errorTitle' }), + intl.formatMessage({ + id: 'vehicle.mileage.errorFailedToUpdate', + }), + ) + }) + }, + [data, id, parseMileage, updateMileage, intl], + ) + + const onEdit = useCallback(async () => { + if (isIos) { + return Alert.prompt( + intl.formatMessage({ id: 'vehicle.mileage.promptEditTitle' }), + undefined, + [ + { + isPreferred: true, + onPress(value) { + return onEditMileageSubmit(value) + }, + text: intl.formatMessage({ + id: 'vehicle.mileage.promptEditButton', + }), + style: 'default', + }, + { + text: intl.formatMessage({ + id: 'vehicle.mileage.promptCancelButton', + }), + style: 'cancel', + }, + ], + 'plain-text', + String(highestMileage), + 'number-pad', + ) + } + if (isAndroid) { + await showAndroidPrompt( + intl.formatMessage({ id: 'vehicle.mileage.promptEditTitle' }), + undefined, { - text: intl.formatMessage({ + keyboardType: 'numeric', + allowEmptyInput: false, + defaultValue: String(highestMileage), + positiveText: intl.formatMessage({ + id: 'vehicle.mileage.promptEditButton', + }), + negativeText: intl.formatMessage({ id: 'vehicle.mileage.promptCancelButton', }), - style: 'cancel', }, - ], - 'plain-text', - String(highestMileage), - 'number-pad', - ) - }, [data, highestMileage, id, parseMileage, updateMileage, intl]) + ).then((res) => { + if (res.action === 'positive') { + return onEditMileageSubmit(res.text) + } + }) + } + }, [highestMileage, onEditMileageSubmit, intl]) return ( <> diff --git a/apps/native/app/src/types/react-native.d.ts b/apps/native/app/src/types/react-native.d.ts index 983788ba91db..18e99d85c455 100644 --- a/apps/native/app/src/types/react-native.d.ts +++ b/apps/native/app/src/types/react-native.d.ts @@ -17,6 +17,59 @@ declare module 'react-native-dialogs' { type Content = void | null | string type ListItem = { label: string; id?: any } + type OptionsCommon = { + title?: null | string + titleColor?: ColorValue + content?: null | string + contentIsHtml?: boolean + contentColor?: string + positiveText?: string + negativeText?: string + neutralText?: string + positiveColor?: ColorValue + negativeColor?: ColorValue + neutralColor?: ColorValue + backgroundColor?: ColorValue + cancelable?: boolean + linkColor?: ColorValue + forceStacking?: boolean + checkboxLabel?: string + checkboxDefaultValue?: boolean + } + + type OptionsPrompt = OptionsCommon & { + keyboardType?: + | 'numeric' + | 'number-pad' + | 'decimal-pad' + | 'numeric-password' + | 'email-address' + | 'password' + | 'phone-pad' + | 'url' + defaultValue?: string + placeholder?: string + allowEmptyInput?: boolean + minLength?: number + maxLength?: number + widgetColor?: ColorValue + } + + type PromptResponse = + | { + action: 'negative' | 'neutral' | 'dismiss' + } + | { + action: 'negative' | 'neutral' + checked: boolean + } + | { action: 'positive'; text: string } + | { + action: 'positive' + text: string + checked: boolean + } + export const listPlain = 'listPlain' export const listRadio = 'listRadio' @@ -25,6 +78,12 @@ declare module 'react-native-dialogs' { message: Content, options: Record, ): Promise<{ action: string; selectedItem?: ListItem }> + + export function prompt( + title: Title, + message: Content, + options: OptionsPrompt, + ): Promise } declare module '@island.is/application/types/lib/ApplicationTypes' {