Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(mobile): app layout #61

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
53 changes: 36 additions & 17 deletions apps/mobile/app/(app)/(tabs)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,56 @@
import { useColorScheme } from '@/hooks/useColorScheme'
import { theme } from '@/lib/theme'
import { Tabs } from 'expo-router'

import { TabBarIcon } from '@/components/navigation/TabBarIcon'
import { CogIcon, LandPlotIcon, ScanTextIcon, WalletIcon } from 'lucide-react-native'

export default function TabLayout() {
const colorScheme = useColorScheme()
return (
<Tabs
screenOptions={{
// tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
headerShadowVisible: false,
tabBarActiveTintColor: theme[colorScheme ?? 'light'].primary,
tabBarShowLabel: false,
tabBarStyle: {
borderTopWidth: 0,
},
headerTitleStyle: {
fontFamily: 'Be Vietnam Pro',
fontSize: 16,
color: theme[colorScheme ?? 'light'].primary,
},
}}
>
<Tabs.Screen
name="index"
options={{
headerShown: false,
tabBarShowLabel: false,
tabBarIcon: ({ color }) => <WalletIcon color={color} />,
}}
/>
<Tabs.Screen
name="budgets"
options={{
headerTitle: 'Budgets',
tabBarShowLabel: false,
tabBarIcon: ({ color }) => <LandPlotIcon color={color} />,
}}
/>
<Tabs.Screen
name="scanner"
options={{
headerTitle: 'Scanner',
tabBarShowLabel: false,
tabBarIcon: ({ color, focused }) => (
<TabBarIcon
name={focused ? 'home' : 'home-outline'}
color={color}
/>
),
tabBarIcon: ({ color }) => <ScanTextIcon color={color} />,
}}
/>
<Tabs.Screen
name="explore"
name="settings"
options={{
headerTitle: 'Settings',
tabBarShowLabel: false,
tabBarIcon: ({ color, focused }) => (
<TabBarIcon
name={focused ? 'code-slash' : 'code-slash-outline'}
color={color}
/>
),
tabBarIcon: ({ color }) => <CogIcon color={color} />,
}}
/>
</Tabs>
Expand Down
11 changes: 11 additions & 0 deletions apps/mobile/app/(app)/(tabs)/budgets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Toolbar } from '@/components/common/toolbar'
import { Text, View } from 'react-native'

export default function BudgetsScreen() {
return (
<View className="flex-1 bg-card">
<Text className="font-sans">Budgets Screen</Text>
<Toolbar />
</View>
)
}
37 changes: 0 additions & 37 deletions apps/mobile/app/(app)/(tabs)/explore.tsx

This file was deleted.

16 changes: 14 additions & 2 deletions apps/mobile/app/(app)/(tabs)/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
import { Text } from 'react-native'
import { Toolbar } from '@/components/common/toolbar'
import { HomeHeader } from '@/components/home/header'
import { ScrollView, Text, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'

export default function HomeScreen() {
return <Text className="font-sans">Home Screen</Text>
const { top } = useSafeAreaInsets()
return (
<View className="flex-1 bg-card" style={{ paddingTop: top }}>
<HomeHeader />
<ScrollView>
<Text className="font-sans">Home Screen</Text>
</ScrollView>
<Toolbar />
</View>
)
}
11 changes: 11 additions & 0 deletions apps/mobile/app/(app)/(tabs)/scanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Toolbar } from '@/components/common/toolbar'
import { Text, View } from 'react-native'

export default function ScannerScreen() {
return (
<View className="flex-1 bg-card">
<Text className="font-sans">Scanner Screen</Text>
<Toolbar />
</View>
)
}
53 changes: 53 additions & 0 deletions apps/mobile/app/(app)/(tabs)/settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Avatar, AvatarFallback, AvatarImage } from '@/components/Avatar'
import { Badge } from '@/components/Badge'
import { Button } from '@/components/Button'
import { IconButton } from '@/components/IconButton'
import { useAuth, useUser } from '@clerk/clerk-expo'
import { LogOutIcon, PencilIcon } from 'lucide-react-native'
import { ScrollView, Text, View } from 'react-native'

export default function SettingsScreen() {
const { signOut } = useAuth()
const { user } = useUser()

return (
<ScrollView contentContainerClassName="py-4" className="bg-card">
<View className="bg-muted rounded-lg mx-6 px-4 py-3 justify-end h-40">
<View className="flex flex-row items-center gap-2 justify-between">
<View className="flex flex-row items-center gap-3">
<Avatar className="w-12 h-12">
<AvatarImage
source={{
uri: user?.imageUrl,
}}
/>
<AvatarFallback>QK</AvatarFallback>
</Avatar>
<View>
<Badge
variant="secondary"
label="Free"
className="self-start rounded-md mb-1"
/>
<Text className="font-medium">
{user?.fullName ?? user?.primaryEmailAddress?.emailAddress}
</Text>
</View>
</View>
<IconButton icon={PencilIcon} size="sm" variant="ghost" />
</View>
</View>
<Text className='font-sans mx-6 text-muted-foreground mt-6'>
Others
</Text>
<Button
label="Sign out"
variant="ghost"
onPress={() => signOut()}
labelClasses="text-red-500 font-regular"
leftIcon={LogOutIcon}
className="justify-start gap-6 px-6"
/>
</ScrollView>
)
}
11 changes: 10 additions & 1 deletion apps/mobile/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,14 @@ export default function AuthenticatedLayout() {
return <Redirect href={'/login'} />
}

