diff --git a/apps/mobile/app/(app)/new-record.tsx b/apps/mobile/app/(app)/new-record.tsx index e2d19a51..e1a7ea19 100644 --- a/apps/mobile/app/(app)/new-record.tsx +++ b/apps/mobile/app/(app)/new-record.tsx @@ -1,12 +1,34 @@ +import { toast } from '@/components/common/toast' import { TransactionForm } from '@/components/transaction/transaction-form' +import { createTransaction } from '@/mutations/transaction' import { useWallets } from '@/queries/wallet' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { useMutation } from '@tanstack/react-query' import { useRouter } from 'expo-router' import { LoaderIcon } from 'lucide-react-native' -import { View } from 'react-native' +import { Alert, View } from 'react-native' export default function NewRecordScreen() { const router = useRouter() const { data: walletAccounts } = useWallets() + const { i18n } = useLingui() + // const queryClient = useQueryClient() + const { mutateAsync } = useMutation({ + mutationFn: createTransaction, + onError(error) { + Alert.alert(error.message) + }, + onSuccess() { + router.back() + toast.success(t(i18n)`Transaction created`) + }, + async onSettled() { + // await queryClient.invalidateQueries({ + // queryKey: transactionQueries.list._def, + // }) + }, + }) const defaultWallet = walletAccounts?.[0] @@ -20,7 +42,7 @@ export default function NewRecordScreen() { return ( console.log('submit', values)} + onSubmit={mutateAsync} onCancel={router.back} defaultValues={{ walletAccountId: defaultWallet.id, diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index e6fb4869..81a2622b 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -14,6 +14,7 @@ import { SplashScreen, Stack } from 'expo-router' import * as WebBrowser from 'expo-web-browser' import 'react-native-reanimated' +import { ToastRoot } from '@/components/common/toast' import { useWarmUpBrowser } from '@/hooks/use-warm-up-browser' import { useColorScheme } from '@/hooks/useColorScheme' import { queryClient } from '@/lib/client' @@ -85,6 +86,7 @@ export default function RootLayout() { }} /> + diff --git a/apps/mobile/components/common/toast.tsx b/apps/mobile/components/common/toast.tsx new file mode 100644 index 00000000..0f636e0a --- /dev/null +++ b/apps/mobile/components/common/toast.tsx @@ -0,0 +1,15 @@ +import { Toasts, toast as rntoast } from '@backpackapp-io/react-native-toast' + +export function ToastRoot() { + return ( + + ) +} + +export const toast = rntoast diff --git a/apps/mobile/mutations/transaction.ts b/apps/mobile/mutations/transaction.ts new file mode 100644 index 00000000..198e4db7 --- /dev/null +++ b/apps/mobile/mutations/transaction.ts @@ -0,0 +1,22 @@ +import { getHonoClient } from '@/lib/client' +import { type TransactionFormValues, TransactionSchema } from '@6pm/validation' +import { z } from 'zod' + +export async function createTransaction(data: TransactionFormValues) { + const hc = await getHonoClient() + const result = await hc.v1.transactions.$post({ + json: data, + }) + + if (result.ok) { + const transaction = await result.json() + return TransactionSchema.merge( + z.object({ + // override Decimal type with number + amount: z.number({ coerce: true }), + }), + ).parse(transaction) + } + + return result +} diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 8b88e12f..91fbcd88 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -19,6 +19,7 @@ "@6pm/api": "workspace:^", "@6pm/currency": "workspace:^", "@6pm/validation": "workspace:^", + "@backpackapp-io/react-native-toast": "^0.11.0", "@clerk/clerk-expo": "^1.2.0", "@expo-google-fonts/be-vietnam-pro": "^0.2.3", "@expo/vector-icons": "^14.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1247e2f8..1bec9965 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: '@6pm/validation': specifier: workspace:^ version: link:../../packages/validation + '@backpackapp-io/react-native-toast': + specifier: ^0.11.0 + version: 0.11.0(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-reanimated@3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1) '@clerk/clerk-expo': specifier: ^1.2.0 version: 1.2.1(@types/react@18.2.79)(expo-auth-session@5.5.2(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(expo-web-browser@13.0.3(expo@51.0.14(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))))(react-dom@18.3.1(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1) @@ -1056,6 +1059,15 @@ packages: resolution: {integrity: sha512-XEFXSlxiG5td2EJRe8vOmRbaXVgfcBlszKujvVmWIK/UpywWljQCfzAv3RQCGujWQ1RD4YYWEAqDXfuJiy8f5Q==} engines: {node: '>=6.9.0'} + '@backpackapp-io/react-native-toast@0.11.0': + resolution: {integrity: sha512-ZRVYQPK6QOvt6vP1bF0I5oBpN5pnssdrX1JLV+KHPyxXWSNNvl1oo0qdJ5uzM7zj2TxMMUBPIsMofFeLA8E/dw==} + peerDependencies: + react: '*' + react-native: '*' + react-native-gesture-handler: '>=2.2.1' + react-native-reanimated: '>=2.8.0' + react-native-safe-area-context: '>=4.2.4' + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -7343,6 +7355,14 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@backpackapp-io/react-native-toast@0.11.0(react-native-gesture-handler@2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-reanimated@3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1)': + dependencies: + react: 18.3.1 + react-native: 0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1) + react-native-gesture-handler: 2.16.2(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1) + react-native-reanimated: 3.10.1(@babel/core@7.24.7)(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1) + react-native-safe-area-context: 4.10.1(react-native@0.74.2(@babel/core@7.24.7)(@babel/preset-env@7.24.7(@babel/core@7.24.7))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1) + '@bcoe/v8-coverage@0.2.3': {} '@biomejs/biome@1.8.1':