From 97a1a364dafad7668eea4f5c5c90ee5fe505bb26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Qu=E1=BB=91c=20Kh=C3=A1nh?= Date: Sun, 14 Jul 2024 14:51:25 +0700 Subject: [PATCH] feat(mobile): [Transaction] add haptic feedback (#114) --- apps/mobile/app/(app)/new-record.tsx | 10 +++---- apps/mobile/components/common/toolbar.tsx | 3 ++- .../components/numeric-pad/numeric-pad.tsx | 6 +++++ .../transaction/select-account-field.tsx | 4 +++ .../transaction/select-category-field.tsx | 4 +++ .../transaction/select-date-field.tsx | 6 ++++- apps/mobile/package.json | 1 + pnpm-lock.yaml | 26 ++++++++++++++----- 8 files changed, 46 insertions(+), 14 deletions(-) diff --git a/apps/mobile/app/(app)/new-record.tsx b/apps/mobile/app/(app)/new-record.tsx index 91d0a21d..b5a2a3f4 100644 --- a/apps/mobile/app/(app)/new-record.tsx +++ b/apps/mobile/app/(app)/new-record.tsx @@ -1,11 +1,9 @@ -import { toast } from '@/components/common/toast' import { TransactionForm } from '@/components/transaction/transaction-form' import { createTransaction } from '@/mutations/transaction' import { transactionQueries } from '@/queries/transaction' import { useWallets, walletQueries } from '@/queries/wallet' -import { t } from '@lingui/macro' -import { useLingui } from '@lingui/react' import { useMutation, useQueryClient } from '@tanstack/react-query' +import * as Haptics from 'expo-haptics' import { useRouter } from 'expo-router' import { LoaderIcon } from 'lucide-react-native' import { Alert, View } from 'react-native' @@ -13,16 +11,18 @@ import { Alert, View } from 'react-native' export default function NewRecordScreen() { const router = useRouter() const { data: walletAccounts } = useWallets() - const { i18n } = useLingui() + // const { i18n } = useLingui() const queryClient = useQueryClient() const { mutateAsync } = useMutation({ mutationFn: createTransaction, onError(error) { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error) Alert.alert(error.message) }, onSuccess() { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success) router.back() - toast.success(t(i18n)`Transaction created`) + // toast.success(t(i18n)`Transaction created`) }, async onSettled() { await queryClient.invalidateQueries({ diff --git a/apps/mobile/components/common/toolbar.tsx b/apps/mobile/components/common/toolbar.tsx index 82504b1a..4cbf1814 100644 --- a/apps/mobile/components/common/toolbar.tsx +++ b/apps/mobile/components/common/toolbar.tsx @@ -1,5 +1,6 @@ import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' +import * as Haptics from 'expo-haptics' import { Link } from 'expo-router' import { PlusIcon, Sparkles } from 'lucide-react-native' import { TouchableOpacity, View } from 'react-native' @@ -21,7 +22,7 @@ export function Toolbar() { - + diff --git a/apps/mobile/components/numeric-pad/numeric-pad.tsx b/apps/mobile/components/numeric-pad/numeric-pad.tsx index eee60192..65447b4c 100644 --- a/apps/mobile/components/numeric-pad/numeric-pad.tsx +++ b/apps/mobile/components/numeric-pad/numeric-pad.tsx @@ -1,8 +1,10 @@ import { cn } from '@/lib/utils' +import * as Haptics from 'expo-haptics' import { DeleteIcon } from 'lucide-react-native' import { View } from 'react-native' import Animated from 'react-native-reanimated' import { useSafeAreaInsets } from 'react-native-safe-area-context' + import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -38,15 +40,19 @@ export function NumericPad({ return } + Haptics.selectionAsync() + onValueChange?.(newValue) } function handleDelete() { + Haptics.selectionAsync() const newValue = Math.floor(value / 10) onValueChange?.(newValue) } function handleClear() { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success) onValueChange?.(0) } diff --git a/apps/mobile/components/transaction/select-account-field.tsx b/apps/mobile/components/transaction/select-account-field.tsx index 1a3c51ba..414b95c5 100644 --- a/apps/mobile/components/transaction/select-account-field.tsx +++ b/apps/mobile/components/transaction/select-account-field.tsx @@ -8,11 +8,13 @@ import { } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' +import * as Haptics from 'expo-haptics' import { useRef } from 'react' import { useController } from 'react-hook-form' import { Keyboard } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { FullWindowOverlay } from 'react-native-screens' + import GenericIcon from '../common/generic-icon' import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -42,6 +44,7 @@ export function SelectAccountField({ className="border border-border !px-3 max-w-[160px]" disabled={isLoading} onPress={() => { + Haptics.selectionAsync() Keyboard.dismiss() sheetRef.current?.present() }} @@ -97,6 +100,7 @@ export function SelectAccountField({ className="h-20 flex-1 flex gap-2 px-2 flex-col flex-grow" variant={value === item ? 'secondary' : 'ghost'} onPress={async () => { + Haptics.selectionAsync() sheetRef.current?.close() await sleep(500) onChange(item.id) diff --git a/apps/mobile/components/transaction/select-category-field.tsx b/apps/mobile/components/transaction/select-category-field.tsx index b63dafd6..3c50c079 100644 --- a/apps/mobile/components/transaction/select-category-field.tsx +++ b/apps/mobile/components/transaction/select-category-field.tsx @@ -8,9 +8,11 @@ import { } from '@gorhom/bottom-sheet' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' +import * as Haptics from 'expo-haptics' import { useRef } from 'react' import { useController } from 'react-hook-form' import { FlatList, Keyboard, View } from 'react-native' + import { useSafeAreaInsets } from 'react-native-safe-area-context' import { FullWindowOverlay } from 'react-native-screens' import GenericIcon from '../common/generic-icon' @@ -52,6 +54,7 @@ export function SelectCategoryField({ className="border border-border !px-3 max-w-[160px]" disabled={isLoading} onPress={() => { + Haptics.selectionAsync() Keyboard.dismiss() sheetRef.current?.present() }} @@ -112,6 +115,7 @@ export function SelectCategoryField({ className="h-20 flex flex-1 w-full gap-2 px-2 flex-col flex-grow" variant={value === item ? 'secondary' : 'ghost'} onPress={async () => { + Haptics.selectionAsync() sheetRef.current?.close() await sleep(500) onChange(item.id) diff --git a/apps/mobile/components/transaction/select-date-field.tsx b/apps/mobile/components/transaction/select-date-field.tsx index 31ebd666..197c44d6 100644 --- a/apps/mobile/components/transaction/select-date-field.tsx +++ b/apps/mobile/components/transaction/select-date-field.tsx @@ -8,10 +8,11 @@ import { import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import DateTimePicker from '@react-native-community/datetimepicker' +import * as Haptics from 'expo-haptics' import { Calendar } from 'lucide-react-native' import { useRef, useState } from 'react' import { useController } from 'react-hook-form' -import { View } from 'react-native' +import { Keyboard, View } from 'react-native' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { FullWindowOverlay } from 'react-native-screens' import { Button } from '../ui/button' @@ -40,6 +41,7 @@ function SpinnerDatePicker({