From 740036346690da54fa0004c0e787e1949db8a721 Mon Sep 17 00:00:00 2001 From: corpi Date: Wed, 31 Dec 2025 18:40:46 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=B0=8F=20=EA=B3=B5=ED=86=B5=20=EC=86=8D=EC=84=B1?= =?UTF-8?q?=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 27 ++- package-lock.json | 312 +++++++++++++++++++++++++++++++++- package.json | 6 +- src/components/ui/AppText.tsx | 27 +++ src/components/ui/Button.tsx | 73 ++++++++ src/components/ui/Input.tsx | 32 ++++ src/navigation/AppStack.tsx | 17 ++ src/navigation/AuthGate.tsx | 10 ++ src/navigation/AuthStack.tsx | 17 ++ src/screens/HomeScreen.tsx | 18 ++ src/screens/LoginScreen.tsx | 74 ++++++++ src/store/auth.tsx | 31 ++++ src/theme/colors.ts | 22 +++ src/theme/radius.ts | 6 + src/theme/spacing.ts | 8 + src/theme/theme.ts | 12 ++ src/theme/typography.ts | 14 ++ 17 files changed, 689 insertions(+), 17 deletions(-) create mode 100644 src/components/ui/AppText.tsx create mode 100644 src/components/ui/Button.tsx create mode 100644 src/components/ui/Input.tsx create mode 100644 src/navigation/AppStack.tsx create mode 100644 src/navigation/AuthGate.tsx create mode 100644 src/navigation/AuthStack.tsx create mode 100644 src/screens/HomeScreen.tsx create mode 100644 src/screens/LoginScreen.tsx create mode 100644 src/store/auth.tsx create mode 100644 src/theme/colors.ts create mode 100644 src/theme/radius.ts create mode 100644 src/theme/spacing.ts create mode 100644 src/theme/theme.ts create mode 100644 src/theme/typography.ts diff --git a/App.tsx b/App.tsx index 0329d0c..9c87540 100644 --- a/App.tsx +++ b/App.tsx @@ -1,20 +1,17 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; +import { NavigationContainer } from "@react-navigation/native"; +import React from "react"; +import { enableScreens } from "react-native-screens"; +import AuthGate from "./src/navigation/AuthGate"; +import { AuthProvider } from "./src/store/auth"; + +enableScreens(); export default function App() { return ( - - Open up App.tsx to start working on your app! - - + + + + + ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); diff --git a/package-lock.json b/package-lock.json index 05e4bac..d09a78d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,14 @@ "name": "audiobook-app", "version": "1.0.0", "dependencies": { + "@react-navigation/native": "^7.1.26", + "@react-navigation/native-stack": "^7.9.0", "expo": "~54.0.30", "expo-status-bar": "~3.0.9", "react": "19.1.0", - "react-native": "0.81.5" + "react-native": "0.81.5", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0" }, "devDependencies": { "@types/react": "~19.1.0", @@ -2972,6 +2976,123 @@ "integrity": "sha512-0HuJ8YtqlTVRXGZuGeBejLE04wSQsibpTI+RGOyVqxZvgtlLLC/Ssw0UmbHhT4lYMp2fhdtvKZSs5emWB1zR/g==", "license": "MIT" }, + "node_modules/@react-navigation/core": { + "version": "7.13.7", + "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.13.7.tgz", + "integrity": "sha512-k2ABo3250vq1ovOh/iVwXS6Hwr5PVRGXoPh/ewVFOOuEKTvOx9i//OBzt8EF+HokBxS2HBRlR2b+aCOmscRqBw==", + "license": "MIT", + "dependencies": { + "@react-navigation/routers": "^7.5.3", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "query-string": "^7.1.3", + "react-is": "^19.1.0", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "react": ">= 18.2.0" + } + }, + "node_modules/@react-navigation/core/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/core/node_modules/react-is": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.3.tgz", + "integrity": "sha512-qJNJfu81ByyabuG7hPFEbXqNcWSU3+eVus+KJs+0ncpGfMyYdvSmxiJxbWR65lYi1I+/0HBcliO029gc4F+PnA==", + "license": "MIT" + }, + "node_modules/@react-navigation/elements": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.3.tgz", + "integrity": "sha512-3+eyvWiVPIEf6tN9UdduhOEHcTuNe3R5WovgiVkfH9+jApHMTZDc2loePTpY/i2HDJhObhhChpJzO6BVjrpdYQ==", + "license": "MIT", + "dependencies": { + "color": "^4.2.3", + "use-latest-callback": "^0.2.4", + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@react-native-masked-view/masked-view": ">= 0.2.0", + "@react-navigation/native": "^7.1.26", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0" + }, + "peerDependenciesMeta": { + "@react-native-masked-view/masked-view": { + "optional": true + } + } + }, + "node_modules/@react-navigation/native": { + "version": "7.1.26", + "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.1.26.tgz", + "integrity": "sha512-RhKmeD0E2ejzKS6z8elAfdfwShpcdkYY8zJzvHYLq+wv183BBcElTeyMLcIX6wIn7QutXeI92Yi21t7aUWfqNQ==", + "license": "MIT", + "dependencies": { + "@react-navigation/core": "^7.13.7", + "escape-string-regexp": "^4.0.0", + "fast-deep-equal": "^3.1.3", + "nanoid": "^3.3.11", + "use-latest-callback": "^0.2.4" + }, + "peerDependencies": { + "react": ">= 18.2.0", + "react-native": "*" + } + }, + "node_modules/@react-navigation/native-stack": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/@react-navigation/native-stack/-/native-stack-7.9.0.tgz", + "integrity": "sha512-C/mNPhI0Pnerl7C2cB+6fAkdgSmfKECMERrbyfjx3P6JmEuTC54o+GV1c62FUmlRaRUassVHbtw4EeaY2uLh0g==", + "license": "MIT", + "dependencies": { + "@react-navigation/elements": "^2.9.3", + "color": "^4.2.3", + "sf-symbols-typescript": "^2.1.0", + "warn-once": "^0.1.1" + }, + "peerDependencies": { + "@react-navigation/native": "^7.1.26", + "react": ">= 18.2.0", + "react-native": "*", + "react-native-safe-area-context": ">= 4.0.0", + "react-native-screens": ">= 4.0.0" + } + }, + "node_modules/@react-navigation/native/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@react-navigation/routers": { + "version": "7.5.3", + "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.5.3.tgz", + "integrity": "sha512-1tJHg4KKRJuQ1/EvJxatrMef3NZXEPzwUIUZ3n1yJ2t7Q97siwRtbynRpQG9/69ebbtiZ8W3ScOZF/OmhvM4Rg==", + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -3916,6 +4037,19 @@ "node": ">=0.8" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -3931,6 +4065,34 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -4096,6 +4258,15 @@ } } }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -4301,6 +4472,7 @@ "resolved": "https://registry.npmjs.org/expo/-/expo-54.0.30.tgz", "integrity": "sha512-6q+aFfKL0SpT8prfdpR3V8HcN51ov0mCGuwQTzyuk6eeO9rg7a7LWbgPv9rEVXGZEuyULstL8LGNwHqusand7Q==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.0", "@expo/cli": "54.0.20", @@ -4885,6 +5057,12 @@ "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "license": "Apache-2.0" }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -4912,6 +5090,15 @@ "node": ">=8" } }, + "node_modules/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/finalhandler": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", @@ -5284,6 +5471,12 @@ "loose-envify": "^1.0.0" } }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", @@ -7222,6 +7415,24 @@ "qrcode-terminal": "bin/qrcode-terminal.js" } }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/queue": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", @@ -7274,6 +7485,18 @@ "ws": "^7" } }, + "node_modules/react-freeze": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz", + "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=17.0.0" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -7347,6 +7570,33 @@ "react-native": "*" } }, + "node_modules/react-native-safe-area-context": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.6.2.tgz", + "integrity": "sha512-4XGqMNj5qjUTYywJqpdWZ9IG8jgkS3h06sfVjfw5yZQZfWnRFXczi0GnYyFyCc2EBps/qFmoCH8fez//WumdVg==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, + "node_modules/react-native-screens": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.16.0.tgz", + "integrity": "sha512-yIAyh7F/9uWkOzCi1/2FqvNvK6Wb9Y1+Kzn16SuGfN9YFJDTbwlzGRvePCNTOX0recpLQF3kc2FmvMUhyTCH1Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "react-freeze": "^1.0.0", + "react-native-is-edge-to-edge": "^1.2.1", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native/node_modules/@react-native/virtualized-lists": { "version": "0.81.5", "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.81.5.tgz", @@ -7820,6 +8070,15 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sf-symbols-typescript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz", + "integrity": "sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7870,6 +8129,15 @@ "plist": "^3.0.5" } }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -7931,6 +8199,15 @@ "node": ">=0.10.0" } }, + "node_modules/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -7994,6 +8271,15 @@ "node": ">= 0.10.0" } }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8499,6 +8785,24 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/use-latest-callback": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.6.tgz", + "integrity": "sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -8550,6 +8854,12 @@ "makeerror": "1.0.12" } }, + "node_modules/warn-once": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/warn-once/-/warn-once-0.1.1.tgz", + "integrity": "sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==", + "license": "MIT" + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", diff --git a/package.json b/package.json index d4735e2..e81e041 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,14 @@ "web": "expo start --web" }, "dependencies": { + "@react-navigation/native": "^7.1.26", + "@react-navigation/native-stack": "^7.9.0", "expo": "~54.0.30", "expo-status-bar": "~3.0.9", "react": "19.1.0", - "react-native": "0.81.5" + "react-native": "0.81.5", + "react-native-safe-area-context": "~5.6.0", + "react-native-screens": "~4.16.0" }, "devDependencies": { "@types/react": "~19.1.0", diff --git a/src/components/ui/AppText.tsx b/src/components/ui/AppText.tsx new file mode 100644 index 0000000..4eacf0a --- /dev/null +++ b/src/components/ui/AppText.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { Text, type TextProps, type TextStyle } from "react-native"; +import { Theme } from "../../theme/theme"; + +type Variant = "logo" | "title" | "body" | "muted" | "error"; + +type Props = TextProps & { + variant?: Variant; + style?: TextStyle | TextStyle[]; +}; + +export default function AppText({ variant = "body", style, ...props }: Props) { + const base: TextStyle = { + color: Theme.Colors.text, + fontSize: Theme.FontSize.md, + }; + + const variants: Record = { + logo: { fontSize: Theme.FontSize.xxl, fontWeight: Theme.FontWeight.bold }, + title: { fontSize: Theme.FontSize.xl, fontWeight: Theme.FontWeight.bold }, + body: { fontSize: Theme.FontSize.md }, + muted: { color: Theme.Colors.mutedText, fontSize: Theme.FontSize.sm }, + error: { color: Theme.Colors.danger, fontSize: Theme.FontSize.sm }, + }; + + return ; +} diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 0000000..634afcb --- /dev/null +++ b/src/components/ui/Button.tsx @@ -0,0 +1,73 @@ +import React from "react"; +import { Pressable, type PressableProps, StyleSheet, View } from "react-native"; +import { Theme } from "../../theme/theme"; +import AppText from "./AppText"; + +type Props = PressableProps & { + title: string; + variant?: "primary" | "secondary"; +}; + +export default function Button({ + title, + variant = "primary", + style, + ...props +}: Props) { + const isPrimary = variant === "primary"; + + return ( + [ + styles.base, + isPrimary ? styles.primary : styles.secondary, + pressed && + (isPrimary ? styles.primaryPressed : styles.secondaryPressed), + typeof style === "function" ? style({ pressed }) : style, + ]} + > + + + {title} + + + + ); +} + +const styles = StyleSheet.create({ + base: { + height: 48, + paddingVertical: 0, + paddingHorizontal: Theme.Spacing.lg, + borderRadius: Theme.Radius.md, + alignItems: "center", // 가로 가운데 + justifyContent: "center", // 세로 가운데 + }, + text: { + textAlign: "center", + fontWeight: Theme.FontWeight.bold, + lineHeight: 20, + }, + primary: { + backgroundColor: Theme.Colors.primary, + }, + primaryPressed: { + backgroundColor: Theme.Colors.primaryPressed, + }, + secondary: { + backgroundColor: Theme.Colors.surface, + borderWidth: 1, + borderColor: Theme.Colors.border, + }, + secondaryPressed: { + opacity: 0.9, + }, +}); diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx new file mode 100644 index 0000000..ce3f3dd --- /dev/null +++ b/src/components/ui/Input.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import { StyleSheet, TextInput, type TextInputProps, View } from "react-native"; +import { Theme } from "../../theme/theme"; + +type Props = TextInputProps; + +export default function Input(props: Props) { + return ( + + + + ); +} + +const styles = StyleSheet.create({ + wrap: { + borderWidth: 1, + borderColor: Theme.Colors.border, + borderRadius: Theme.Radius.md, + backgroundColor: Theme.Colors.inputBg, + }, + input: { + paddingHorizontal: Theme.Spacing.md, + paddingVertical: Theme.Spacing.md, + color: Theme.Colors.text, + fontSize: Theme.FontSize.md, + }, +}); diff --git a/src/navigation/AppStack.tsx b/src/navigation/AppStack.tsx new file mode 100644 index 0000000..d17ff21 --- /dev/null +++ b/src/navigation/AppStack.tsx @@ -0,0 +1,17 @@ +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import React from "react"; +import HomeScreen from "../screens/HomeScreen"; + +export type AppStackParamList = { + Home: undefined; +}; + +const Stack = createNativeStackNavigator(); + +export default function AppStack() { + return ( + + + + ); +} diff --git a/src/navigation/AuthGate.tsx b/src/navigation/AuthGate.tsx new file mode 100644 index 0000000..bdc50f1 --- /dev/null +++ b/src/navigation/AuthGate.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import AppStack from "./AppStack"; +import AuthStack from "./AuthStack"; + +export default function AuthGate() { + // const { isAuthed } = useAuth(); + const isAuthed = true; + // return ; + return isAuthed ? : ; +} diff --git a/src/navigation/AuthStack.tsx b/src/navigation/AuthStack.tsx new file mode 100644 index 0000000..49c671d --- /dev/null +++ b/src/navigation/AuthStack.tsx @@ -0,0 +1,17 @@ +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import React from "react"; +import LoginScreen from "../screens/LoginScreen"; + +export type AuthStackParamList = { + Login: undefined; +}; + +const Stack = createNativeStackNavigator(); + +export default function AuthStack() { + return ( + + + + ); +} diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx new file mode 100644 index 0000000..fb615a5 --- /dev/null +++ b/src/screens/HomeScreen.tsx @@ -0,0 +1,18 @@ +import { StyleSheet, Text, View } from "react-native"; + +export default function HomeScreen() { + return ( + + feawfewa + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "red", + alignContent: "center", + justifyContent: "center", + }, +}); diff --git a/src/screens/LoginScreen.tsx b/src/screens/LoginScreen.tsx new file mode 100644 index 0000000..e4c98df --- /dev/null +++ b/src/screens/LoginScreen.tsx @@ -0,0 +1,74 @@ +import React from "react"; +import { Pressable, StyleSheet, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import AppText from "../components/ui/AppText"; +import Button from "../components/ui/Button"; +import Input from "../components/ui/Input"; +import { Theme } from "../theme/theme"; + +export default function LoginScreen() { + return ( + + + Your personal growth podcast + Sign in to continue + + + + + + + + + + + +