diff --git a/App.tsx b/App.tsx
index 14742bb..86623ba 100644
--- a/App.tsx
+++ b/App.tsx
@@ -2,16 +2,33 @@ import React from 'react';
import { StatusBar } from 'expo-status-bar';
import { NavigationContainer } from '@react-navigation/native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
-import { StyleSheet } from 'react-native';
+import { StyleSheet, View, ActivityIndicator } from 'react-native';
import TabNavigator from './src/navigation/TabNavigator';
+import AuthNavigator from './src/navigation/AuthNavigator';
+import { AuthProvider, useAuth } from './src/context/AuthContext';
+import { colors } from './src/theme/colors';
+
+function AppContent() {
+ const { user } = useAuth();
+
+ return (
+
+
+ {user ? (
+
+ ) : (
+
+ )}
+
+ );
+}
export default function App() {
return (
-
-
-
-
+
+
+
);
}
@@ -19,5 +36,13 @@ export default function App() {
const styles = StyleSheet.create({
container: {
flex: 1,
+ backgroundColor: '#000',
+ },
+ loadingContainer: {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: colors.sentinel.background,
},
});
+
diff --git a/package-lock.json b/package-lock.json
index 8dd5bcb..ab285f0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@expo/vector-icons": "~14.0.4",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
+ "@react-navigation/native-stack": "^6.11.0",
"expo": "~52.0.0",
"expo-asset": "~11.0.5",
"expo-constants": "~17.0.8",
@@ -76,7 +77,6 @@
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz",
"integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.28.6",
"@babel/generator": "^7.28.6",
@@ -480,6 +480,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz",
"integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/traverse": "^7.28.5"
@@ -496,6 +497,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz",
"integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -511,6 +513,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz",
"integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -526,6 +529,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz",
"integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
@@ -543,6 +547,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz",
"integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6",
"@babel/traverse": "^7.28.6"
@@ -643,6 +648,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
"integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=6.9.0"
},
@@ -763,6 +769,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz",
"integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -949,6 +956,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
"integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.18.6",
"@babel/helper-plugin-utils": "^7.18.6"
@@ -1014,6 +1022,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz",
"integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1060,6 +1069,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz",
"integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-class-features-plugin": "^7.28.6",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1128,6 +1138,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz",
"integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1144,6 +1155,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz",
"integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1159,6 +1171,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.28.6.tgz",
"integrity": "sha512-5suVoXjC14lUN6ZL9OLKIHCNVWCrqGqlmEp/ixdXjvgnEl/kauLvvMO/Xw9NyMc95Joj1AeLVPVMvibBgSoFlA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1175,6 +1188,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz",
"integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1190,6 +1204,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz",
"integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6",
"@babel/plugin-transform-destructuring": "^7.28.5"
@@ -1206,6 +1221,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz",
"integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -1285,6 +1301,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz",
"integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.28.6"
},
@@ -1330,6 +1347,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz",
"integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1345,6 +1363,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz",
"integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1377,6 +1396,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.28.5.tgz",
"integrity": "sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.28.3",
"@babel/helper-plugin-utils": "^7.27.1",
@@ -1395,6 +1415,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz",
"integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-module-transforms": "^7.27.1",
"@babel/helper-plugin-utils": "^7.27.1"
@@ -1427,6 +1448,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz",
"integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1491,6 +1513,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz",
"integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1",
"@babel/helper-replace-supers": "^7.27.1"
@@ -1586,6 +1609,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz",
"integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1711,6 +1735,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz",
"integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1727,6 +1752,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz",
"integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1823,6 +1849,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz",
"integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1857,6 +1884,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz",
"integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.27.1"
},
@@ -1872,6 +1900,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz",
"integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -1904,6 +1933,7 @@
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz",
"integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-create-regexp-features-plugin": "^7.28.5",
"@babel/helper-plugin-utils": "^7.28.6"
@@ -2022,6 +2052,7 @@
"resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
"integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/types": "^7.4.4",
@@ -2639,7 +2670,6 @@
"resolved": "https://registry.npmjs.org/@expo/metro-runtime/-/metro-runtime-4.0.1.tgz",
"integrity": "sha512-CRpbLvdJ1T42S+lrYa1iZp1KfDeBp4oeZOK3hdpiS5n0vR0nhD6sC1gGF0sTboCTp64tLteikz5Y3j53dvgOIw==",
"license": "MIT",
- "peer": true,
"peerDependencies": {
"react-native": "*"
}
@@ -3672,7 +3702,6 @@
"resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-6.1.18.tgz",
"integrity": "sha512-mIT9MiL/vMm4eirLcmw2h6h/Nm5FICtnYSdohq4vTLA2FF/6PNhByM7s8ffqoVfE5L0uAa6Xda1B7oddolUiGg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@react-navigation/core": "^6.4.17",
"escape-string-regexp": "^4.0.0",
@@ -3684,6 +3713,23 @@
"react-native": "*"
}
},
+ "node_modules/@react-navigation/native-stack": {
+ "version": "6.11.0",
+ "resolved": "https://registry.npmmirror.com/@react-navigation/native-stack/-/native-stack-6.11.0.tgz",
+ "integrity": "sha512-U5EcUB9Q2NQspCFwYGGNJm0h6wBCOv7T30QjndmvlawLkNt7S7KWbpWyxS9XBHSIKF57RgWjfxuJNTgTstpXxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/elements": "^1.3.31",
+ "warn-once": "^0.1.0"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^6.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 3.0.0",
+ "react-native-screens": ">= 3.0.0"
+ }
+ },
"node_modules/@react-navigation/routers": {
"version": "6.1.9",
"resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-6.1.9.tgz",
@@ -3837,7 +3883,6 @@
"integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"devOptional": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@types/prop-types": "*",
"csstype": "^3.2.2"
@@ -4440,7 +4485,6 @@
}
],
"license": "MIT",
- "peer": true,
"dependencies": {
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
@@ -5456,6 +5500,7 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"license": "BSD-2-Clause",
+ "peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -5580,7 +5625,6 @@
"resolved": "https://registry.npmjs.org/expo/-/expo-52.0.48.tgz",
"integrity": "sha512-/HR/vuo57KGEWlvF3GWaquwEAjXuA5hrOCsaLcZ3pMSA8mQ27qKd1jva4GWzpxXYedlzs/7LLP1XpZo6hXTsog==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/runtime": "^7.20.0",
"@expo/cli": "0.22.27",
@@ -9053,7 +9097,6 @@
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
"license": "MIT",
- "peer": true,
"dependencies": {
"loose-envify": "^1.1.0"
},
@@ -9137,7 +9180,6 @@
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.76.9.tgz",
"integrity": "sha512-+LRwecWmTDco7OweGsrECIqJu0iyrREd6CTCgC/uLLYipiHvk+MH9nd6drFtCw/6Blz6eoKTcH9YTTJusNtrWg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native/assets-registry": "0.76.9",
@@ -9239,7 +9281,6 @@
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.12.0.tgz",
"integrity": "sha512-ukk5PxcF4p3yu6qMZcmeiZgowhb5AsKRnil54YFUUAXVIS7PJcMHGGC+q44fCiBg44/1AJk5njGMez1m9H0BVQ==",
"license": "MIT",
- "peer": true,
"peerDependencies": {
"react": "*",
"react-native": "*"
@@ -9250,7 +9291,6 @@
"resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.4.0.tgz",
"integrity": "sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg==",
"license": "MIT",
- "peer": true,
"dependencies": {
"react-freeze": "^1.0.0",
"warn-once": "^0.1.0"
diff --git a/package.json b/package.json
index c55d677..a3691be 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
"@expo/vector-icons": "~14.0.4",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
+ "@react-navigation/native-stack": "^6.11.0",
"expo": "~52.0.0",
"expo-asset": "~11.0.5",
"expo-constants": "~17.0.8",
diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx
new file mode 100644
index 0000000..891de9c
--- /dev/null
+++ b/src/context/AuthContext.tsx
@@ -0,0 +1,66 @@
+import React, { createContext, useContext, useState, ReactNode } from 'react';
+import { User, LoginRequest, RegisterRequest } from '../types';
+import { authService } from '../services/auth.service';
+import { Alert } from 'react-native';
+
+interface AuthContextType {
+ user: User | null;
+ token: string | null;
+ isLoading: boolean;
+ signIn: (credentials: LoginRequest) => Promise;
+ signUp: (data: RegisterRequest) => Promise;
+ signOut: () => void;
+}
+
+const AuthContext = createContext(undefined);
+
+export function AuthProvider({ children }: { children: ReactNode }) {
+ const [user, setUser] = useState(null);
+ const [token, setToken] = useState(null);
+ const [isLoading, setIsLoading] = useState(false);
+
+ const signIn = async (credentials: LoginRequest) => {
+ setIsLoading(true);
+ try {
+ const response = await authService.login(credentials);
+ setToken(response.access_token);
+ setUser(response.user);
+ } catch (error) {
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const signUp = async (data: RegisterRequest) => {
+ setIsLoading(true);
+ try {
+ await authService.register(data);
+ // After successful registration, sign in automatically
+ await signIn({ username: data.username, password: data.password });
+ } catch (error) {
+ throw error;
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const signOut = () => {
+ setUser(null);
+ setToken(null);
+ };
+
+ return (
+
+ {children}
+
+ );
+}
+
+export function useAuth() {
+ const context = useContext(AuthContext);
+ if (context === undefined) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+ return context;
+}
diff --git a/src/navigation/AuthNavigator.tsx b/src/navigation/AuthNavigator.tsx
new file mode 100644
index 0000000..278c85e
--- /dev/null
+++ b/src/navigation/AuthNavigator.tsx
@@ -0,0 +1,20 @@
+import React from 'react';
+import { createNativeStackNavigator } from '@react-navigation/native-stack';
+import LoginScreen from '../screens/LoginScreen';
+import RegisterScreen from '../screens/RegisterScreen';
+
+const Stack = createNativeStackNavigator();
+
+export default function AuthNavigator() {
+ return (
+
+
+
+
+ );
+}
diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx
new file mode 100644
index 0000000..7c5e05f
--- /dev/null
+++ b/src/screens/LoginScreen.tsx
@@ -0,0 +1,256 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TextInput,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+ Platform,
+ SafeAreaView,
+ Alert,
+ ActivityIndicator,
+} from 'react-native';
+import { LinearGradient } from 'expo-linear-gradient';
+import { Feather, MaterialCommunityIcons } from '@expo/vector-icons';
+import { colors, spacing, borderRadius, typography, shadows } from '../theme/colors';
+import { useAuth } from '../context/AuthContext';
+
+export default function LoginScreen({ navigation }: any) {
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [error, setError] = useState(null);
+ const { signIn, isLoading } = useAuth();
+
+ const handleLogin = async () => {
+ setError(null);
+ if (!email || !password) {
+ setError('Please enter both username and password.');
+ return;
+ }
+
+ try {
+ await signIn({ username: email, password });
+ } catch (err: any) {
+ setError('Login failed. Please check your credentials.');
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+
+ Welcome Aboard!
+ Sign in to access your sanctuaryy
+ {error && (
+
+
+ {error}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Forgot Password?
+
+
+
+ {isLoading ? (
+
+ ) : (
+ Login
+ )}
+
+
+
+
+ OR
+
+
+
+ navigation.navigate('Register')}
+ >
+
+ Don't have an account? Join the Fleet
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ gradient: {
+ flex: 1,
+ },
+ safeArea: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ paddingHorizontal: spacing.lg,
+ justifyContent: 'center',
+ },
+ header: {
+ alignItems: 'center',
+ marginBottom: spacing.xxl,
+ },
+ logoContainer: {
+ width: 80,
+ height: 80,
+ borderRadius: borderRadius.full,
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: spacing.lg,
+ borderWidth: 1,
+ borderColor: 'rgba(184, 224, 229, 0.2)',
+ },
+ title: {
+ fontSize: typography.fontSize.xxl,
+ fontWeight: 'bold',
+ color: colors.sentinel.text,
+ marginBottom: spacing.xs,
+ letterSpacing: 0.5,
+ },
+ subtitle: {
+ fontSize: typography.fontSize.base,
+ color: colors.sentinel.textSecondary,
+ marginBottom: spacing.md,
+ },
+ errorContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(255, 68, 68, 0.1)',
+ padding: spacing.sm,
+ borderRadius: borderRadius.md,
+ marginTop: spacing.sm,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 68, 68, 0.2)',
+ },
+ errorText: {
+ color: colors.sentinel.statusCritical,
+ fontSize: typography.fontSize.sm,
+ marginLeft: spacing.xs,
+ fontWeight: '500',
+ },
+ form: {
+ width: '100%',
+ },
+ inputContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ borderRadius: borderRadius.lg,
+ borderWidth: 1,
+ borderColor: 'rgba(184, 224, 229, 0.1)',
+ marginBottom: spacing.base,
+ height: 56,
+ paddingHorizontal: spacing.base,
+ },
+ inputIcon: {
+ marginRight: spacing.md,
+ },
+ input: {
+ flex: 1,
+ color: colors.sentinel.text,
+ fontSize: typography.fontSize.base,
+ },
+ forgotButton: {
+ alignSelf: 'flex-end',
+ marginBottom: spacing.lg,
+ },
+ forgotText: {
+ color: colors.sentinel.primary,
+ fontSize: typography.fontSize.sm,
+ },
+ loginButton: {
+ backgroundColor: colors.nautical.teal,
+ height: 56,
+ borderRadius: borderRadius.lg,
+ alignItems: 'center',
+ justifyContent: 'center',
+ ...shadows.glow,
+ },
+ loginButtonText: {
+ color: colors.white,
+ fontSize: typography.fontSize.md,
+ fontWeight: 'bold',
+ letterSpacing: 0.5,
+ },
+ divider: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ marginVertical: spacing.xl,
+ },
+ dividerLine: {
+ flex: 1,
+ height: 1,
+ backgroundColor: 'rgba(184, 224, 229, 0.1)',
+ },
+ dividerText: {
+ color: colors.sentinel.textSecondary,
+ paddingHorizontal: spacing.md,
+ fontSize: typography.fontSize.sm,
+ },
+ registerLink: {
+ alignItems: 'center',
+ },
+ registerText: {
+ color: colors.sentinel.textSecondary,
+ fontSize: typography.fontSize.base,
+ },
+ registerHighlight: {
+ color: colors.sentinel.primary,
+ fontWeight: 'bold',
+ },
+});
diff --git a/src/screens/MeScreen.tsx b/src/screens/MeScreen.tsx
index 7389d1c..e9446f5 100644
--- a/src/screens/MeScreen.tsx
+++ b/src/screens/MeScreen.tsx
@@ -13,6 +13,7 @@ import {
import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons, Feather, MaterialCommunityIcons, FontAwesome5 } from '@expo/vector-icons';
import { colors, typography, spacing, borderRadius, shadows } from '../theme/colors';
+import { useAuth } from '../context/AuthContext';
// Sentinel Protocol Status
const protocolStatus = {
@@ -179,21 +180,22 @@ export default function MeScreen() {
const [triggerGraceDays, setTriggerGraceDays] = useState(15);
const [triggerSource, setTriggerSource] = useState<'dual' | 'subscription' | 'activity'>('dual');
const [triggerKillSwitch, setTriggerKillSwitch] = useState(true);
+ const { user, signOut } = useAuth();
const handleOpenLink = (url: string) => {
- Linking.openURL(url).catch(() => {});
+ Linking.openURL(url).catch(() => { });
};
const handleAbandonIsland = () => {
Alert.alert(
- 'Abandon Island',
- 'Are you sure you want to delete your account? This action is irreversible. All your data, including vault contents, will be permanently destroyed.',
+ 'Sign Out',
+ 'Are you sure you want to sign out?',
[
{ text: 'Cancel', style: 'cancel' },
- {
- text: 'Delete Account',
+ {
+ text: 'Sign Out',
style: 'destructive',
- onPress: () => Alert.alert('Account Deletion', 'Please contact support to proceed with account deletion.')
+ onPress: signOut
},
]
);
@@ -206,7 +208,7 @@ export default function MeScreen() {
style={styles.gradient}
>
-
- Captain
+ {user?.username || 'Captain'}
MASTER OF THE SANCTUM
- Pro Member
+ {user?.tier || 'Pro Member'}
@@ -280,8 +282,8 @@ export default function MeScreen() {
THE CAPTAIN'S PROTOCOLS
{captainProtocols.map((item, index) => (
-
- {/* Abandon Island Button */}
-
- ABANDON ISLAND
+ SIGN OUT
{/* Settings Menu */}
SETTINGS
{settingsMenu.map((item, index) => (
- ABOUT
{protocolExplainers.slice(0, 2).map((item) => (
- setSelectedExplainer(item)}
@@ -407,7 +409,7 @@ export default function MeScreen() {
{selectedExplainer.title}
-
@@ -443,11 +445,11 @@ export default function MeScreen() {
-
-
- 12 words split into two sealed shards
-
-
+
+
+ 12 words split into two sealed shards
+
+
@@ -661,126 +663,126 @@ export default function MeScreen() {
activeOpacity={1}
onPress={() => setShowTideModal(false)}
>
- {}}>
+ { }}>
-
-
-
-
- Tide Notifications
-
-
-
- ALERT LEVEL
-
- {[
- { key: 'low', label: 'Low Tide' },
- { key: 'high', label: 'High Tide' },
- { key: 'red', label: 'Red Tide' },
- ].map((item) => {
- const isActive = tideLevel === item.key;
- return (
- setTideLevel(item.key as typeof tideLevel)}
- activeOpacity={0.85}
- >
-
- {item.label}
-
-
- );
- })}
+
+
+
+ Tide Notifications
-
-
- CHANNELS
-
- {[
- { key: 'push', label: 'App Push', icon: 'notifications' },
- { key: 'email', label: 'Email', icon: 'mail' },
- { key: 'sms', label: 'SMS', icon: 'chatbubble' },
- { key: 'emergency', label: 'Emergency', icon: 'alert' },
- ].map((item) => {
- const isActive = tideChannels[item.key as keyof typeof tideChannels];
- return (
-
- setTideChannels((prev) => ({
- ...prev,
- [item.key]: !prev[item.key as keyof typeof prev],
- }))
- }
- activeOpacity={0.85}
- >
-
-
- {item.label}
-
-
- );
- })}
+
+
+ ALERT LEVEL
+
+ {[
+ { key: 'low', label: 'Low Tide' },
+ { key: 'high', label: 'High Tide' },
+ { key: 'red', label: 'Red Tide' },
+ ].map((item) => {
+ const isActive = tideLevel === item.key;
+ return (
+ setTideLevel(item.key as typeof tideLevel)}
+ activeOpacity={0.85}
+ >
+
+ {item.label}
+
+
+ );
+ })}
+
-
-
- CADENCE
-
- {[
- { key: 'daily', label: 'Daily' },
- { key: 'weekly', label: 'Weekly' },
- { key: 'monthly', label: 'Monthly' },
- ].map((item) => {
- const isActive = tideCadence === item.key;
- return (
- setTideCadence(item.key as typeof tideCadence)}
- activeOpacity={0.85}
- >
-
- {item.label}
-
-
- );
- })}
+
+ CHANNELS
+
+ {[
+ { key: 'push', label: 'App Push', icon: 'notifications' },
+ { key: 'email', label: 'Email', icon: 'mail' },
+ { key: 'sms', label: 'SMS', icon: 'chatbubble' },
+ { key: 'emergency', label: 'Emergency', icon: 'alert' },
+ ].map((item) => {
+ const isActive = tideChannels[item.key as keyof typeof tideChannels];
+ return (
+
+ setTideChannels((prev) => ({
+ ...prev,
+ [item.key]: !prev[item.key as keyof typeof prev],
+ }))
+ }
+ activeOpacity={0.85}
+ >
+
+
+ {item.label}
+
+
+ );
+ })}
+
-
-
-
-
- Last confirmation: 3 days ago · Next reminder in 4 days.
-
+
+ CADENCE
+
+ {[
+ { key: 'daily', label: 'Daily' },
+ { key: 'weekly', label: 'Weekly' },
+ { key: 'monthly', label: 'Monthly' },
+ ].map((item) => {
+ const isActive = tideCadence === item.key;
+ return (
+ setTideCadence(item.key as typeof tideCadence)}
+ activeOpacity={0.85}
+ >
+
+ {item.label}
+
+
+ );
+ })}
+
+
+
+
+
+
+ Last confirmation: 3 days ago · Next reminder in 4 days.
+
+
+
+
+ setShowTideModal(false)}
+ >
+
+ Save
+
+ setShowTideModal(false)}
+ >
+
+ Close
+
-
-
- setShowTideModal(false)}
- >
-
- Save
-
- setShowTideModal(false)}
- >
-
- Close
-
-
diff --git a/src/screens/RegisterScreen.tsx b/src/screens/RegisterScreen.tsx
new file mode 100644
index 0000000..15a3370
--- /dev/null
+++ b/src/screens/RegisterScreen.tsx
@@ -0,0 +1,290 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ StyleSheet,
+ TextInput,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+ Platform,
+ SafeAreaView,
+ ScrollView,
+ Alert,
+} from 'react-native';
+import { LinearGradient } from 'expo-linear-gradient';
+import { Feather, MaterialCommunityIcons } from '@expo/vector-icons';
+import { colors, spacing, borderRadius, typography, shadows } from '../theme/colors';
+import { useAuth } from '../context/AuthContext';
+import { ActivityIndicator } from 'react-native';
+
+export default function RegisterScreen({ navigation }: any) {
+ const [name, setName] = useState('');
+ const [email, setEmail] = useState('');
+ const [password, setPassword] = useState('');
+ const [confirmPassword, setConfirmPassword] = useState('');
+ const [error, setError] = useState(null);
+ const { signUp, isLoading } = useAuth();
+
+ const handleRegister = async () => {
+ setError(null);
+ if (!name || !email || !password || !confirmPassword) {
+ setError('Please fill in all fields.');
+ return;
+ }
+
+ if (password !== confirmPassword) {
+ setError('Passwords do not match.');
+ return;
+ }
+
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ setError('Please enter a valid email address.');
+ return;
+ }
+
+ try {
+ await signUp({ username: name, email, password });
+ } catch (err: any) {
+ setError(err.message || 'Registration failed. Please try again.');
+ }
+ };
+
+ return (
+
+
+
+
+
+ navigation.goBack()}
+ >
+
+
+
+
+
+
+
+ Join the Fleet
+ Begin your journey with Sentinel
+ {error && (
+
+
+ {error}
+
+ )}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+ Create Account
+ )}
+
+
+ navigation.navigate('Login')}
+ >
+
+ Already have an account? Login
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ gradient: {
+ flex: 1,
+ },
+ safeArea: {
+ flex: 1,
+ },
+ content: {
+ flex: 1,
+ },
+ scrollContent: {
+ flexGrow: 1,
+ paddingHorizontal: spacing.lg,
+ paddingBottom: spacing.xl,
+ },
+ backButton: {
+ marginTop: spacing.md,
+ marginBottom: spacing.lg,
+ width: 40,
+ height: 40,
+ borderRadius: borderRadius.full,
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ header: {
+ alignItems: 'center',
+ marginBottom: spacing.xl,
+ },
+ logoContainer: {
+ width: 80,
+ height: 80,
+ borderRadius: borderRadius.full,
+ backgroundColor: 'rgba(255, 255, 255, 0.1)',
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginBottom: spacing.lg,
+ borderWidth: 1,
+ borderColor: 'rgba(184, 224, 229, 0.2)',
+ },
+ title: {
+ fontSize: typography.fontSize.xxl,
+ fontWeight: 'bold',
+ color: colors.sentinel.text,
+ marginBottom: spacing.xs,
+ letterSpacing: 0.5,
+ },
+ subtitle: {
+ fontSize: typography.fontSize.base,
+ color: colors.sentinel.textSecondary,
+ },
+ form: {
+ width: '100%',
+ },
+ inputContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(255, 255, 255, 0.05)',
+ borderRadius: borderRadius.lg,
+ borderWidth: 1,
+ borderColor: 'rgba(184, 224, 229, 0.1)',
+ marginBottom: spacing.base,
+ height: 56,
+ paddingHorizontal: spacing.base,
+ },
+ inputIcon: {
+ marginRight: spacing.md,
+ },
+ input: {
+ flex: 1,
+ color: colors.sentinel.text,
+ fontSize: typography.fontSize.base,
+ },
+ registerButton: {
+ backgroundColor: colors.nautical.seafoam,
+ height: 56,
+ borderRadius: borderRadius.lg,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginTop: spacing.md,
+ ...shadows.glow,
+ },
+ registerButtonText: {
+ color: colors.white,
+ fontSize: typography.fontSize.md,
+ fontWeight: 'bold',
+ letterSpacing: 0.5,
+ },
+ loginLink: {
+ alignItems: 'center',
+ marginTop: spacing.xl,
+ },
+ loginLinkText: {
+ color: colors.sentinel.textSecondary,
+ fontSize: typography.fontSize.base,
+ },
+ highlight: {
+ color: colors.nautical.seafoam,
+ fontWeight: 'bold',
+ },
+ disabledButton: {
+ opacity: 0.7,
+ },
+ errorContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ backgroundColor: 'rgba(255, 68, 68, 0.1)',
+ padding: spacing.sm,
+ borderRadius: borderRadius.md,
+ marginTop: spacing.md,
+ borderWidth: 1,
+ borderColor: 'rgba(255, 68, 68, 0.2)',
+ },
+ errorText: {
+ color: colors.sentinel.statusCritical,
+ fontSize: typography.fontSize.sm,
+ marginLeft: spacing.xs,
+ fontWeight: '500',
+ },
+});
diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts
new file mode 100644
index 0000000..a1f221d
--- /dev/null
+++ b/src/services/auth.service.ts
@@ -0,0 +1,80 @@
+import { LoginRequest, LoginResponse, RegisterRequest, User } from '../types';
+
+const PLATFORM_URL = 'http://192.168.56.103:8000';
+const no_backend_mode = true;
+
+const MOCK_USER: User = {
+ id: 999,
+ username: 'MockCaptain',
+ public_key: 'mock_public_key',
+ is_admin: true,
+ guale: false,
+ tier: 'premium',
+ tier_expires_at: '2026-12-31T23:59:59Z',
+ last_active_at: new Date().toISOString(),
+};
+
+export const authService = {
+ async login(credentials: LoginRequest): Promise {
+ if (no_backend_mode) {
+ console.log('No-Backend Mode: Simulating login...');
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve({
+ access_token: 'mock_access_token',
+ token_type: 'bearer',
+ user: { ...MOCK_USER, username: credentials.username },
+ });
+ }, 200);
+ });
+ }
+ try {
+ const response = await fetch(`${PLATFORM_URL}/login`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(credentials),
+ });
+
+ if (!response.ok) {
+ throw new Error('Login failed');
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('Login error:', error);
+ throw error;
+ }
+ },
+
+ async register(data: RegisterRequest): Promise {
+ if (no_backend_mode) {
+ console.log('No-Backend Mode: Simulating registration...');
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve({ ...MOCK_USER, username: data.username });
+ }, 200);
+ });
+ }
+ try {
+ const response = await fetch(`${PLATFORM_URL}/register`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json().catch(() => ({}));
+ throw new Error(errorData.detail?.[0]?.msg || 'Registration failed');
+ }
+
+ return await response.json();
+ } catch (error) {
+ console.error('Registration error:', error);
+ throw error;
+ }
+ },
+};
diff --git a/src/types/index.ts b/src/types/index.ts
index 4de19da..5d46372 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -13,7 +13,7 @@ export interface FlowRecord {
}
// Vault Types
-export type VaultAssetType =
+export type VaultAssetType =
| 'game_account'
| 'private_key'
| 'document'
@@ -72,3 +72,32 @@ export interface ProtocolInfo {
version: string;
lastUpdated: Date;
}
+
+// Auth Types
+export interface User {
+ id: number;
+ username: string;
+ public_key: string;
+ is_admin: boolean;
+ guale: boolean;
+ tier: string;
+ tier_expires_at: string;
+ last_active_at: string;
+}
+
+export interface LoginRequest {
+ username: string;
+ password: string;
+}
+
+export interface RegisterRequest {
+ username: string;
+ password: string;
+ email: string;
+}
+
+export interface LoginResponse {
+ access_token: string;
+ token_type: string;
+ user: User;
+}