Skip to content

Commit 818e92c

Browse files
authored
Feat: app onboarding (#129)
* wip * add badge theme * add devtools * fix icons * minor fixs * setup screens * animated onboarding screens * fix public layout dark mode with new use themed style hook * fix dark mode * fix scroll on large screen * animated mascot * clean code and fix status bar
1 parent b96a683 commit 818e92c

31 files changed

+634
-59
lines changed

β€ŽREADME.mdβ€Ž

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ pnpm dev:ios # To run with local ios build
7878
pnpm dev:android # To run with local android build
7979
```
8080

81+
### Devtools
82+
83+
You can use @dev-plugins pressing `Shift + m` in your Expo terminal.
84+
8185
## Storybook
8286

8387
Storybook is managed as a specific mode of the app that is launch apart in port 8083

β€Žpackage.jsonβ€Ž

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
"dependencies": {
2525
"@bearstudio/ui-state": "1.0.2",
2626
"@better-auth/expo": "1.3.27",
27+
"@dev-plugins/async-storage": "0.4.0",
28+
"@dev-plugins/react-navigation": "0.4.0",
29+
"@dev-plugins/react-query": "0.4.0",
2730
"@expo/metro-runtime": "6.1.2",
2831
"@gorhom/bottom-sheet": "5.2.6",
2932
"@react-native-async-storage/async-storage": "2.2.0",
@@ -68,7 +71,8 @@
6871
"react-native-web": "0.21.1",
6972
"react-native-worklets": "0.5.1",
7073
"sonner-native": "0.21.1",
71-
"zod": "4.1.11"
74+
"zod": "4.1.11",
75+
"zustand": "5.0.8"
7276
},
7377
"devDependencies": {
7478
"@babel/core": "7.28.4",

β€Žpnpm-lock.yamlβ€Ž

Lines changed: 97 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žsrc/app/(logged)/(tabs)/_layout.tsxβ€Ž

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,8 @@ import { Icon, Label, NativeTabs } from 'expo-router/unstable-native-tabs';
33
import { ComponentProps } from 'react';
44
import { useTranslation } from 'react-i18next';
55
import { DynamicColorIOS } from 'react-native';
6-
import { useColorModeValue } from 'react-native-ficus-ui';
76

8-
import theme from '@/lib/ficus-ui/theme';
7+
import { useThemedStyle } from '@/hooks/use-themed-style';
98

109
import { HapticTab } from '@/components/haptic-tab';
1110
import {
@@ -58,18 +57,7 @@ const TABS = [
5857
export default function TabLayout() {
5958
const { t } = useTranslation(['layout']);
6059

61-
const themedStyle = useColorModeValue(
62-
{
63-
backgroundColor: 'white',
64-
color: theme.colors.neutral[950],
65-
sceneBackgroundColor: theme.colors.neutral[50],
66-
},
67-
{
68-
backgroundColor: theme.colors.neutral[950],
69-
color: 'white',
70-
sceneBackgroundColor: theme.colors.neutral[900],
71-
}
72-
);
60+
const themedStyle = useThemedStyle();
7361

7462
if (WITH_NATIVE_TABS) {
7563
return (

β€Žsrc/app/(logged)/_layout.tsxβ€Ž

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import { Stack, useRouter } from 'expo-router';
22
import { useEffect } from 'react';
33
import { useTranslation } from 'react-i18next';
4-
import { useColorModeValue } from 'react-native-ficus-ui';
54

6-
import theme from '@/lib/ficus-ui/theme';
5+
import { useThemedStyle } from '@/hooks/use-themed-style';
76

87
import { authClient } from '@/features/auth/client';
9-
import { ViewOnboarding } from '@/features/auth/view-onboarding';
8+
import { ViewAuthOnboarding } from '@/features/auth/view-auth-onboarding';
109

1110
export default function LoggedLayout() {
1211
const router = useRouter();
@@ -19,21 +18,10 @@ export default function LoggedLayout() {
1918
}
2019
}, [router, session.data?.user, session.isPending]);
2120

22-
const themedStyle = useColorModeValue(
23-
{
24-
backgroundColor: 'white',
25-
color: theme.colors.neutral[950],
26-
sceneBackgroundColor: theme.colors.neutral[50],
27-
},
28-
{
29-
backgroundColor: theme.colors.neutral[950],
30-
color: 'white',
31-
sceneBackgroundColor: theme.colors.neutral[900],
32-
}
33-
);
21+
const themedStyle = useThemedStyle();
3422

3523
if (!session.data?.user?.name) {
36-
return <ViewOnboarding />;
24+
return <ViewAuthOnboarding />;
3725
}
3826

3927
return (

β€Žsrc/app/(public)/_layout.tsxβ€Ž

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
import { Stack, useRouter } from 'expo-router';
22
import { useEffect } from 'react';
33

4+
import { useThemedStyle } from '@/hooks/use-themed-style';
5+
46
import { authClient } from '@/features/auth/client';
57

68
export default function PublicLayout() {
79
const router = useRouter();
810
const session = authClient.useSession();
911

12+
const themedStyle = useThemedStyle();
13+
1014
useEffect(() => {
1115
if (!session.isPending && session.data?.user) {
1216
router.replace('/(logged)/(tabs)/home');
@@ -18,6 +22,10 @@ export default function PublicLayout() {
1822
initialRouteName="sign-in"
1923
screenOptions={{
2024
headerShown: false,
25+
headerTitleAlign: 'left',
26+
headerStyle: { backgroundColor: themedStyle.backgroundColor },
27+
headerTintColor: themedStyle.color,
28+
contentStyle: { backgroundColor: themedStyle.sceneBackgroundColor },
2129
}}
2230
>
2331
<Stack.Protected guard={!session.data?.user?.id}>

β€Žsrc/app/(public)/sign-in.tsxβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ViewSignIn } from '@/features/auth/view-sign-in';
22

3-
export default function () {
3+
export default function SignIn() {
44
return <ViewSignIn />;
55
}

β€Žsrc/app/_layout.tsxβ€Ž

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { BottomSheetModalProvider } from '@gorhom/bottom-sheet';
22
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
33
import { Slot } from 'expo-router';
44
import * as SplashScreen from 'expo-splash-screen';
5-
import { Box, FicusProvider } from 'react-native-ficus-ui';
5+
import { FicusProvider } from 'react-native-ficus-ui';
66
import { GestureHandlerRootView } from 'react-native-gesture-handler';
77
import {
88
initialWindowMetrics,
@@ -16,9 +16,10 @@ import theme from '@/lib/ficus-ui/theme';
1616
import { ThemeManager } from '@/components/theme-manager';
1717
import { Sonner } from '@/components/ui/sonner';
1818

19+
import { DevTools } from '@/features/devtools/devtools';
1920
import { SplashScreenManager } from '@/layout/splash-screen-manager';
2021

21-
const queryClient = new QueryClient();
22+
export const queryClient = new QueryClient();
2223

2324
// SplashScreen hide management in on src/app/index.tsx
2425
SplashScreen.preventAutoHideAsync();
@@ -27,19 +28,18 @@ export default function RootLayout() {
2728
return (
2829
<QueryClientProvider client={queryClient}>
2930
<FicusProvider theme={theme}>
30-
<Box flex={1} bg="white" _dark={{ bg: 'neutral.950' }}>
31-
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
32-
<GestureHandlerRootView>
33-
<BottomSheetModalProvider>
34-
<SplashScreenManager>
35-
<Slot />
36-
</SplashScreenManager>
37-
<Sonner />
38-
<ThemeManager />
39-
</BottomSheetModalProvider>
40-
</GestureHandlerRootView>
41-
</SafeAreaProvider>
42-
</Box>
31+
<ThemeManager />
32+
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
33+
<GestureHandlerRootView>
34+
<BottomSheetModalProvider>
35+
<SplashScreenManager>
36+
<Slot />
37+
</SplashScreenManager>
38+
<Sonner />
39+
{process.env.NODE_ENV === 'development' && <DevTools />}
40+
</BottomSheetModalProvider>
41+
</GestureHandlerRootView>
42+
</SafeAreaProvider>
4343
</FicusProvider>
4444
</QueryClientProvider>
4545
);

β€Žsrc/app/index.tsxβ€Ž

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,30 @@ import { useCallback } from 'react';
44

55
import { FullLoader } from '@/components/ui/full-loader';
66

7+
import { useOnboardingStore } from '@/features/app-onboarding/store';
8+
import { ViewOnboarding } from '@/features/app-onboarding/view-app-onboarding';
79
import { authClient } from '@/features/auth/client';
810

911
export default function Index() {
1012
const session = authClient.useSession();
13+
const isOnboarded = useOnboardingStore((state) => state.done);
1114

1215
// Manage first app redirection
1316
useFocusEffect(
1417
useCallback(() => {
15-
if (session.isPending) {
18+
if (!isOnboarded || session.isPending) {
1619
return;
1720
}
1821
router.replace(
1922
session.data?.user?.id ? '/(logged)/(tabs)/home' : '/(public)/sign-in'
2023
);
21-
}, [session.data, session.isPending])
24+
}, [session.data, session.isPending, isOnboarded])
2225
);
2326

27+
if (!isOnboarded) {
28+
return <ViewOnboarding />;
29+
}
30+
2431
if (Constants.expoConfig?.extra?.isStorybook) {
2532
return <Redirect href="/storybook" />;
2633
}

β€Žsrc/components/icons/icon.tsxβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const Icon = ({
66
size,
77
...props
88
}: StyleProps & {
9-
icon: LucideIcon;
9+
icon: LucideIcon | ReturnType<typeof ficus>;
1010
color?: string;
1111
size?: number;
1212
}) => {

0 commit comments

Comments
Β (0)