return <Stack />
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen
name="new-record"
options={{
presentation: 'modal',
}}
/>
</Stack>
)
}
5 changes: 5 additions & 0 deletions apps/mobile/app/(app)/new-record.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Text } from 'react-native'

export default function NewRecordScreen() {
return <Text className="font-sans m-4 mx-auto">New Record</Text>
}
8 changes: 4 additions & 4 deletions apps/mobile/components/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import { Text, View } from 'react-native'
import { cn } from '../lib/utils'

const badgeVariants = cva(
'flex flex-row items-center rounded-full px-2 py-1 text-xs font-semibold',
'flex flex-row items-center rounded-full px-2 py-0.5 text-xs font-semibold',
{
variants: {
variant: {
default: 'bg-primary',
secondary: 'bg-secondary',
secondary: 'bg-secondary border border-border',
destructive: 'bg-destructive',
success: 'bg-green-500 dark:bg-green-700',
},
Expand All @@ -24,7 +24,7 @@ const badgeTextVariants = cva('font-medium text-center text-xs', {
variants: {
variant: {
default: 'text-primary-foreground',
secondary: 'text-secondary-foreground',
secondary: 'text-muted-foreground',
destructive: 'text-destructive-foreground',
success: 'text-green-100',
},
Expand All @@ -36,7 +36,7 @@ const badgeTextVariants = cva('font-medium text-center text-xs', {

export interface BadgeProps
extends React.ComponentPropsWithoutRef<typeof View>,
VariantProps<typeof badgeVariants> {
VariantProps<typeof badgeVariants> {
label: string
labelClasses?: string
}
Expand Down
4 changes: 2 additions & 2 deletions apps/mobile/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const buttonVariants = cva(
secondary: 'bg-secondary',
outline: 'border-border border',
destructive: 'bg-destructive',
ghost: 'bg-slate-700',
ghost: 'bg-transparent',
link: 'text-primary underline-offset-4',
},
size: {
Expand All @@ -36,7 +36,7 @@ const buttonTextVariants = cva('text-center font-medium font-sans', {
secondary: 'text-secondary-foreground',
outline: 'text-primary',
destructive: 'text-destructive-foreground',
ghost: 'text-primary-foreground',
ghost: 'text-primary',
link: 'text-primary-foreground underline',
},
size: {
Expand Down
19 changes: 12 additions & 7 deletions apps/mobile/components/IconButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { type VariantProps, cva } from 'class-variance-authority'
import { TouchableOpacity } from 'react-native'

import { forwardRef } from 'react'
import type { SvgProps } from 'react-native-svg'
import { cn } from '../lib/utils'

Expand All @@ -17,9 +18,10 @@ const buttonVariants = cva(
link: 'text-primary underline-offset-4',
},
size: {
default: 'h-12 w-12',
default: 'h-10 w-10',
lg: 'h-12 w-12',
sm: 'h-8 w-8',
lg: 'h-14 w-14',
xl: 'h-14 w-14',
},
},
defaultVariants: {
Expand All @@ -40,9 +42,10 @@ const iconVariants = cva('text-center font-medium font-sans', {
link: 'text-primary-foreground underline',
},
size: {
default: 'text-base',
default: 'w-5 h-5',
sm: 'w-5 h-5',
lg: 'text-xl',
lg: 'w-6 h-6',
xl: 'w-6 h-6',
},
},
defaultVariants: {
Expand All @@ -57,17 +60,19 @@ export interface IconButtonProps
icon: React.ComponentType<SvgProps>
iconClasses?: string
}
function IconButton({

const IconButton = forwardRef(function ({
icon: Icon,
iconClasses,
className,
variant,
size,
disabled,
...props
}: IconButtonProps) {
}: IconButtonProps, ref: React.ForwardedRef<TouchableOpacity>) {
return (
<TouchableOpacity
ref={ref}
activeOpacity={0.8}
className={cn(buttonVariants({ variant, size, className }), {
'opacity-50': disabled,
Expand All @@ -80,6 +85,6 @@ function IconButton({
/>
</TouchableOpacity>
)
}
})

export { IconButton, buttonVariants, iconVariants }
7 changes: 7 additions & 0 deletions apps/mobile/components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ const Input = forwardRef<React.ElementRef<typeof TextInput>, InputProps>(
<View className={cn('flex flex-col gap-1.5', className)}>
{label && <Text className={cn('text-base', labelClasses)}>{label}</Text>}
<View>
{leftSection && (
<View className="absolute left-2 top-1/2 transform -translate-y-1/2">
{leftSection}
</View>
)}
<TextInput
className={cn(
inputClasses,
'border border-border placeholder-input py-2.5 px-4 rounded-lg font-sans',
leftSection && 'pl-10',
rightSection && 'pr-10',
)}
{...props}
/>
Expand Down
20 changes: 20 additions & 0 deletions apps/mobile/components/common/toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Link } from 'expo-router'
import { PlusIcon, Sparkles } from 'lucide-react-native'
import { View } from 'react-native'
import { IconButton } from '../IconButton'
import { Input } from '../Input'

export function Toolbar() {
return (
<View className="gap-3 items-center absolute flex-row bottom-4 left-6 right-6">
<Input
placeholder="Ask AI anything..."
leftSection={<Sparkles className="w-5 h-5 text-muted-foreground" />}
className="flex-1"
/>
<Link href="/new-record" asChild>
<IconButton icon={PlusIcon} className="w-10 h-10" iconClasses='size-6' />
</Link>
</View>
)
}
Loading