Skip to content
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
40 changes: 35 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,34 @@ Before starting, ensure you have:

```
react-native-crud/
├── .expo/ # Expo cache and config
├── .github/ # GitHub Actions & workflows
├── assets/ # Images and fonts
├── drizzle/ # Generated SQL migration files
├── i18n/ # Internationalization config
├── node_modules/ # Dependencies
├── src/
│ ├── app/ # Expo Router routes
│ │ ├── (auth)/ # Public auth routes
│ │ │ ├── +html.tsx
│ │ │ ├── +not-found.tsx
│ │ │ └── _layout.tsx
│ │ ├── (auth)/ # Public auth routes (unauthenticated)
│ │ │ ├── _layout.tsx # Auth stack layout
│ │ │ ├── forgot-password.tsx # Forgot password screen
│ │ │ ├── reset-password.tsx # Reset password screen
│ │ │ ├── signin.tsx # Sign in screen
│ │ │ └── signup.tsx # Sign up screen
│ │ ├── (main)/ # Protected main routes (authenticated)
│ │ │ └── (tabs)/ # Bottom tab navigator
│ │ │ ├── about/ # About tab
│ │ │ │ ├── _layout.tsx
│ │ │ │ └── index.tsx
│ │ │ ├── home/ # Home tab
│ │ │ │ ├── _layout.tsx
│ │ │ │ └── index.tsx
│ │ │ └── settings/ # Settings tab
│ │ │ ├── _layout.tsx
│ │ │ └── index.tsx
│ │ ├── +html.tsx # Custom HTML shell (web)
│ │ ├── +not-found.tsx # 404 screen
│ │ └── _layout.tsx # Root layout
│ └── shared/
│ ├── auth/ # Auth context & logic
│ │ ├── context.ts
Expand All @@ -164,6 +186,7 @@ react-native-crud/
│ │ │ └── index.tsx
│ │ └── ui/ # Reusable UI components
│ │ ├── forgot-password-form.tsx
│ │ ├── header-avatar.tsx
│ │ ├── reset-password-form.tsx
│ │ ├── sign-in-form.tsx
│ │ ├── sign-up-form.tsx
Expand Down Expand Up @@ -192,7 +215,6 @@ react-native-crud/
│ ├── icon.ts
│ └── locale.ts
├── tests/ # Test files
├── assets/ # Images and fonts
├── global.css # Global styles
├── app.json # Expo configuration
├── drizzle.config.ts # Drizzle Kit configuration
Expand Down Expand Up @@ -226,6 +248,14 @@ With the development server running (`bun run dev`), press `Shift + M` in the te

> **Note:** This plugin is available during native development only (iOS/Android). It does not work on Web.

</br>

<p align="center">
<img src="https://github.com/user-attachments/assets/05dce4c2-72cd-46c9-afb9-d04146848ea2" width="1000" height="600" alt="SQL Drizze Studio">
</p>

</br>

---

<h2 id="adding-components">
Expand Down
45 changes: 14 additions & 31 deletions bun.lock

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ CREATE TABLE `users` (
`id` integer PRIMARY KEY NOT NULL,
`email` text NOT NULL,
`password_hash` text NOT NULL,
`avatar_url` text DEFAULT '' NOT NULL,
`created_at` integer NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);
CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);
10 changes: 9 additions & 1 deletion drizzle/meta/0000_snapshot.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"version": "6",
"dialect": "sqlite",
"id": "a80d7cec-989f-4bd1-97a4-b7d30ff894c8",
"id": "a6d42745-9c63-42b7-aac0-76d74e9fff7a",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"items": {
Expand Down Expand Up @@ -135,6 +135,14 @@
"notNull": true,
"autoincrement": false
},
"avatar_url": {
"name": "avatar_url",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": "''"
},
"created_at": {
"name": "created_at",
"type": "integer",
Expand Down
4 changes: 2 additions & 2 deletions drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
{
"idx": 0,
"version": "6",
"when": 1772274776298,
"tag": "0000_wet_zemo",
"when": 1772545231738,
"tag": "0000_calm_captain_britain",
"breakpoints": true
}
]
Expand Down
2 changes: 1 addition & 1 deletion drizzle/migrations.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// This file is required for Expo/React Native SQLite migrations - https://orm.drizzle.team/quick-sqlite/expo

import m0000 from "./0000_wet_zemo.sql";
import m0000 from "./0000_calm_captain_britain.sql";
import journal from "./meta/_journal.json";

export default {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@react-native-async-storage/async-storage": "2.2.0",
"@react-navigation/native": "^7.1.28",
"@rn-primitives/avatar": "^1.2.0",
"@rn-primitives/dropdown-menu": "^1.2.0",
"@rn-primitives/label": "^1.2.0",
"@rn-primitives/popover": "^1.2.0",
"@rn-primitives/portal": "~1.3.0",
Expand Down
6 changes: 3 additions & 3 deletions src/app/(auth)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { Redirect, Stack } from "expo-router";
import { useAuth } from "@/shared/hooks/useAuth";

export default function AuthLayout() {
const { isAuthenticated, user } = useAuth();
if (isAuthenticated) return null;
if (user) return <Redirect href="/(app)" />;
const { isAuthenticated } = useAuth();

if (isAuthenticated) return <Redirect href="/(main)/(tabs)/home" />;

return <Stack screenOptions={{ headerShown: true }} />;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function TabLayout() {

return (
<NativeTabs>
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger name="home">
<NativeTabs.Trigger.Label>{t("tabs.home")}</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
</NativeTabs.Trigger>
Expand Down
28 changes: 28 additions & 0 deletions src/app/(main)/(tabs)/about/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Stack } from "expo-router";
import { useTranslation } from "react-i18next";
import { Platform } from "react-native";
import { HeaderAvatar } from "@/shared/components/ui/header-avatar";

export default function AboutLayout() {
const { t } = useTranslation();

return (
<Stack
screenOptions={{
headerShown: true,
animation:
Platform.OS === "android" ? "fade_from_bottom" : "simple_push",
animationDuration: Platform.OS === "android" ? undefined : 200,
contentStyle: { backgroundColor: "#141414" },
}}
>
<Stack.Screen
name="index"
options={{
title: t("tabs.about"),
headerRight: () => <HeaderAvatar />,
}}
/>
</Stack>
);
}
File renamed without changes.
28 changes: 28 additions & 0 deletions src/app/(main)/(tabs)/home/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Stack } from "expo-router";
import { useTranslation } from "react-i18next";
import { Platform } from "react-native";
import { HeaderAvatar } from "@/shared/components/ui/header-avatar";

export default function HomeLayout() {
const { t } = useTranslation();

return (
<Stack
screenOptions={{
headerShown: true,
animation:
Platform.OS === "android" ? "fade_from_bottom" : "simple_push",
animationDuration: Platform.OS === "android" ? undefined : 200,
contentStyle: { backgroundColor: "#141414" },
}}
>
<Stack.Screen
name="index"
options={{
title: t("tabs.home"),
headerRight: () => <HeaderAvatar />,
}}
/>
</Stack>
);
}
20 changes: 20 additions & 0 deletions src/app/(main)/(tabs)/home/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useTheme } from "@react-navigation/native";
import { useTranslation } from "react-i18next";
import { View } from "react-native";
import { Text } from "@/shared/components/ui/text";

export default function Home() {
const { colors } = useTheme();
const { t } = useTranslation();

return (
<View
style={{ flex: 1, backgroundColor: colors.background }}
className="px-6 pt-16"
>
<Text className="text-3xl font-semibold tracking-tight text-foreground">
{t("tabs.home")}
</Text>
</View>
);
}
28 changes: 28 additions & 0 deletions src/app/(main)/(tabs)/settings/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Stack } from "expo-router";
import { useTranslation } from "react-i18next";
import { Platform } from "react-native";
import { HeaderAvatar } from "@/shared/components/ui/header-avatar";

export default function SettingsLayout() {
const { t } = useTranslation();

return (
<Stack
screenOptions={{
headerShown: true,
animation:
Platform.OS === "android" ? "fade_from_bottom" : "simple_push",
animationDuration: Platform.OS === "android" ? undefined : 200,
contentStyle: { backgroundColor: "#141414" },
}}
>
<Stack.Screen
name="index"
options={{
title: t("tabs.settings"),
headerRight: () => <HeaderAvatar />,
}}
/>
</Stack>
);
}
File renamed without changes.
9 changes: 9 additions & 0 deletions src/app/(main)/_layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Stack } from "expo-router";

export default function AppLayout() {
return (
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(tabs)" />
</Stack>
);
}
8 changes: 6 additions & 2 deletions src/app/(app)/index.tsx → src/app/(main)/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ import { Redirect } from "expo-router";
import { useAuth } from "@/shared/hooks/useAuth";

export default function Index() {
const { user, isAuthenticated } = useAuth();
const { isAuthenticated } = useAuth();

if (isAuthenticated) return null;

return <Redirect href={user ? "/(app)" : "/(auth)/signin"} />;
return (
<Redirect
href={isAuthenticated ? "/(main)/(tabs)/home" : "/(auth)/signin"}
/>
);
}
2 changes: 1 addition & 1 deletion src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export default function RootLayout() {
<AuthProvider>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen name="(auth)" />
<Stack.Screen name="(app)" />
<Stack.Screen name="(main)" />
</Stack>
</AuthProvider>
</DbProvider>
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/sign-in-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function SignInForm() {
async function onSubmit({ email, password }: SignInSchema) {
try {
await signIn(email, password);
router.replace("/(app)");
router.replace("/(main)/(tabs)/home");
} catch (err) {
setError("root", {
message: err instanceof Error ? err.message : "Something went wrong",
Expand Down
4 changes: 2 additions & 2 deletions src/shared/components/sign-up-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function SignUpForm() {
async function onSubmit({ email, password }: SignUpSchema) {
try {
await signUp(email, password);
router.replace("/(app)");
router.replace("/(auth)/signin");
} catch (err) {
setError("root", {
message: err instanceof Error ? err.message : "Something went wrong",
Expand Down Expand Up @@ -66,7 +66,7 @@ export function SignUpForm() {
render={({ field: { onChange, value } }) => (
<Input
id="email"
placeholder="m@example.com"
placeholder="email@gmail.com"
keyboardType="email-address"
autoComplete="email"
autoCapitalize="none"
Expand Down
Loading
Loading