Skip to content

Commit

Permalink
feat(mobile): [Transaction] add select wallet account field (#95)
Browse files Browse the repository at this point in the history
  • Loading branch information
bkdev98 committed Jul 11, 2024
1 parent 738a1aa commit 04d94e5
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 18 deletions.
16 changes: 14 additions & 2 deletions apps/mobile/app/(app)/new-record.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import { TransactionForm } from '@/components/transaction/transaction-form'
import { useWallets } from '@/queries/wallet'
import { useRouter } from 'expo-router'
import { LoaderIcon } from 'lucide-react-native'
import { View } from 'react-native'

export default function NewRecordScreen() {
const router = useRouter()
const { data: walletAccounts } = useWallets()

const defaultWallet = walletAccounts?.[0]

if (!defaultWallet) {
return (
<View className="flex-1 items-center bg-muted justify-center">
<LoaderIcon className="size-7 animate-spin text-primary" />
</View>
)
}

return (
<TransactionForm
onSubmit={(values) => console.log('submit', values)}
onCancel={router.back}
defaultValues={{
walletAccountId: walletAccounts?.[0].id,
currency: walletAccounts?.[0].preferredCurrency,
walletAccountId: defaultWallet.id,
currency: defaultWallet.preferredCurrency ?? 'USD',
}}
/>
)
Expand Down
121 changes: 121 additions & 0 deletions apps/mobile/components/transaction/select-account-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { sleep } from '@/lib/utils'
import { useWallets } from '@/queries/wallet'
import type { UserWalletAccount } from '@6pm/validation'
import {
BottomSheetBackdrop,
BottomSheetFlatList,
BottomSheetModal,
} from '@gorhom/bottom-sheet'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
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'

export function SelectAccountField({
onSelect,
}: {
onSelect?: (walletAccount: UserWalletAccount) => void
}) {
const { bottom } = useSafeAreaInsets()
const { data: walletAccounts, isLoading } = useWallets()

const sheetRef = useRef<BottomSheetModal>(null)
const { i18n } = useLingui()
const {
field: { onChange, onBlur, value },
} = useController({ name: 'walletAccountId' })

const selectedWalletAccount = walletAccounts?.find(
(walletAccount) => walletAccount.id === value,
)

return (
<>
<Button
variant="secondary"
className="border border-border !px-3 max-w-[160px]"
disabled={isLoading}
onPress={() => {
Keyboard.dismiss()
sheetRef.current?.present()
}}
>
{!!selectedWalletAccount && (
<GenericIcon
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
name={selectedWalletAccount.icon as any}
className="w-5 h-5 text-primary"
/>
)}
<Text className="shrink line-clamp-1">
{selectedWalletAccount?.name || t(i18n)`Select account`}
</Text>
</Button>
<BottomSheetModal
ref={sheetRef}
index={0}
enableDynamicSizing
enablePanDownToClose
keyboardBehavior="extend"
backdropComponent={(props) => (
<BottomSheetBackdrop
{...props}
appearsOnIndex={0}
disappearsOnIndex={-1}
enableTouchThrough
/>
)}
containerComponent={(props) => (
<FullWindowOverlay>{props.children}</FullWindowOverlay>
)}
>
<BottomSheetFlatList
data={walletAccounts}
numColumns={4}
keyExtractor={(i) => i.id}
contentContainerClassName="px-4 gap-4"
columnWrapperClassName="gap-2"
showsVerticalScrollIndicator={false}
showsHorizontalScrollIndicator={false}
keyboardShouldPersistTaps="handled"
keyboardDismissMode="on-drag"
ListHeaderComponent={
<Text className="text-base font-medium text-center pt-2">
{t(i18n)`Wallet Accounts`}
</Text>
}
contentContainerStyle={{ paddingBottom: bottom + 16 }}
renderItem={({ item }) => (
<Button
size="icon"
className="h-20 flex-1 flex gap-2 px-2 flex-col flex-grow"
variant={value === item ? 'secondary' : 'ghost'}
onPress={async () => {
sheetRef.current?.close()
await sleep(500)
onChange(item.id)
onBlur()
onSelect?.(item)
}}
>
<GenericIcon
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
name={item.icon as any}
className="size-8 text-foreground"
/>
<Text className="text-muted-foreground line-clamp-1 text-center !text-sm">
{item.name}
</Text>
</Button>
)}
/>
</BottomSheetModal>
</>
)
}
26 changes: 11 additions & 15 deletions apps/mobile/components/transaction/transaction-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,7 @@ import {
import { zodResolver } from '@hookform/resolvers/zod'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import {
Calendar,
CreditCard,
LandPlot,
ShapesIcon,
XIcon,
} from 'lucide-react-native'
import { Calendar, LandPlot, ShapesIcon, XIcon } from 'lucide-react-native'
import { Controller, FormProvider, useForm } from 'react-hook-form'
import { ScrollView, View } from 'react-native'
import Animated, {
Expand All @@ -24,6 +18,7 @@ import { NumericPad } from '../numeric-pad'
import { TextTicker } from '../text-ticker'
import { Button } from '../ui/button'
import { Text } from '../ui/text'
import { SelectAccountField } from './select-account-field'

type TransactionFormProps = {
onSubmit: (data: TransactionFormValues) => void
Expand All @@ -50,7 +45,7 @@ export const TransactionForm = ({
defaultValues: {
date: new Date(),
amount: 0,
currency: 'VND',
currency: 'USD',
note: '',
...defaultValues,
},
Expand Down Expand Up @@ -102,13 +97,14 @@ export const TransactionForm = ({
<Animated.View style={translateStyle}>
<View className="flex-row items-center justify-between bg-card border-t border-border p-2">
<View className="flex-row items-center gap-2">
<Button
variant="secondary"
className="border border-border !px-3"
>
<CreditCard className="w-5 h-5 text-primary" />
<Text>{t(i18n)`Credit Card`}</Text>
</Button>
<SelectAccountField
onSelect={(walletAccount) => {
transactionForm.setValue(
'currency',
walletAccount.preferredCurrency,
)
}}
/>
<Button
variant="secondary"
className="border border-border !px-3"
Expand Down
7 changes: 6 additions & 1 deletion apps/mobile/queries/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getHonoClient } from '@/lib/client'
import { UserWalletAccountSchema } from '@6pm/validation'
import { createQueryKeys } from '@lukemorales/query-key-factory'
import { useQuery } from '@tanstack/react-query'

Expand All @@ -11,7 +12,11 @@ export const walletQueries = createQueryKeys('wallet', {
if (!res.ok) {
throw new Error(await res.text())
}
return await res.json()
const rawResult = await res.json()
const result = rawResult.map((item) =>
UserWalletAccountSchema.parse(item),
)
return result
},
}),
})
Expand Down

0 comments on commit 04d94e5

Please sign in to comment.