Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

feat(mobile): setup lingui #63

Merged
merged 1 commit into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)/#.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