newcandies 0.1.29 → 0.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/templates/app-briefs/expo-router/flourish-auth/README.md +63 -110
- package/templates/app-briefs/expo-router/flourish-auth/app.json +1 -2
- package/templates/app-briefs/expo-router/flourish-auth/babel.config.js +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/eas.json +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/global.css +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/metro.config.js +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/package.json +1 -3
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(tabs)/_layout.tsx +16 -13
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(tabs)/explore.tsx +69 -11
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(tabs)/index.tsx +46 -51
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(tabs)/profile.tsx +99 -17
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(tabs)/search.tsx +15 -14
- package/templates/app-briefs/expo-router/flourish-auth/src/app/_layout.tsx +5 -20
- package/templates/app-briefs/expo-router/flourish-auth/src/components/ui/screen.tsx +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/src/components/ui/squircle.tsx +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/src/components/ui/text.tsx +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/src/lib/constants/index.ts +47 -0
- package/templates/app-briefs/expo-router/flourish-auth/src/lib/utils/index.ts +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/src/types/plant.d.ts +0 -1
- package/templates/app-briefs/expo-router/flourish-auth/tsconfig.json +0 -1
- package/templates/app-briefs/zustand/yummy/README.md +3 -4
- package/templates/app-briefs/zustand/yummy/assets/images/adaptive-icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/favicon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/splash-icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/_layout.tsx +0 -2
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/cart.tsx +12 -44
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/index.tsx +46 -52
- package/templates/app-briefs/zustand/yummy/src/lib/constants/index.ts +8 -9
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(auth)/_layout.tsx +0 -14
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(auth)/sign-in.tsx +0 -25
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(auth)/sign-up.tsx +0 -26
- package/templates/app-briefs/expo-router/flourish-auth/src/app/(auth)/welcome.tsx +0 -22
- package/templates/app-briefs/expo-router/flourish-auth/src/app/plant/[id].tsx +0 -48
- package/templates/app-briefs/expo-router/flourish-auth/src/lib/stores/README.md +0 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Flourish Auth - Expo Router
|
|
1
|
+
# Flourish Auth - Expo Router Authentication App Brief
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A beautiful plant care app built with React Native, Expo, and Expo Router for learning authentication flows.
|
|
4
4
|
|
|
5
5
|
## Getting Started
|
|
6
6
|
|
|
@@ -19,135 +19,88 @@ bun start
|
|
|
19
19
|
```
|
|
20
20
|
src/
|
|
21
21
|
├── app/
|
|
22
|
-
│ ├── _layout.tsx # Root layout
|
|
23
|
-
│ ├── (
|
|
24
|
-
│ │ ├── _layout.tsx #
|
|
25
|
-
│ │ ├──
|
|
26
|
-
│ │ ├──
|
|
27
|
-
│ │
|
|
28
|
-
│
|
|
29
|
-
|
|
30
|
-
│
|
|
31
|
-
│
|
|
32
|
-
│
|
|
33
|
-
│ │ └── profile.tsx # TODO: Add logout button
|
|
34
|
-
│ └── plant/
|
|
35
|
-
│ └── [id].tsx # TODO
|
|
36
|
-
├── components/ui/ # Complete
|
|
22
|
+
│ ├── _layout.tsx # Root layout with Stack
|
|
23
|
+
│ ├── (tabs)/ # Tab group
|
|
24
|
+
│ │ ├── _layout.tsx # Tab navigator config
|
|
25
|
+
│ │ ├── index.tsx # Home tab
|
|
26
|
+
│ │ ├── explore.tsx # Explore tab
|
|
27
|
+
│ │ ├── search.tsx # Search tab
|
|
28
|
+
│ │ └── profile.tsx # Profile tab
|
|
29
|
+
├── components/ui/
|
|
30
|
+
│ ├── screen.tsx
|
|
31
|
+
│ ├── squircle.tsx
|
|
32
|
+
│ └── text.tsx
|
|
37
33
|
├── lib/
|
|
38
|
-
│ ├── constants/
|
|
39
|
-
│
|
|
40
|
-
│ │ └── README.md # TODO: Create auth.ts store
|
|
41
|
-
│ └── utils/ # Complete
|
|
34
|
+
│ ├── constants/index.ts # Plant data
|
|
35
|
+
│ └── utils/index.ts # Date helpers
|
|
42
36
|
└── types/
|
|
37
|
+
└── plant.d.ts
|
|
43
38
|
```
|
|
44
39
|
|
|
45
|
-
## Your
|
|
40
|
+
## Your Task
|
|
46
41
|
|
|
47
|
-
|
|
42
|
+
This app brief teaches **Expo Router Authentication Flows**. The base app has tab navigation already set up. Your task is to add authentication.
|
|
48
43
|
|
|
49
|
-
|
|
50
|
-
import { create } from 'zustand';
|
|
44
|
+
### Screens to Build
|
|
51
45
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const useAuth = create<AuthState>((set) => ({
|
|
59
|
-
isLoggedIn: false,
|
|
60
|
-
login: () => set({ isLoggedIn: true }),
|
|
61
|
-
logout: () => set({ isLoggedIn: false }),
|
|
62
|
-
}));
|
|
63
|
-
|
|
64
|
-
export default useAuth;
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
### 2. Implement Protected Routes (`src/app/_layout.tsx`)
|
|
46
|
+
1. **`(auth)/welcome.tsx`** - Welcome/landing screen
|
|
47
|
+
- App branding and logo
|
|
48
|
+
- "Sign In" and "Sign Up" buttons
|
|
49
|
+
- Navigate to respective auth screens
|
|
68
50
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return (
|
|
76
|
-
<Stack screenOptions={{ headerShown: false }}>
|
|
77
|
-
{/* Show auth screens when NOT logged in */}
|
|
78
|
-
<Stack.Protected guard={!isLoggedIn}>
|
|
79
|
-
<Stack.Screen name="(auth)" />
|
|
80
|
-
</Stack.Protected>
|
|
81
|
-
|
|
82
|
-
{/* Show app screens when logged in */}
|
|
83
|
-
<Stack.Protected guard={isLoggedIn}>
|
|
84
|
-
<Stack.Screen name="(tabs)" />
|
|
85
|
-
<Stack.Screen name="plant/[id]" />
|
|
86
|
-
</Stack.Protected>
|
|
87
|
-
</Stack>
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
```
|
|
51
|
+
2. **`(auth)/sign-in.tsx`** - Sign in form
|
|
52
|
+
- Email and password inputs
|
|
53
|
+
- Sign in button
|
|
54
|
+
- Link to sign up page
|
|
55
|
+
- Back button to welcome
|
|
91
56
|
|
|
92
|
-
|
|
57
|
+
3. **`(auth)/sign-up.tsx`** - Sign up form
|
|
58
|
+
- Name, email, and password inputs
|
|
59
|
+
- Sign up button
|
|
60
|
+
- Link to sign in page
|
|
61
|
+
- Back button to welcome
|
|
93
62
|
|
|
94
|
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
- "Sign In" link → sign-in
|
|
63
|
+
4. **`(auth)/_layout.tsx`** - Auth group layout
|
|
64
|
+
- Stack navigator for auth screens
|
|
65
|
+
- Configure screen options
|
|
98
66
|
|
|
99
|
-
|
|
100
|
-
- Email & password inputs
|
|
101
|
-
- Sign In button → calls `login()`
|
|
102
|
-
- Link to sign-up
|
|
67
|
+
### Authentication Concepts
|
|
103
68
|
|
|
104
|
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
107
|
-
-
|
|
69
|
+
You'll need to implement:
|
|
70
|
+
- Protected routes (redirect unauthenticated users)
|
|
71
|
+
- Auth state management (using Zustand or Context)
|
|
72
|
+
- Conditional navigation based on auth status
|
|
73
|
+
- Route groups for organization: `(auth)` and `(tabs)`
|
|
108
74
|
|
|
109
|
-
###
|
|
75
|
+
### Navigation Flow
|
|
110
76
|
|
|
111
|
-
```tsx
|
|
112
|
-
import useAuth from "~/lib/stores/auth";
|
|
113
|
-
|
|
114
|
-
export default function Profile() {
|
|
115
|
-
const { logout } = useAuth();
|
|
116
|
-
|
|
117
|
-
return (
|
|
118
|
-
// ... profile content ...
|
|
119
|
-
<Pressable onPress={logout}>
|
|
120
|
-
<Text>Sign Out</Text>
|
|
121
|
-
</Pressable>
|
|
122
|
-
);
|
|
123
|
-
}
|
|
124
77
|
```
|
|
78
|
+
Unauthenticated:
|
|
79
|
+
welcome -> sign-in -> (tabs)
|
|
80
|
+
welcome -> sign-up -> (tabs)
|
|
125
81
|
|
|
126
|
-
|
|
82
|
+
Authenticated:
|
|
83
|
+
(tabs) directly
|
|
84
|
+
```
|
|
127
85
|
|
|
128
|
-
###
|
|
129
|
-
- `(auth)` - Screens for unauthenticated users
|
|
130
|
-
- `(tabs)` - Main app for authenticated users
|
|
86
|
+
### Protecting Routes
|
|
131
87
|
|
|
132
|
-
|
|
133
|
-
Conditionally shows screens based on a guard boolean:
|
|
88
|
+
In `_layout.tsx`, check auth state and redirect:
|
|
134
89
|
```tsx
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
90
|
+
// Redirect to welcome if not authenticated
|
|
91
|
+
if (!isAuthenticated && !isAuthRoute) {
|
|
92
|
+
return <Redirect href="/welcome" />;
|
|
93
|
+
}
|
|
138
94
|
```
|
|
139
95
|
|
|
140
|
-
|
|
141
|
-
1. User opens app → sees welcome (not logged in)
|
|
142
|
-
2. User signs in → `login()` sets `isLoggedIn: true`
|
|
143
|
-
3. Protected Routes automatically show `(tabs)`
|
|
144
|
-
4. User taps Sign Out → `logout()` → back to `(auth)`
|
|
96
|
+
## Design System
|
|
145
97
|
|
|
146
|
-
|
|
98
|
+
- 🌿 Green nature theme (`#234823` primary)
|
|
99
|
+
- ☀️ Golden accent (`#ffce47`)
|
|
100
|
+
- 🟢 Squircle cards with smooth corners
|
|
147
101
|
|
|
148
|
-
## Available
|
|
102
|
+
## Available Data
|
|
149
103
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
- Home screen fully implemented
|
|
104
|
+
- `customPlants` - Array of 5 plants with full details
|
|
105
|
+
- `getWeekDays(date)` - Helper for calendar display
|
|
106
|
+
- `Plant` interface - TypeScript type for plants
|
|
@@ -42,8 +42,7 @@
|
|
|
42
42
|
"sonner-native": "latest",
|
|
43
43
|
"tailwind-merge": "latest",
|
|
44
44
|
"tailwindcss": "^4.1.16",
|
|
45
|
-
"uniwind": "^1.0.0"
|
|
46
|
-
"zustand": "^5.0.9"
|
|
45
|
+
"uniwind": "^1.0.0"
|
|
47
46
|
},
|
|
48
47
|
"devDependencies": {
|
|
49
48
|
"@rozenite/metro": "^1.0.0-alpha.16",
|
|
@@ -52,4 +51,3 @@
|
|
|
52
51
|
"typescript": "~5.9.2"
|
|
53
52
|
}
|
|
54
53
|
}
|
|
55
|
-
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
import IonIcons from "@expo/vector-icons/Ionicons";
|
|
2
2
|
import { Tabs } from "expo-router";
|
|
3
|
+
import React from "react";
|
|
3
4
|
import { useCSSVariable } from "uniwind";
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
type TabBarIconType = {
|
|
6
7
|
name: React.ComponentProps<typeof IonIcons>["name"];
|
|
7
8
|
color: string;
|
|
8
|
-
}
|
|
9
|
-
return <IonIcons size={28} style={{ marginBottom: -4 }} {...props} />;
|
|
10
|
-
}
|
|
9
|
+
};
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const TabBarIcon = (props: TabBarIconType) => {
|
|
12
|
+
return <IonIcons size={28} {...props} />;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const TabsLayout = () => {
|
|
14
16
|
const primaryColor = useCSSVariable("--color-primary") as string;
|
|
15
|
-
const
|
|
17
|
+
const tabBarInActiveTintColor = useCSSVariable(
|
|
16
18
|
"--color-muted-foreground"
|
|
17
19
|
) as string;
|
|
18
20
|
|
|
21
|
+
const backgroundColor = useCSSVariable("--color-muted") as string;
|
|
22
|
+
|
|
19
23
|
return (
|
|
20
24
|
<Tabs
|
|
21
25
|
screenOptions={{
|
|
22
26
|
headerShown: false,
|
|
27
|
+
|
|
23
28
|
tabBarActiveTintColor: primaryColor,
|
|
24
|
-
tabBarInactiveTintColor:
|
|
29
|
+
tabBarInactiveTintColor: tabBarInActiveTintColor,
|
|
25
30
|
tabBarStyle: {
|
|
26
31
|
backgroundColor,
|
|
32
|
+
paddingTop: 16,
|
|
27
33
|
},
|
|
28
34
|
}}
|
|
29
35
|
>
|
|
@@ -31,7 +37,6 @@ export default function TabLayout() {
|
|
|
31
37
|
name="index"
|
|
32
38
|
options={{
|
|
33
39
|
title: "Home",
|
|
34
|
-
headerShown: false,
|
|
35
40
|
tabBarShowLabel: false,
|
|
36
41
|
tabBarIcon: ({ color }) => (
|
|
37
42
|
<TabBarIcon name="partly-sunny" color={color} />
|
|
@@ -42,7 +47,6 @@ export default function TabLayout() {
|
|
|
42
47
|
name="explore"
|
|
43
48
|
options={{
|
|
44
49
|
title: "Explore",
|
|
45
|
-
headerShown: false,
|
|
46
50
|
tabBarShowLabel: false,
|
|
47
51
|
tabBarIcon: ({ color }) => <TabBarIcon name="grid" color={color} />,
|
|
48
52
|
}}
|
|
@@ -51,7 +55,6 @@ export default function TabLayout() {
|
|
|
51
55
|
name="search"
|
|
52
56
|
options={{
|
|
53
57
|
title: "Search",
|
|
54
|
-
headerShown: false,
|
|
55
58
|
tabBarShowLabel: false,
|
|
56
59
|
tabBarIcon: ({ color }) => <TabBarIcon name="search" color={color} />,
|
|
57
60
|
}}
|
|
@@ -60,12 +63,12 @@ export default function TabLayout() {
|
|
|
60
63
|
name="profile"
|
|
61
64
|
options={{
|
|
62
65
|
title: "Profile",
|
|
63
|
-
headerShown: false,
|
|
64
66
|
tabBarShowLabel: false,
|
|
65
67
|
tabBarIcon: ({ color }) => <TabBarIcon name="person" color={color} />,
|
|
66
68
|
}}
|
|
67
69
|
/>
|
|
68
70
|
</Tabs>
|
|
69
71
|
);
|
|
70
|
-
}
|
|
72
|
+
};
|
|
71
73
|
|
|
74
|
+
export default TabsLayout;
|
|
@@ -1,24 +1,82 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Ionicons from "@expo/vector-icons/Ionicons";
|
|
2
|
+
import { FlatList, Image, Pressable, ScrollView, View } from "react-native";
|
|
3
|
+
import { twMerge } from "tailwind-merge";
|
|
2
4
|
import { Screen } from "~/components/ui/screen";
|
|
5
|
+
import { Squircle } from "~/components/ui/squircle";
|
|
3
6
|
import Text from "~/components/ui/text";
|
|
7
|
+
import { categories, customPlants } from "~/lib/constants";
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
type ExploreCardProps = {
|
|
10
|
+
item: Plant;
|
|
11
|
+
};
|
|
12
|
+
const ExploreCard = ({ item }: ExploreCardProps) => {
|
|
13
|
+
return (
|
|
14
|
+
<Pressable className="mx-5">
|
|
15
|
+
<Squircle
|
|
16
|
+
cornerSmoothing={1}
|
|
17
|
+
className="overflow-hidden rounded-3xl bg-primary"
|
|
18
|
+
>
|
|
19
|
+
<Image
|
|
20
|
+
source={{ uri: item.coverImg }}
|
|
21
|
+
className="w-full h-48"
|
|
22
|
+
resizeMode="cover"
|
|
23
|
+
/>
|
|
24
|
+
<View className="p-5">
|
|
25
|
+
<Text variant="subtitle-secondary">{item.locationType}</Text>
|
|
26
|
+
<Text variant="caption">{item.name}</Text>
|
|
27
|
+
</View>
|
|
28
|
+
</Squircle>
|
|
29
|
+
</Pressable>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
10
32
|
|
|
33
|
+
const ListHeader = () => {
|
|
11
34
|
return (
|
|
12
|
-
|
|
35
|
+
<>
|
|
13
36
|
<View className="px-5 pt-4 pb-4">
|
|
14
37
|
<Text variant="display" className="text-primary">
|
|
15
38
|
Explore
|
|
16
39
|
</Text>
|
|
17
|
-
<Text className="text-muted-foreground mt-2">
|
|
18
|
-
Build this screen to browse plants by category
|
|
19
|
-
</Text>
|
|
20
40
|
</View>
|
|
41
|
+
<ScrollView
|
|
42
|
+
horizontal
|
|
43
|
+
showsHorizontalScrollIndicator={false}
|
|
44
|
+
contentContainerClassName="px-5 gap-2 pb-4"
|
|
45
|
+
>
|
|
46
|
+
{categories.map((category) => (
|
|
47
|
+
<Pressable key={category.id}>
|
|
48
|
+
<Squircle
|
|
49
|
+
cornerSmoothing={1}
|
|
50
|
+
className={twMerge(
|
|
51
|
+
"flex-row bg-primary items-center gap-2 px-3 py-2 rounded-xl",
|
|
52
|
+
category.bgColor
|
|
53
|
+
)}
|
|
54
|
+
>
|
|
55
|
+
<Ionicons
|
|
56
|
+
name={category.icon as any}
|
|
57
|
+
size={16}
|
|
58
|
+
color={category.bgColor}
|
|
59
|
+
/>
|
|
60
|
+
<Text variant="caption-primary">{category.label}</Text>
|
|
61
|
+
</Squircle>
|
|
62
|
+
</Pressable>
|
|
63
|
+
))}
|
|
64
|
+
</ScrollView>
|
|
65
|
+
</>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const Explore = () => {
|
|
70
|
+
return (
|
|
71
|
+
<Screen>
|
|
72
|
+
<FlatList
|
|
73
|
+
data={customPlants}
|
|
74
|
+
renderItem={({ item }) => <ExploreCard item={item} />}
|
|
75
|
+
contentContainerClassName="pb-8 gap-5 pt-2"
|
|
76
|
+
ListHeaderComponent={ListHeader}
|
|
77
|
+
/>
|
|
21
78
|
</Screen>
|
|
22
79
|
);
|
|
23
|
-
}
|
|
80
|
+
};
|
|
24
81
|
|
|
82
|
+
export default Explore;
|
|
@@ -1,51 +1,71 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { format } from "date-fns";
|
|
2
|
+
import { router } from "expo-router";
|
|
3
|
+
import { FlatList, Image, Pressable, View } from "react-native";
|
|
2
4
|
import { Screen } from "~/components/ui/screen";
|
|
3
|
-
import Text from "~/components/ui/text";
|
|
4
5
|
import { Squircle } from "~/components/ui/squircle";
|
|
6
|
+
import Text from "~/components/ui/text";
|
|
5
7
|
import { customPlants } from "~/lib/constants";
|
|
6
|
-
import { useRouter } from "expo-router";
|
|
7
|
-
import { format } from "date-fns";
|
|
8
8
|
import { getWeekDays } from "~/lib/utils";
|
|
9
9
|
|
|
10
|
+
type PlantCardProps = {
|
|
11
|
+
item: Plant;
|
|
12
|
+
onPress?: () => void;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const PlantCard = ({ item, onPress }: PlantCardProps) => {
|
|
16
|
+
return (
|
|
17
|
+
<Pressable onPress={() => router.push(`/`)} className="flex-1">
|
|
18
|
+
<Squircle
|
|
19
|
+
className="bg-primary overflow-hidden rounded-3xl p-2"
|
|
20
|
+
cornerSmoothing={1}
|
|
21
|
+
>
|
|
22
|
+
<Squircle className="overflow-hidden rounded-2xl w-full aspect-square">
|
|
23
|
+
<Image
|
|
24
|
+
source={{ uri: item.coverImg }}
|
|
25
|
+
className="w-full h-full aspect-square"
|
|
26
|
+
/>
|
|
27
|
+
</Squircle>
|
|
28
|
+
<View className="px-1 py-2">
|
|
29
|
+
<Text variant="body-secondary">{item.name}</Text>
|
|
30
|
+
</View>
|
|
31
|
+
</Squircle>
|
|
32
|
+
</Pressable>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
35
|
+
|
|
10
36
|
const now = new Date();
|
|
11
37
|
const weekDays = getWeekDays(now);
|
|
12
38
|
|
|
13
39
|
const HomeHeader = () => {
|
|
14
40
|
return (
|
|
15
|
-
<View className="px-5
|
|
41
|
+
<View className="px-5 py-4">
|
|
16
42
|
<View className="flex-row justify-between items-start mb-5">
|
|
17
43
|
<View className="flex-row items-center gap-2">
|
|
18
|
-
<Text variant="display"
|
|
19
|
-
<View className="w-
|
|
44
|
+
<Text variant="display">{format(now, "EEE")}</Text>
|
|
45
|
+
<View className="w-4 h-4 rounded-full bg-accent"></View>
|
|
20
46
|
</View>
|
|
21
|
-
|
|
22
47
|
<View className="items-end">
|
|
23
48
|
<Text variant="subtitle">{format(now, "MMMM d")}</Text>
|
|
24
|
-
<Text
|
|
49
|
+
<Text>{format(now, "yyyy")}</Text>
|
|
25
50
|
</View>
|
|
26
51
|
</View>
|
|
27
|
-
|
|
28
52
|
<View className="flex-row justify-between">
|
|
29
53
|
{weekDays.map((day, index) => (
|
|
30
54
|
<View key={index} className="items-center">
|
|
31
55
|
{day.isToday ? (
|
|
32
56
|
<Squircle
|
|
33
|
-
className="bg-primary
|
|
57
|
+
className="bg-primary p-2 items-center rounded-xl"
|
|
34
58
|
cornerSmoothing={1}
|
|
35
59
|
>
|
|
36
|
-
<Text
|
|
37
|
-
|
|
38
|
-
</Text>
|
|
39
|
-
<Text className="text-xs font-medium text-secondary mt-1">
|
|
40
|
-
{day.day}
|
|
41
|
-
</Text>
|
|
60
|
+
<Text variant="subtitle-secondary">{day.date}</Text>
|
|
61
|
+
<Text variant="caption">{day.day}</Text>
|
|
42
62
|
</Squircle>
|
|
43
63
|
) : (
|
|
44
|
-
<View className="
|
|
45
|
-
<Text className="text-
|
|
64
|
+
<View className="p-2 items-center">
|
|
65
|
+
<Text variant="subtitle" className="text-primary/40">
|
|
46
66
|
{day.date}
|
|
47
67
|
</Text>
|
|
48
|
-
<Text className="text-
|
|
68
|
+
<Text variant="caption" className="text-primary/30">
|
|
49
69
|
{day.day}
|
|
50
70
|
</Text>
|
|
51
71
|
</View>
|
|
@@ -57,46 +77,21 @@ const HomeHeader = () => {
|
|
|
57
77
|
);
|
|
58
78
|
};
|
|
59
79
|
|
|
60
|
-
const
|
|
61
|
-
<Pressable onPress={onPress} className="flex-1">
|
|
62
|
-
<Squircle className="bg-primary overflow-hidden rounded-3xl p-2" cornerSmoothing={1}>
|
|
63
|
-
<Squircle className="overflow-hidden rounded-2xl w-full aspect-square" cornerSmoothing={1}>
|
|
64
|
-
<Image source={{ uri: item.coverImg }} className="w-full h-full" resizeMode="cover" />
|
|
65
|
-
</Squircle>
|
|
66
|
-
<View className="px-1 py-2">
|
|
67
|
-
<Text variant="body-secondary" className="text-primary-foreground">{item.name}</Text>
|
|
68
|
-
</View>
|
|
69
|
-
</Squircle>
|
|
70
|
-
</Pressable>
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
const ListHeader = () => (
|
|
74
|
-
<>
|
|
75
|
-
<HomeHeader />
|
|
76
|
-
<View className="px-5 pb-4">
|
|
77
|
-
<Text variant="subtitle" className="text-primary">Our Plants</Text>
|
|
78
|
-
</View>
|
|
79
|
-
</>
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
export default function Index() {
|
|
83
|
-
const router = useRouter();
|
|
84
|
-
|
|
80
|
+
const Home = () => {
|
|
85
81
|
return (
|
|
86
82
|
<Screen>
|
|
87
83
|
<FlatList
|
|
88
84
|
data={customPlants}
|
|
85
|
+
renderItem={({ item }) => <PlantCard item={item} />}
|
|
89
86
|
numColumns={2}
|
|
87
|
+
ListHeaderComponent={HomeHeader}
|
|
90
88
|
keyExtractor={(item) => item.id}
|
|
91
|
-
renderItem={({ item }) => (
|
|
92
|
-
<PlantCard item={item} onPress={() => router.push(`/plant/${item.id}`)} />
|
|
93
|
-
)}
|
|
94
|
-
ListHeaderComponent={ListHeader}
|
|
95
|
-
columnWrapperClassName="gap-4 px-5"
|
|
96
89
|
contentContainerClassName="gap-4 pb-8"
|
|
90
|
+
columnWrapperClassName="gap-4 px-5"
|
|
97
91
|
showsVerticalScrollIndicator={false}
|
|
98
92
|
/>
|
|
99
93
|
</Screen>
|
|
100
94
|
);
|
|
101
|
-
}
|
|
95
|
+
};
|
|
102
96
|
|
|
97
|
+
export default Home;
|