From cb88e5e1bfbb2fccb06ad1c41c087d9410c8d830 Mon Sep 17 00:00:00 2001 From: victorzarzar Date: Tue, 3 Mar 2026 10:42:36 -0300 Subject: [PATCH] Feat: Update routes and layout. --- README.md | 40 ++- bun.lock | 45 +-- ...zemo.sql => 0000_calm_captain_britain.sql} | 3 +- drizzle/meta/0000_snapshot.json | 10 +- drizzle/meta/_journal.json | 4 +- drizzle/migrations.js | 2 +- package.json | 1 + src/app/(auth)/_layout.tsx | 6 +- src/app/{(app) => (main)/(tabs)}/_layout.tsx | 2 +- src/app/(main)/(tabs)/about/_layout.tsx | 28 ++ .../(tabs)/about/index.tsx} | 0 src/app/(main)/(tabs)/home/_layout.tsx | 28 ++ src/app/(main)/(tabs)/home/index.tsx | 20 ++ src/app/(main)/(tabs)/settings/_layout.tsx | 28 ++ .../(tabs)/settings/index.tsx} | 0 src/app/(main)/_layout.tsx | 9 + src/app/{(app) => (main)}/index.tsx | 8 +- src/app/_layout.tsx | 2 +- src/shared/components/sign-in-form.tsx | 2 +- src/shared/components/sign-up-form.tsx | 4 +- src/shared/components/ui/dropdown-menu.tsx | 331 ++++++++++++++++++ src/shared/components/ui/header-avatar.tsx | 33 ++ src/shared/db/schema.ts | 1 + src/shared/services/userService.ts | 8 +- src/shared/types/auth.ts | 1 + 25 files changed, 561 insertions(+), 55 deletions(-) rename drizzle/{0000_wet_zemo.sql => 0000_calm_captain_britain.sql} (86%) rename src/app/{(app) => (main)/(tabs)}/_layout.tsx (95%) create mode 100644 src/app/(main)/(tabs)/about/_layout.tsx rename src/app/{(app)/about.tsx => (main)/(tabs)/about/index.tsx} (100%) create mode 100644 src/app/(main)/(tabs)/home/_layout.tsx create mode 100644 src/app/(main)/(tabs)/home/index.tsx create mode 100644 src/app/(main)/(tabs)/settings/_layout.tsx rename src/app/{(app)/settings.tsx => (main)/(tabs)/settings/index.tsx} (100%) create mode 100644 src/app/(main)/_layout.tsx rename src/app/{(app) => (main)}/index.tsx (53%) create mode 100644 src/shared/components/ui/dropdown-menu.tsx create mode 100644 src/shared/components/ui/header-avatar.tsx diff --git a/README.md b/README.md index 8e24ad4..0ce1149 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 @@ -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 @@ -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. +
+ +

+ SQL Drizze Studio +

+ +
+ ---

