Skip to content

Commit

Permalink
feat(mobile): [Wallet] add basic account form with icon and currency …
Browse files Browse the repository at this point in the history
  • Loading branch information
bkdev98 committed Jun 21, 2024
1 parent 6042fb9 commit b28849e
Show file tree
Hide file tree
Showing 36 changed files with 1,931 additions and 123 deletions.
2 changes: 1 addition & 1 deletion apps/mobile/app/(app)/(tabs)/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default function SettingsScreen() {
{t(i18n)`General`}
</Text>
<View>
<Link href="/wallet-accounts" asChild disabled>
<Link href="/wallet/accounts" asChild>
<MenuItem
label={t(i18n)`Wallet accounts`}
icon={WalletCardsIcon}
Expand Down
61 changes: 46 additions & 15 deletions apps/mobile/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { BackButton } from '@/components/common/back-button'
import { Button } from '@/components/ui/button'
import { useColorScheme } from '@/hooks/useColorScheme'
import { theme } from '@/lib/theme'
import { useAuth } from '@clerk/clerk-expo'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { Redirect, SplashScreen, Stack } from 'expo-router'
import { Link, Redirect, SplashScreen, Stack } from 'expo-router'
import { PlusIcon } from 'lucide-react-native'
import { useEffect } from 'react'

export default function AuthenticatedLayout() {
Expand All @@ -22,35 +25,63 @@ export default function AuthenticatedLayout() {
}

return (
<Stack screenOptions={{ headerShown: false }}>
<Stack screenOptions={{
headerShown: true,
headerBackTitleVisible: false,
headerTintColor: theme[colorScheme ?? 'light'].primary,
headerShadowVisible: false,
headerTitleStyle: {
fontFamily: 'Be Vietnam Pro Medium',
fontSize: 16,
color: theme[colorScheme ?? 'light'].primary,
},
headerStyle: {
backgroundColor: theme[colorScheme ?? 'light'].background,
},
headerLeft: () => <BackButton />,
}}>
<Stack.Screen
name="(tabs)"
options={{
headerShown: false,
}}
/>
<Stack.Screen
name="new-record"
options={{
presentation: 'modal',
headerShown: false
}}
/>
<Stack.Screen
name="language"
options={{
presentation: 'modal',
headerTitle: t(i18n)`Language`,
}}
/>
<Stack.Screen
name="appearance"
options={{ headerTitle: t(i18n)`Appearance` }}
/>
<Stack.Screen
name="wallet/accounts"
options={{
headerTitle: t(i18n)`Wallet accounts`,
headerRight: () => (
<Link href="/wallet/new-account" asChild>
<Button size='icon' variant='ghost'>
<PlusIcon className='size-6 text-primary' />
</Button>
</Link>
)
}}
/>
<Stack.Screen
name="wallet/new-account"
options={{
headerShown: true,
headerBackTitleVisible: false,
headerTintColor: theme[colorScheme ?? 'light'].primary,
headerTitle: t(i18n)`Appearance`,
headerShadowVisible: false,
headerTitleStyle: {
fontFamily: 'Be Vietnam Pro Medium',
fontSize: 16,
color: theme[colorScheme ?? 'light'].primary,
},
headerStyle: {
backgroundColor: theme[colorScheme ?? 'light'].background,
}
// presentation: 'modal',
headerTitle: t(i18n)`New account`
}}
/>
</Stack>
Expand Down
16 changes: 10 additions & 6 deletions apps/mobile/app/(app)/language.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { MenuItem } from '@/components/common/menu-item'
import { Text } from '@/components/ui/text'
import { useLocale } from '@/locales/provider'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
Expand All @@ -14,20 +13,25 @@ export default function LanguageScreen() {

return (
<ScrollView className="bg-card">
<Text className="font-medium text-lg m-6 mx-auto">
{t(i18n)`Language`}
</Text>
<MenuItem
label={t(i18n)`English`}
rightSection={language === 'en' && <CheckCircleIcon className='w-5 h-5 text-primary' />}
rightSection={
language === 'en' && (
<CheckCircleIcon className="w-5 h-5 text-primary" />
)
}
onPress={() => {
setLanguage('en')
router.back()
}}
/>
<MenuItem
label={t(i18n)`Vietnamese`}
rightSection={language === 'vi' && <CheckCircleIcon className='w-5 h-5 text-primary' />}
rightSection={
language === 'vi' && (
<CheckCircleIcon className="w-5 h-5 text-primary" />
)
}
onPress={() => {
setLanguage('vi')
router.back()
Expand Down
42 changes: 42 additions & 0 deletions apps/mobile/app/(app)/wallet/accounts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { AddNewButton } from '@/components/common/add-new-button'
import { Text } from '@/components/ui/text'
import { WalletAccountItem } from '@/components/wallet/wallet-account-item'
import { useWallets } from '@/queries/wallet'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { useRouter } from 'expo-router'
import { FlatList } from 'react-native'

export default function WalletAccountsScreen() {
const { i18n } = useLingui()
const { data: walletAccounts, isLoading, refetch } = useWallets()
const router = useRouter()
return (
<FlatList
className="bg-card"
contentContainerClassName="py-3"
data={walletAccounts}
renderItem={({ item }) => (
<WalletAccountItem
// Date is typed as string in rpc output, not sure why
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
data={item as any}
/>
)}
keyExtractor={(item) => item.id}
refreshing={isLoading}
onRefresh={refetch}
ListFooterComponent={
<AddNewButton
label={t(i18n)`New account`}
onPress={() => router.push('/wallet/new-account')}
/>
}
ListEmptyComponent={
<Text className="font-sans text-muted-foreground text-center mt-6 mb-9">
{t(i18n)`empty`}
</Text>
}
/>
)
}
62 changes: 62 additions & 0 deletions apps/mobile/app/(app)/wallet/new-account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { AccountForm } from '@/components/wallet/account-form'
import { createWallet } from '@/mutations/wallet'
import { walletQueries } from '@/queries/wallet'
import { useMutation, useQueryClient } from '@tanstack/react-query'
import { useRouter } from 'expo-router'
import { Alert, ScrollView } from 'react-native'

export default function NewAccountScreen() {
const queryClient = useQueryClient()
const router = useRouter()
const { mutateAsync } = useMutation({
mutationFn: createWallet,
async onMutate(newWallet) {
// Cancel any outgoing refetches
// (so they don't overwrite our optimistic update)
await queryClient.cancelQueries({
queryKey: walletQueries.list().queryKey,
})
// Snapshot the previous value
const previousWallets = queryClient.getQueryData(
walletQueries.list().queryKey,
)
// Optimistically update to the new value
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
queryClient.setQueryData(walletQueries.list().queryKey, (old: any) => [
...old,
newWallet,
])
// Return a context object with the snapshotted value
return { previousWallets }
},
onError(error, _, context) {
Alert.alert(error.message)
// use the context returned from onMutate to rollback
queryClient.setQueryData(
walletQueries.list().queryKey,
context?.previousWallets,
)
},
onSuccess() {
router.back()
},
async onSettled() {
// Always refetch after error or success:
await queryClient.invalidateQueries({
queryKey: walletQueries._def,
})
},
throwOnError: true,
})

return (
<ScrollView
className="bg-card flex-1"
contentContainerClassName="gap-4 p-6"
automaticallyAdjustKeyboardInsets
keyboardShouldPersistTaps="handled"
>
<AccountForm onSubmit={mutateAsync} />
</ScrollView>
)
}
45 changes: 45 additions & 0 deletions apps/mobile/app/(aux)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { BackButton } from '@/components/common/back-button'
import { useColorScheme } from '@/hooks/useColorScheme'
import { theme } from '@/lib/theme'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { Stack } from 'expo-router'

export default function AuxiliaryLayout() {
const { colorScheme } = useColorScheme()
const { i18n } = useLingui()
return (
<Stack
screenOptions={{
headerShown: true,
headerBackTitleVisible: false,
headerTintColor: theme[colorScheme ?? 'light'].primary,
headerShadowVisible: false,
headerTitleStyle: {
fontFamily: 'Be Vietnam Pro Medium',
fontSize: 16,
color: theme[colorScheme ?? 'light'].primary,
},
headerStyle: {
backgroundColor: theme[colorScheme ?? 'light'].background,
},
headerLeft: () => <BackButton />,
}}
>
<Stack.Screen
name="privacy-policy"
options={{
presentation: 'modal',
headerTitle: t(i18n)`Privacy Policy`,
}}
/>
<Stack.Screen
name="terms-of-service"
options={{
presentation: 'modal',
headerTitle: t(i18n)`Terms & Conditions`,
}}
/>
</Stack>
)
}
6 changes: 5 additions & 1 deletion apps/mobile/app/(aux)/privacy-policy.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { Text } from 'react-native'

export default function PrivacyScreen() {
return <Text className="font-sans m-4 mx-auto">Privacy Policy</Text>
return (
<Text className="font-sans m-4">
lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</Text>
)
}
4 changes: 3 additions & 1 deletion apps/mobile/app/(aux)/terms-of-service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { Text } from 'react-native'

export default function TermsScreen() {
return (
<Text className="font-sans m-4 mx-auto">Terms & Conditions</Text>
<Text className="font-sans m-4">
lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</Text>
)
}
34 changes: 17 additions & 17 deletions apps/mobile/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,22 @@ import {
useFonts,
} from '@expo-google-fonts/be-vietnam-pro'
import { SplashScreen, Stack } from 'expo-router'
import * as WebBrowser from "expo-web-browser";
import * as WebBrowser from 'expo-web-browser'

import 'react-native-reanimated'
import { useWarmUpBrowser } from '@/hooks/use-warm-up-browser'
import { useColorScheme } from '@/hooks/useColorScheme'
import { queryClient } from '@/lib/client'
import { LocaleProvider } from '@/locales/provider'
import { BottomSheetModalProvider } from '@gorhom/bottom-sheet'
import {
DarkTheme,
DefaultTheme,
ThemeProvider,
} from '@react-navigation/native'
import { QueryClientProvider } from '@tanstack/react-query'
import { cssInterop } from 'nativewind'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { SafeAreaProvider } from 'react-native-safe-area-context'
import { Svg } from 'react-native-svg'

Expand All @@ -38,15 +40,15 @@ cssInterop(Svg, {
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync()

WebBrowser.maybeCompleteAuthSession();
WebBrowser.maybeCompleteAuthSession()

// biome-ignore lint/style/useNamingConvention: <explanation>
export const unstable_settings = {
initialRouteName: '(app)',
}

export default function RootLayout() {
useWarmUpBrowser();
useWarmUpBrowser()
const { colorScheme } = useColorScheme()
const [fontsLoaded] = useFonts({
BeVietnamPro_300Light,
Expand All @@ -73,20 +75,18 @@ export default function RootLayout() {
value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}
>
<SafeAreaProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen
name="(aux)/privacy-policy"
options={{
presentation: 'modal',
}}
/>
<Stack.Screen
name="(aux)/terms-of-service"
options={{
presentation: 'modal',
}}
/>
</Stack>
<GestureHandlerRootView>
<BottomSheetModalProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen
name="(aux)"
options={{
presentation: 'modal',
}}
/>
</Stack>
</BottomSheetModalProvider>
</GestureHandlerRootView>
</SafeAreaProvider>
</ThemeProvider>
</LocaleProvider>
Expand Down
Loading

0 comments on commit b28849e

Please sign in to comment.