Skip to content

Commit

Permalink
feat(mobile): setup lingui (#63)
Browse files Browse the repository at this point in the history
Resolves #26
  • Loading branch information
bkdev98 authored Jun 9, 2024
1 parent ad70064 commit 6670104
Show file tree
Hide file tree
Showing 25 changed files with 1,388 additions and 110 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["macros"]
}
9 changes: 6 additions & 3 deletions apps/mobile/app/(app)/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useColorScheme } from '@/hooks/useColorScheme'
import { theme } from '@/lib/theme'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { Tabs } from 'expo-router'
import { CogIcon, LandPlotIcon, ScanTextIcon, WalletIcon } from 'lucide-react-native'

export default function TabLayout() {
const colorScheme = useColorScheme()
const { i18n } = useLingui()
return (
<Tabs
screenOptions={{
Expand Down Expand Up @@ -36,23 +39,23 @@ export default function TabLayout() {
<Tabs.Screen
name="budgets"
options={{
headerTitle: 'Budgets',
headerTitle: t(i18n)`Budgets`,
tabBarShowLabel: false,
tabBarIcon: ({ color }) => <LandPlotIcon color={color} />,
}}
/>
<Tabs.Screen
name="scanner"
options={{
headerTitle: 'Scanner',
headerTitle: t(i18n)`Scanner`,
tabBarShowLabel: false,
tabBarIcon: ({ color }) => <ScanTextIcon color={color} />,
}}
/>
<Tabs.Screen
name="settings"
options={{
headerTitle: 'Settings',
headerTitle: t(i18n)`Settings`,
tabBarShowLabel: false,
tabBarIcon: ({ color }) => <CogIcon color={color} />,
}}
Expand Down
46 changes: 33 additions & 13 deletions apps/mobile/app/(app)/(tabs)/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ import { Avatar, AvatarFallback, AvatarImage } from '@/components/Avatar'
import { Badge } from '@/components/Badge'
import { Button } from '@/components/Button'
import { IconButton } from '@/components/IconButton'
import { MenuItem } from '@/components/common/menu-item'
import { useLocale } from '@/locales/provider'
import { useAuth, useUser } from '@clerk/clerk-expo'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { Link } from 'expo-router'
import { LogOutIcon, PencilIcon, SwatchBookIcon } from 'lucide-react-native'
import { ChevronRightIcon, EarthIcon, LogOutIcon, PencilIcon, SwatchBookIcon } from 'lucide-react-native'
import { Alert, ScrollView, Text, View } from 'react-native'

export default function SettingsScreen() {
const { signOut } = useAuth()
const { user } = useUser()
const { i18n } = useLingui()
const { language } = useLocale()

return (
<ScrollView contentContainerClassName="py-4 gap-4" className="bg-card">
Expand Down Expand Up @@ -40,30 +46,44 @@ export default function SettingsScreen() {
</View>
<View className="gap-2 mt-4">
<Text className="font-sans mx-6 text-muted-foreground">
App settings
{t(i18n)`App settings`}
</Text>
<Link href="/appearance" asChild>
<Button
label="Appearance"
variant="ghost"
labelClasses="font-regular"
leftIcon={SwatchBookIcon}
className="justify-start gap-6 px-6 flex-1"
<MenuItem
label={t(i18n)`Appearance`}
icon={SwatchBookIcon}
rightSection={<ChevronRightIcon className='w-6 h-6 text-primary' />}
/>
</Link>
<Link href="/language" asChild>
<MenuItem
label={t(i18n)`Language`}
icon={EarthIcon}
rightSection={
<View className="flex flex-row items-center gap-2">
<Text className='text-muted-foreground font-sans'>
{t(i18n)`${language}`}
</Text>
<ChevronRightIcon className='w-6 h-6 text-primary' />
</View>
}
/>
</Link>
</View>
<View className="gap-2 mt-4">
<Text className="font-sans mx-6 text-muted-foreground">Others</Text>
<Text className="font-sans mx-6 text-muted-foreground">
{t(i18n)`Others`}
</Text>
<Button
label="Sign out"
label={t(i18n)`Sign out`}
variant="ghost"
onPress={() => Alert.alert('Are you sure you want to sign out?', '', [
onPress={() => Alert.alert(t(i18n)`Are you sure you want to sign out?`, '', [
{
text: 'Cancel',
text: t(i18n)`Cancel`,
style: 'cancel',
},
{
text: 'Sign out',
text: t(i18n)`Sign out`,
style: 'destructive',
onPress: () => signOut(),
},
Expand Down
11 changes: 10 additions & 1 deletion apps/mobile/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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 { useEffect } from 'react'

export default function AuthenticatedLayout() {
const { isLoaded, isSignedIn } = useAuth()
const colorScheme = useColorScheme()
const { i18n } = useLingui()

useEffect(() => {
if (isLoaded) {
Expand All @@ -26,13 +29,19 @@ export default function AuthenticatedLayout() {
presentation: 'modal',
}}
/>
<Stack.Screen
name="language"
options={{
presentation: 'modal',
}}
/>
<Stack.Screen
name="appearance"
options={{
headerShown: true,
headerBackTitleVisible: false,
headerTintColor: theme[colorScheme ?? 'light'].primary,
headerTitle: 'Appearance',
headerTitle: t(i18n)`Appearance`,
headerShadowVisible: false,
headerTitleStyle: {
fontFamily: 'Be Vietnam Pro Medium',
Expand Down
25 changes: 20 additions & 5 deletions apps/mobile/app/(app)/appearance.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
import { Tabs, TabsList, TabsTrigger } from '@/components/Tabs'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { MoonStarIcon, SmartphoneIcon, SunIcon } from 'lucide-react-native'
import { useColorScheme } from 'nativewind'
import { ScrollView, Text } from 'react-native'

export default function AppearanceScreen() {
const { colorScheme, setColorScheme } = useColorScheme()
const { i18n } = useLingui()

return (
<ScrollView className="bg-card" contentContainerClassName="px-6 py-3">
<Text className="font-sans text-primary font-medium text-base">
App theme
{t(i18n)`App theme`}
</Text>
<Text className="font-sans text-muted-foreground text-sm mb-4">
Choose a preferred theme for the 6pm
{t(i18n)`Choose a preferred theme for the 6pm`}
</Text>
<Tabs
defaultValue={colorScheme || 'light'}
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
onChange={(value: any) => setColorScheme(value)}
>
<TabsList>
<TabsTrigger value="light" title="Light" icon={SunIcon} />
<TabsTrigger value="dark" title="Dark" icon={MoonStarIcon} />
<TabsTrigger value="system" title="System" icon={SmartphoneIcon} />
<TabsTrigger
value="light"
title={t(i18n)`Light`}
icon={SunIcon}
/>
<TabsTrigger
value="dark"
title={t(i18n)`Dark`}
icon={MoonStarIcon}
/>
<TabsTrigger
value="system"
title={t(i18n)`System`}
icon={SmartphoneIcon}
/>
</TabsList>
</Tabs>
</ScrollView>
Expand Down
37 changes: 37 additions & 0 deletions apps/mobile/app/(app)/language.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { MenuItem } from '@/components/common/menu-item'
import { useLocale } from '@/locales/provider'
import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { useRouter } from 'expo-router'
import { CheckCircleIcon } from 'lucide-react-native'
import { ScrollView, Text } from 'react-native'

export default function LanguageScreen() {
const { i18n } = useLingui()
const { language, setLanguage } = useLocale()
const router = useRouter()

return (
<ScrollView className="bg-card">
<Text className="font-sans font-medium text-primary 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' />}
onPress={() => {
setLanguage('en')
router.back()
}}
/>
<MenuItem
label={t(i18n)`Vietnamese`}
rightSection={language === 'vi' && <CheckCircleIcon className='w-5 h-5 text-primary' />}
onPress={() => {
setLanguage('vi')
router.back()
}}
/>
</ScrollView>
)
}
43 changes: 26 additions & 17 deletions apps/mobile/app/(auth)/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,58 @@ import {
GoogleAuthButton,
} from '@/components/auth/auth-social'
import { AuthIllustration } from '@/components/svg-assets/auth-illustration'
import { Trans, t } from '@lingui/macro'
import { useLingui } from '@lingui/react'
import { Link } from 'expo-router'
import { MailIcon } from 'lucide-react-native'
import { useState } from 'react'
import { ScrollView, Text, View } from 'react-native'

export default function LoginScreen() {
const [withEmail, setWithEmail] = useState(false)
const { i18n } = useLingui()
return (
<ScrollView
className="bg-card"
contentContainerClassName="gap-4 p-8"
automaticallyAdjustKeyboardInsets
keyboardShouldPersistTaps="handled"
>
<Text className="text-3xl text-primary font-semibold font-sans">
Manage your expense seamlessly
</Text>
<Text className="text-muted-foreground font-sans">
Let <Text className="text-primary">6pm</Text> a good time to spend
</Text>
<Trans>
<View className='gap-4'>
<Text className="text-3xl text-primary font-semibold font-sans">
Manage your expense seamlessly
</Text>
<Text className="text-muted-foreground font-sans">
Let <Text className="text-primary">6pm</Text> a good time to spend
</Text>
</View>
</Trans>
<AuthIllustration className="h-[326px] my-16 text-primary" />
<View className="flex flex-col gap-3">
<AppleAuthButton />
<GoogleAuthButton />
<Button
label="Continue with Email"
label={t(i18n)`Continue with Email`}
leftIcon={MailIcon}
variant="outline"
onPress={() => setWithEmail(true)}
/>
<Separator className="w-[70%] mx-auto my-3" />
{withEmail && <AuthEmail />}
</View>
<Text className="font-sans text-muted-foreground text-xs text-center mx-auto px-4 mt-4">
By continuing, you acknowledge that you understand and agree to the{' '}
<Link href="/terms-of-service" asChild className="text-primary">
<Text>Terms & Conditions</Text>
</Link>{' '}
and{' '}
<Link href="/privacy-policy" asChild className="text-primary">
<Text>Privacy Policy</Text>
</Link>
</Text>
<Trans>
<Text className="font-sans text-muted-foreground text-xs text-center mx-auto px-4 mt-4">
By continuing, you acknowledge that you understand and agree to the{' '}
<Link href="/terms-of-service" asChild className="text-primary">
<Text>Terms & Conditions</Text>
</Link>{' '}
and{' '}
<Link href="/privacy-policy" asChild className="text-primary">
<Text>Privacy Policy</Text>
</Link>
</Text>
</Trans>
</ScrollView>
)
}
21 changes: 4 additions & 17 deletions apps/mobile/app/+not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,18 @@
import { Link, Stack } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'
import { Text, View } from 'react-native'

import { Button } from '@/components/Button'

export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Oops!' }} />
<View className="flex-1 items-center justify-center p-4">
<Text>This screen doesn't exist.</Text>
<Link href="/" style={styles.link} asChild={true}>
<View className="flex-1 items-center justify-center p-4 gap-4">
<Text className='font-sans text-primary font-medium'>This screen doesn't exist.</Text>
<Link href="/" asChild={true}>
<Button label="Go to home screen!" />
</Link>
</View>
</>
)
}

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
link: {
marginTop: 15,
paddingVertical: 15,
},
})
Loading

0 comments on commit 6670104

Please sign in to comment.