diff --git a/bun.lock b/bun.lock index 9e5ac8f..cca7f0c 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,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", @@ -449,6 +450,8 @@ "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-menu": "2.1.16", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw=="], + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw=="], "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], @@ -457,6 +460,8 @@ "@radix-ui/react-label": ["@radix-ui/react-label@2.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A=="], + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA=="], "@radix-ui/react-popper": ["@radix-ui/react-popper@1.2.8", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", "@radix-ui/react-use-size": "1.1.1", "@radix-ui/rect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw=="], @@ -465,7 +470,7 @@ "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.5", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ=="], - "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], @@ -531,6 +536,8 @@ "@rn-primitives/avatar": ["@rn-primitives/avatar@1.2.0", "", { "dependencies": { "@rn-primitives/hooks": "1.3.0", "@rn-primitives/slot": "1.2.0", "@rn-primitives/types": "1.2.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-ic029KaJRADdjmjPzpaSaZ9QrtgGF8DnAA7TcQ/gYqUfLXjkbfzsjARKv7NtEoJLjWAcjIAK6R8JkcbMfPtYig=="], + "@rn-primitives/dropdown-menu": ["@rn-primitives/dropdown-menu@1.2.0", "", { "dependencies": { "@radix-ui/react-dropdown-menu": "^2.1.15", "@rn-primitives/hooks": "1.3.0", "@rn-primitives/slot": "1.2.0", "@rn-primitives/types": "1.2.0", "@rn-primitives/utils": "1.2.0" }, "peerDependencies": { "@rn-primitives/portal": "*", "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-TJDDr8VQfw9CRZ7xZ6kBYLVMqL1xFVC5ZZ4sfRmWP6PCT0lNks4XqGuTFLeVVlNLPSmzt9GKC2DZqzDXui8/NQ=="], + "@rn-primitives/hooks": ["@rn-primitives/hooks@1.3.0", "", { "dependencies": { "@rn-primitives/types": "1.2.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-BR97reSu7uVDpyMeQdRJHT0w8KdS6jdYnOL6xQtqS2q3H6N7vXBlX4LFERqJZphD+aziJFIAJ3HJF1vtt6XlpQ=="], "@rn-primitives/label": ["@rn-primitives/label@1.2.0", "", { "dependencies": { "@radix-ui/react-label": "^2.1.7", "@rn-primitives/slot": "1.2.0", "@rn-primitives/types": "1.2.0" }, "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-eThBr6vn2jS81ZS4JNcg0+02TkEircH4bZmjF4IZUDl4XRpevwK95NyOkbfhGYmpVbAuisAVxDmvNOQ4OVjfug=="], @@ -547,6 +554,8 @@ "@rn-primitives/types": ["@rn-primitives/types@1.2.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-b+6zKgdKVqAfaFPSfhwlQL0dnPQXPpW890m3eguC0VDI1eOsoEvUfVb6lmgH4bum9MmI0xymq4tOUI/fsKLoCQ=="], + "@rn-primitives/utils": ["@rn-primitives/utils@1.2.0", "", { "peerDependencies": { "react": "*", "react-native": "*", "react-native-web": "*" }, "optionalPeers": ["react-native", "react-native-web"] }, "sha512-vLXV5NuxIHDeb4Bw57FzdUh89/g8gz6GERm8TsbJaSUPsDXfnC/ffeYiZJb0LxNteKE3Nr8na4Jy2n26tFil7w=="], + "@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="], "@sinonjs/commons": ["@sinonjs/commons@3.0.1", "", { "dependencies": { "type-detect": "4.0.8" } }, "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ=="], @@ -1557,31 +1566,19 @@ "@istanbuljs/load-nyc-config/js-yaml": ["js-yaml@3.14.2", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg=="], - "@radix-ui/react-arrow/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-collection/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-dialog/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-label/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], - "@radix-ui/react-focus-scope/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-popover/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], "@radix-ui/react-popover/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-popper/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-portal/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@radix-ui/react-roving-focus/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], - - "@radix-ui/react-tabs/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], "@react-native/community-cli-plugin/metro": ["metro@0.83.5", "", { "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/core": "^7.25.2", "@babel/generator": "^7.29.1", "@babel/parser": "^7.29.0", "@babel/template": "^7.28.6", "@babel/traverse": "^7.29.0", "@babel/types": "^7.29.0", "accepts": "^2.0.0", "chalk": "^4.0.0", "ci-info": "^2.0.0", "connect": "^3.6.5", "debug": "^4.4.0", "error-stack-parser": "^2.0.6", "flow-enums-runtime": "^0.0.6", "graceful-fs": "^4.2.4", "hermes-parser": "0.33.3", "image-size": "^1.0.2", "invariant": "^2.2.4", "jest-worker": "^29.7.0", "jsc-safe-url": "^0.2.2", "lodash.throttle": "^4.1.1", "metro-babel-transformer": "0.83.5", "metro-cache": "0.83.5", "metro-cache-key": "0.83.5", "metro-config": "0.83.5", "metro-core": "0.83.5", "metro-file-map": "0.83.5", "metro-resolver": "0.83.5", "metro-runtime": "0.83.5", "metro-source-map": "0.83.5", "metro-symbolicate": "0.83.5", "metro-transform-plugins": "0.83.5", "metro-transform-worker": "0.83.5", "mime-types": "^3.0.1", "nullthrows": "^1.1.1", "serialize-error": "^2.1.0", "source-map": "^0.5.6", "throat": "^5.0.0", "ws": "^7.5.10", "yargs": "^17.6.2" }, "bin": { "metro": "src/cli.js" } }, "sha512-BgsXevY1MBac/3ZYv/RfNFf/4iuW9X7f4H8ZNkiH+r667HD9sVujxcmu4jvEzGCAm4/WyKdZCuyhAcyhTHOucQ=="], @@ -1753,20 +1750,6 @@ "@istanbuljs/load-nyc-config/js-yaml/argparse": ["argparse@1.0.10", "", { "dependencies": { "sprintf-js": "~1.0.2" } }, "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg=="], - "@radix-ui/react-arrow/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-focus-scope/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-popper/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-portal/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-roving-focus/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - - "@radix-ui/react-tabs/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], - "@react-native/community-cli-plugin/metro/accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], "@react-native/community-cli-plugin/metro/ci-info": ["ci-info@2.0.0", "", {}, "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ=="], diff --git a/drizzle/0000_wet_zemo.sql b/drizzle/0000_calm_captain_britain.sql similarity index 86% rename from drizzle/0000_wet_zemo.sql rename to drizzle/0000_calm_captain_britain.sql index 29ef2d4..dd48898 100644 --- a/drizzle/0000_wet_zemo.sql +++ b/drizzle/0000_calm_captain_britain.sql @@ -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`); \ No newline at end of file +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`); diff --git a/drizzle/meta/0000_snapshot.json b/drizzle/meta/0000_snapshot.json index 7d7b78c..ec310d8 100644 --- a/drizzle/meta/0000_snapshot.json +++ b/drizzle/meta/0000_snapshot.json @@ -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": { @@ -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", diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 69f7322..574d4a5 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -5,8 +5,8 @@ { "idx": 0, "version": "6", - "when": 1772274776298, - "tag": "0000_wet_zemo", + "when": 1772545231738, + "tag": "0000_calm_captain_britain", "breakpoints": true } ] diff --git a/drizzle/migrations.js b/drizzle/migrations.js index b9853c0..ac28fda 100644 --- a/drizzle/migrations.js +++ b/drizzle/migrations.js @@ -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 { diff --git a/package.json b/package.json index ff9e788..3610ef3 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/app/(auth)/_layout.tsx b/src/app/(auth)/_layout.tsx index fe7d0c5..50750f0 100644 --- a/src/app/(auth)/_layout.tsx +++ b/src/app/(auth)/_layout.tsx @@ -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 ; + const { isAuthenticated } = useAuth(); + + if (isAuthenticated) return ; return ; } diff --git a/src/app/(app)/_layout.tsx b/src/app/(main)/(tabs)/_layout.tsx similarity index 95% rename from src/app/(app)/_layout.tsx rename to src/app/(main)/(tabs)/_layout.tsx index 5687a08..00b642f 100644 --- a/src/app/(app)/_layout.tsx +++ b/src/app/(main)/(tabs)/_layout.tsx @@ -6,7 +6,7 @@ export default function TabLayout() { return ( - + {t("tabs.home")} diff --git a/src/app/(main)/(tabs)/about/_layout.tsx b/src/app/(main)/(tabs)/about/_layout.tsx new file mode 100644 index 0000000..daa1127 --- /dev/null +++ b/src/app/(main)/(tabs)/about/_layout.tsx @@ -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 ( + + , + }} + /> + + ); +} diff --git a/src/app/(app)/about.tsx b/src/app/(main)/(tabs)/about/index.tsx similarity index 100% rename from src/app/(app)/about.tsx rename to src/app/(main)/(tabs)/about/index.tsx diff --git a/src/app/(main)/(tabs)/home/_layout.tsx b/src/app/(main)/(tabs)/home/_layout.tsx new file mode 100644 index 0000000..e310e3f --- /dev/null +++ b/src/app/(main)/(tabs)/home/_layout.tsx @@ -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 ( + + , + }} + /> + + ); +} diff --git a/src/app/(main)/(tabs)/home/index.tsx b/src/app/(main)/(tabs)/home/index.tsx new file mode 100644 index 0000000..f219d1a --- /dev/null +++ b/src/app/(main)/(tabs)/home/index.tsx @@ -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 ( + + + {t("tabs.home")} + + + ); +} diff --git a/src/app/(main)/(tabs)/settings/_layout.tsx b/src/app/(main)/(tabs)/settings/_layout.tsx new file mode 100644 index 0000000..693c3fd --- /dev/null +++ b/src/app/(main)/(tabs)/settings/_layout.tsx @@ -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 ( + + , + }} + /> + + ); +} diff --git a/src/app/(app)/settings.tsx b/src/app/(main)/(tabs)/settings/index.tsx similarity index 100% rename from src/app/(app)/settings.tsx rename to src/app/(main)/(tabs)/settings/index.tsx diff --git a/src/app/(main)/_layout.tsx b/src/app/(main)/_layout.tsx new file mode 100644 index 0000000..5edda9b --- /dev/null +++ b/src/app/(main)/_layout.tsx @@ -0,0 +1,9 @@ +import { Stack } from "expo-router"; + +export default function AppLayout() { + return ( + + + + ); +} diff --git a/src/app/(app)/index.tsx b/src/app/(main)/index.tsx similarity index 53% rename from src/app/(app)/index.tsx rename to src/app/(main)/index.tsx index 5b467ed..22e3087 100644 --- a/src/app/(app)/index.tsx +++ b/src/app/(main)/index.tsx @@ -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 ; + return ( + + ); } diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index ef46efc..80dc702 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -28,7 +28,7 @@ export default function RootLayout() { - + diff --git a/src/shared/components/sign-in-form.tsx b/src/shared/components/sign-in-form.tsx index 36db6d0..1c093d6 100644 --- a/src/shared/components/sign-in-form.tsx +++ b/src/shared/components/sign-in-form.tsx @@ -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", diff --git a/src/shared/components/sign-up-form.tsx b/src/shared/components/sign-up-form.tsx index ac381e9..7a07354 100644 --- a/src/shared/components/sign-up-form.tsx +++ b/src/shared/components/sign-up-form.tsx @@ -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", @@ -66,7 +66,7 @@ export function SignUpForm() { render={({ field: { onChange, value } }) => ( & { + children?: React.ReactNode; + iconClassName?: string; + inset?: boolean; + }) { + const { open } = DropdownMenuPrimitive.useSubContext(); + const icon = + Platform.OS === "web" ? ChevronRight : open ? ChevronUp : ChevronDown; + return ( + + + + + + ); +} + +function DropdownMenuSubContent({ + className, + ...props +}: DropdownMenuPrimitive.SubContentProps & + React.RefAttributes) { + return ( + + + + ); +} + +const FullWindowOverlay = + Platform.OS === "ios" ? RNFullWindowOverlay : React.Fragment; + +function DropdownMenuContent({ + className, + overlayClassName, + overlayStyle, + portalHost, + ...props +}: DropdownMenuPrimitive.ContentProps & + React.RefAttributes & { + overlayStyle?: StyleProp; + overlayClassName?: string; + portalHost?: string; + }) { + return ( + + + + + + + + + + + + ); +} + +function DropdownMenuItem({ + className, + inset, + variant, + ...props +}: DropdownMenuPrimitive.ItemProps & + React.RefAttributes & { + className?: string; + inset?: boolean; + variant?: "default" | "destructive"; + }) { + return ( + + + + ); +} + +function DropdownMenuCheckboxItem({ + className, + children, + ...props +}: DropdownMenuPrimitive.CheckboxItemProps & + React.RefAttributes & { + children?: React.ReactNode; + }) { + return ( + + + + + + + + + + ); +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: DropdownMenuPrimitive.RadioItemProps & + React.RefAttributes & { + children?: React.ReactNode; + }) { + return ( + + + + + + + + + + ); +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: DropdownMenuPrimitive.LabelProps & + React.RefAttributes & { + className?: string; + inset?: boolean; + }) { + return ( + + ); +} + +function DropdownMenuSeparator({ + className, + ...props +}: DropdownMenuPrimitive.SeparatorProps & + React.RefAttributes) { + return ( + + ); +} + +function DropdownMenuShortcut({ + className, + ...props +}: TextProps & React.RefAttributes) { + return ( + + ); +} + +export { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +}; diff --git a/src/shared/components/ui/header-avatar.tsx b/src/shared/components/ui/header-avatar.tsx new file mode 100644 index 0000000..f8fe503 --- /dev/null +++ b/src/shared/components/ui/header-avatar.tsx @@ -0,0 +1,33 @@ +import { useRouter } from "expo-router"; +import { Pressable } from "react-native"; +import { + Avatar, + AvatarFallback, + AvatarImage, +} from "@/shared/components/ui/avatar"; +import { Text } from "@/shared/components/ui/text"; +import { useAuth } from "@/shared/hooks/useAuth"; + +export function HeaderAvatar() { + const { user, signOut } = useAuth(); + const router = useRouter(); + + const initials = + user?.email?.split("@")[0]?.slice(0, 2)?.toUpperCase() ?? "??"; + + async function handleSignOut() { + await signOut(); + router.replace("/(auth)/signin"); + } + + return ( + + + + + {initials} + + + + ); +} diff --git a/src/shared/db/schema.ts b/src/shared/db/schema.ts index e3ed9e2..341e883 100644 --- a/src/shared/db/schema.ts +++ b/src/shared/db/schema.ts @@ -4,6 +4,7 @@ export const users = sqliteTable("users", { id: integer("id").primaryKey().notNull(), email: text("email").notNull().unique(), passwordHash: text("password_hash").notNull(), + avatar_url: text("avatar_url").notNull().default(""), createdAt: integer("created_at").notNull(), }); diff --git a/src/shared/services/userService.ts b/src/shared/services/userService.ts index 0595b97..faaad78 100644 --- a/src/shared/services/userService.ts +++ b/src/shared/services/userService.ts @@ -22,7 +22,7 @@ export async function getSessionUser(): Promise { .limit(1); if (!user) return null; - return { id: user.id, email: user.email }; + return { id: user.id, email: user.email, avatar_url: user.avatar_url }; } export async function signUpUser( @@ -44,7 +44,7 @@ export async function signUpUser( const [user] = await db .insert(users) - .values({ email, passwordHash, createdAt: now }) + .values({ email, passwordHash, createdAt: now, avatar_url: "" }) .returning(); await db.insert(sessions).values({ @@ -52,7 +52,7 @@ export async function signUpUser( createdAt: now, }); - return { id: user.id, email: user.email }; + return { id: user.id, email: user.email, avatar_url: user.avatar_url }; } export async function signInUser( @@ -78,7 +78,7 @@ export async function signInUser( createdAt: Date.now(), }); - return { id: user.id, email: user.email }; + return { id: user.id, email: user.email, avatar_url: user.avatar_url }; } export async function signOutUser(userId: number): Promise { diff --git a/src/shared/types/auth.ts b/src/shared/types/auth.ts index 768622e..25dd2b8 100644 --- a/src/shared/types/auth.ts +++ b/src/shared/types/auth.ts @@ -9,4 +9,5 @@ export type AuthContextType = { export type AuthUser = { id: number; email: string; + avatar_url: string; };