From 7386f012aa54541dffe842cfb9c5da4c51e201ee Mon Sep 17 00:00:00 2001 From: Stepan Date: Thu, 1 Jan 2026 17:46:58 +0100 Subject: [PATCH] init --- .env.example | 1 + .gitignore | 4 + api/axiosInstance.ts | 25 +++ api/client.ts | 14 ++ api/index.ts | 1 + api/questions.ts | 15 ++ api/types.ts | 67 ++++++++ app.json | 2 +- app/(tabs)/_layout.tsx | 24 ++- app/(tabs)/index.tsx | 91 +++-------- app/(tabs)/me.tsx | 57 +++++++ app/(tabs)/{explore.tsx => questions.tsx} | 4 +- app/(tabs)/tests.tsx | 57 +++++++ app/_layout.tsx | 19 ++- app/modal.tsx | 2 +- components/question.tsx | 32 ++++ components/ui/collapsible.tsx | 2 +- components/ui/not-found.tsx | 24 +++ components/ui/panel.tsx | 25 +++ components/{ => ui}/themed-text.tsx | 0 package-lock.json | 178 ++++++++++++++++++++-- package.json | 11 +- 22 files changed, 554 insertions(+), 101 deletions(-) create mode 100644 .env.example create mode 100644 api/axiosInstance.ts create mode 100644 api/client.ts create mode 100644 api/index.ts create mode 100644 api/questions.ts create mode 100644 api/types.ts create mode 100644 app/(tabs)/me.tsx rename app/(tabs)/{explore.tsx => questions.tsx} (98%) create mode 100644 app/(tabs)/tests.tsx create mode 100644 components/question.tsx create mode 100644 components/ui/not-found.tsx create mode 100644 components/ui/panel.tsx rename components/{ => ui}/themed-text.tsx (100%) diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6bc369b --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +EXPO_BACKEND_BASE_URL= \ No newline at end of file diff --git a/.gitignore b/.gitignore index f8c6c2e..bcd2cda 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ yarn-error.* # local env files .env*.local +.env # typescript *.tsbuildinfo @@ -41,3 +42,6 @@ app-example # generated native folders /ios /android + +# Editors +.vscode/ \ No newline at end of file diff --git a/api/axiosInstance.ts b/api/axiosInstance.ts new file mode 100644 index 0000000..89e2059 --- /dev/null +++ b/api/axiosInstance.ts @@ -0,0 +1,25 @@ +import axios from "axios"; + +const axiosInstance = axios.create({ + baseURL: process.env.EXPO_PUBLIC_BACKEND_BASE_URL, + timeout: 30 * 1000 +}); + +axiosInstance.interceptors.request.use( + (config) => { + const token = localStorage.getItem("access_token"); + if (token) config.headers.Authorization = `Bearer ${token}`; + + return config; + }, + (error) => Promise.reject(error), +); +axiosInstance.interceptors.response.use(undefined, async (error) => { + if (error.response?.status === 401) { + localStorage.removeItem("access_token"); + } + + throw error; +}); + +export default axiosInstance; \ No newline at end of file diff --git a/api/client.ts b/api/client.ts new file mode 100644 index 0000000..ee28664 --- /dev/null +++ b/api/client.ts @@ -0,0 +1,14 @@ +import axiosInstance from "./axiosInstance"; +import { Pagination, QuestionResponse } from "./types"; + +export const get_questions = async (page: number, test_id?: number, question_id?: number) => { + const response = await axiosInstance.get>("/api/questions/", { + params: { + page, + test_id, + question_id + } + }) + + return response.data; +} diff --git a/api/index.ts b/api/index.ts new file mode 100644 index 0000000..076bf92 --- /dev/null +++ b/api/index.ts @@ -0,0 +1 @@ +export * from './questions'; diff --git a/api/questions.ts b/api/questions.ts new file mode 100644 index 0000000..39bf8eb --- /dev/null +++ b/api/questions.ts @@ -0,0 +1,15 @@ +import { useQuery } from "@tanstack/react-query"; +import { get_questions } from "./client"; + +interface useQuestionsAttr { + page?: number; + test_id?: number; + question_id?: number; +} +export const useQuestions = ({ page = 1, test_id, question_id }: useQuestionsAttr = { }) => { + return useQuery({ + queryKey: ['questions', page, test_id, question_id], + queryFn: () => get_questions(page, test_id, question_id) + }) +} + diff --git a/api/types.ts b/api/types.ts new file mode 100644 index 0000000..c0412e5 --- /dev/null +++ b/api/types.ts @@ -0,0 +1,67 @@ +export interface Pagination { + data: T[]; + links: { + first: string; + last: string; + }; + meta: { + current_page: number; + from: number; + last_page: number; + per_page: number; + to: number; + total: number; + } +} +export interface QuestionVariant { + id: number; + text: string; +} +export enum QuestionTypes { + Single = "single", + Multiple = "multiple", + Text = "text" +} +export interface QuestionResponse { + id: number; + title: string; + description: string; + type: QuestionTypes; + difficulty: number; + + variants: QuestionVariant[]; + correct_answers: number[]; + + category_id: number; + category: CategoryResponse; + + author_id: number; + author: UserResponse; + + created_at: string; + updated_at: string; +} +export interface CategoryResponse { + id: number; + name: string; + + questions_count: number; + user_tests_count: number; + + created_at: string; +} +export enum UserTypes { + Admin = "admin", + Creator = "creator", + User = "user", + Banned = "banned" +} +export interface UserResponse { + id: number; + username: string; + email: string; + email_verified_at: string; + type: UserTypes; + created_at: string; + updated_at: string; +} \ No newline at end of file diff --git a/app.json b/app.json index 5d4941e..79ee422 100644 --- a/app.json +++ b/app.json @@ -1,6 +1,6 @@ { "expo": { - "name": "ai_questions_app", + "name": "Hoshi AI", "slug": "ai_questions_app", "version": "1.0.0", "orientation": "portrait", diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 54e11d0..09c37f1 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -2,9 +2,9 @@ import { Tabs } from 'expo-router'; import React from 'react'; import { HapticTab } from '@/components/haptic-tab'; -import { IconSymbol } from '@/components/ui/icon-symbol'; import { Colors } from '@/constants/theme'; import { useColorScheme } from '@/hooks/use-color-scheme'; +import { FontAwesome5 } from '@expo/vector-icons'; export default function TabLayout() { const colorScheme = useColorScheme(); @@ -20,14 +20,28 @@ export default function TabLayout() { name="index" options={{ title: 'Home', - tabBarIcon: ({ color }) => , + tabBarIcon: ({ color }) => , }} /> , + title: 'Questions', + tabBarIcon: ({ color }) => , + }} + /> + , + }} + /> + , }} /> diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 786b736..6d29145 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,87 +1,46 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } from 'react-native'; +import { Image } from "expo-image"; +import { StyleSheet } from "react-native"; -import { HelloWave } from '@/components/hello-wave'; -import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; -import { ThemedView } from '@/components/themed-view'; -import { Link } from 'expo-router'; +import { useQuestions } from "@/api"; +import { HelloWave } from "@/components/hello-wave"; +import ParallaxScrollView from "@/components/parallax-scroll-view"; +import Question from "@/components/question"; +import { ThemedView } from "@/components/themed-view"; +import { ThemedText } from "@/components/ui/themed-text"; export default function HomeScreen() { + const { data: questions, isLoading: isLoadingQuestions } = useQuestions(); + + const questionsLoaded = + !isLoadingQuestions && questions && questions.meta.total > 0; + return ( - }> + } + > - Welcome! + Questions! - - Step 1: Try it - - Edit app/(tabs)/index.tsx to see changes. - Press{' '} - - {Platform.select({ - ios: 'cmd + d', - android: 'cmd + m', - web: 'F12', - })} - {' '} - to open developer tools. - - - - - - Step 2: Explore - - - - alert('Action pressed')} /> - alert('Share pressed')} - /> - - alert('Delete pressed')} - /> - - - - - {`Tap the Explore tab to learn more about what's included in this starter app.`} - - - - Step 3: Get a fresh start - - {`When you're ready, run `} - npm run reset-project to get a fresh{' '} - app directory. This will move the current{' '} - app to{' '} - app-example. - - + {questionsLoaded && + questions.data.map((question) => ( + + ))} ); } const styles = StyleSheet.create({ titleContainer: { - flexDirection: 'row', - alignItems: 'center', + flexDirection: "row", + alignItems: "center", gap: 8, }, stepContainer: { @@ -93,6 +52,6 @@ const styles = StyleSheet.create({ width: 290, bottom: 0, left: 0, - position: 'absolute', + position: "absolute", }, }); diff --git a/app/(tabs)/me.tsx b/app/(tabs)/me.tsx new file mode 100644 index 0000000..ba6b0d8 --- /dev/null +++ b/app/(tabs)/me.tsx @@ -0,0 +1,57 @@ +import { Image } from 'expo-image'; +import { Platform, StyleSheet } from 'react-native'; + +import ParallaxScrollView from '@/components/parallax-scroll-view'; +import { ThemedView } from '@/components/themed-view'; +import { ThemedText } from '@/components/ui/themed-text'; + +export default function HomeScreen() { + return ( + + }> + + Welcome! + + + Step 1: Try it + + Edit app/(tabs)/index.tsx to see changes. + Press{' '} + + {Platform.select({ + ios: 'cmd + d', + android: 'cmd + m', + web: 'F12', + })} + {' '} + to open developer tools. + + + + ); +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: 'absolute', + }, +}); diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/questions.tsx similarity index 98% rename from app/(tabs)/explore.tsx rename to app/(tabs)/questions.tsx index 71518f9..9decaba 100644 --- a/app/(tabs)/explore.tsx +++ b/app/(tabs)/questions.tsx @@ -1,12 +1,12 @@ import { Image } from 'expo-image'; import { Platform, StyleSheet } from 'react-native'; -import { Collapsible } from '@/components/ui/collapsible'; import { ExternalLink } from '@/components/external-link'; import ParallaxScrollView from '@/components/parallax-scroll-view'; -import { ThemedText } from '@/components/themed-text'; import { ThemedView } from '@/components/themed-view'; +import { Collapsible } from '@/components/ui/collapsible'; import { IconSymbol } from '@/components/ui/icon-symbol'; +import { ThemedText } from '@/components/ui/themed-text'; import { Fonts } from '@/constants/theme'; export default function TabTwoScreen() { diff --git a/app/(tabs)/tests.tsx b/app/(tabs)/tests.tsx new file mode 100644 index 0000000..ba6b0d8 --- /dev/null +++ b/app/(tabs)/tests.tsx @@ -0,0 +1,57 @@ +import { Image } from 'expo-image'; +import { Platform, StyleSheet } from 'react-native'; + +import ParallaxScrollView from '@/components/parallax-scroll-view'; +import { ThemedView } from '@/components/themed-view'; +import { ThemedText } from '@/components/ui/themed-text'; + +export default function HomeScreen() { + return ( + + }> + + Welcome! + + + Step 1: Try it + + Edit app/(tabs)/index.tsx to see changes. + Press{' '} + + {Platform.select({ + ios: 'cmd + d', + android: 'cmd + m', + web: 'F12', + })} + {' '} + to open developer tools. + + + + ); +} + +const styles = StyleSheet.create({ + titleContainer: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + stepContainer: { + gap: 8, + marginBottom: 8, + }, + reactLogo: { + height: 178, + width: 290, + bottom: 0, + left: 0, + position: 'absolute', + }, +}); diff --git a/app/_layout.tsx b/app/_layout.tsx index f518c9b..3203d9d 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,4 +1,5 @@ import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { Stack } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; import 'react-native-reanimated'; @@ -9,16 +10,20 @@ export const unstable_settings = { anchor: '(tabs)', }; +const queryClient = new QueryClient(); + export default function RootLayout() { const colorScheme = useColorScheme(); return ( - - - - - - - + + + + + + + + + ); } diff --git a/app/modal.tsx b/app/modal.tsx index 6dfbc1a..524d776 100644 --- a/app/modal.tsx +++ b/app/modal.tsx @@ -1,8 +1,8 @@ import { Link } from 'expo-router'; import { StyleSheet } from 'react-native'; -import { ThemedText } from '@/components/themed-text'; import { ThemedView } from '@/components/themed-view'; +import { ThemedText } from '@/components/ui/themed-text'; export default function ModalScreen() { return ( diff --git a/components/question.tsx b/components/question.tsx new file mode 100644 index 0000000..e5c6c43 --- /dev/null +++ b/components/question.tsx @@ -0,0 +1,32 @@ +import { QuestionResponse } from "@/api/types"; +import { Pressable, StyleSheet } from "react-native"; +import Panel from "./ui/panel"; +import { ThemedText } from "./ui/themed-text"; + +interface QuestionProps { + question: QuestionResponse; + onPress?: () => void; +} + +const Question = ({ question, onPress }: QuestionProps) => { + return ( + + + {question.title} + {question.description} + + + ) +} + +const styles = StyleSheet.create({ + questionTitle: { + fontSize: 18, + fontWeight: "600", + marginBottom: 10 + }, +}); + + + +export default Question; \ No newline at end of file diff --git a/components/ui/collapsible.tsx b/components/ui/collapsible.tsx index 6345fde..bdd9262 100644 --- a/components/ui/collapsible.tsx +++ b/components/ui/collapsible.tsx @@ -1,9 +1,9 @@ import { PropsWithChildren, useState } from 'react'; import { StyleSheet, TouchableOpacity } from 'react-native'; -import { ThemedText } from '@/components/themed-text'; import { ThemedView } from '@/components/themed-view'; import { IconSymbol } from '@/components/ui/icon-symbol'; +import { ThemedText } from '@/components/ui/themed-text'; import { Colors } from '@/constants/theme'; import { useColorScheme } from '@/hooks/use-color-scheme'; diff --git a/components/ui/not-found.tsx b/components/ui/not-found.tsx new file mode 100644 index 0000000..7757c83 --- /dev/null +++ b/components/ui/not-found.tsx @@ -0,0 +1,24 @@ +import { StyleSheet, View } from "react-native"; +import { ThemedText } from "./themed-text"; + +interface NotFoundProps { + text?: string; +} + +const NotFound = ({text = "Not Found"}: NotFoundProps) => { + return ( + + {text} + + ) +} + +const styles = StyleSheet.create({ + notFound: { + padding: 15, + textAlign: "center" + }, +}); + + +export default NotFound; \ No newline at end of file diff --git a/components/ui/panel.tsx b/components/ui/panel.tsx new file mode 100644 index 0000000..08ebedc --- /dev/null +++ b/components/ui/panel.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from "react"; +import { StyleSheet, View } from "react-native"; + +interface PanelProps { + children?: ReactNode; +} + +const Panel = ({children}: PanelProps) => { + return ( + + {children} + + ) +} + +const styles = StyleSheet.create({ + panel: { + padding: 15, + borderRadius: 5, + boxShadow: '0px 4px 5px 0px rgba(0,0,0,0.18)' + }, +}); + + +export default Panel; \ No newline at end of file diff --git a/components/themed-text.tsx b/components/ui/themed-text.tsx similarity index 100% rename from components/themed-text.tsx rename to components/ui/themed-text.tsx diff --git a/package-lock.json b/package-lock.json index 6dc6b06..44803bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", + "@tanstack/react-query": "^5.90.16", + "axios": "^1.13.2", "expo": "~54.0.30", "expo-constants": "~18.0.12", "expo-font": "~14.0.10", @@ -31,6 +33,7 @@ "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", + "react-native-vector-icons": "^10.3.0", "react-native-web": "~0.21.0", "react-native-worklets": "0.5.1" }, @@ -3153,6 +3156,32 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@tanstack/query-core": { + "version": "5.90.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.16.tgz", + "integrity": "sha512-MvtWckSVufs/ja463/K4PyJeqT+HMlJWtw6PrCpywznd2NSgO3m4KwO9RqbFqGg6iDE8vVMFWMeQI4Io3eEYww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.16.tgz", + "integrity": "sha512-bpMGOmV4OPmif7TNMteU/Ehf/hoC0Kf98PDc0F4BZkFrEapRMEqI/V6YS0lyzwSV6PQpY1y4xxArUIfBW5LVxQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -4236,6 +4265,12 @@ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", "license": "MIT" }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4252,6 +4287,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", + "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -4681,7 +4727,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4907,6 +4952,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -5235,6 +5292,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -5313,7 +5379,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -5442,7 +5507,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5452,7 +5516,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5490,7 +5553,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -5503,7 +5565,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -6867,6 +6928,26 @@ "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", "license": "MIT" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/fontfaceobserver": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/fontfaceobserver/-/fontfaceobserver-2.3.0.tgz", @@ -6889,6 +6970,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/freeport-async": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/freeport-async/-/freeport-async-2.0.0.tgz", @@ -6999,7 +7096,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -7042,7 +7138,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -7183,7 +7278,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7253,7 +7347,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -7266,7 +7359,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -8785,7 +8877,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -10027,7 +10118,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -10039,7 +10129,12 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, "node_modules/punycode": { @@ -10309,6 +10404,61 @@ "react-native": "*" } }, + "node_modules/react-native-vector-icons": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-10.3.0.tgz", + "integrity": "sha512-IFQ0RE57819hOUdFvgK4FowM5aMXg7C7XKsuGLevqXkkIJatc3QopN0wYrb2IrzUgmdpfP+QVIbI3S6h7M0btw==", + "deprecated": "react-native-vector-icons package has moved to a new model of per-icon-family packages. See the https://github.com/oblador/react-native-vector-icons/blob/master/MIGRATION.md on how to migrate", + "license": "MIT", + "dependencies": { + "prop-types": "^15.7.2", + "yargs": "^16.1.1" + }, + "bin": { + "fa-upgrade.sh": "bin/fa-upgrade.sh", + "fa5-upgrade": "bin/fa5-upgrade.sh", + "fa6-upgrade": "bin/fa6-upgrade.sh", + "generate-icon": "bin/generate-icon.js" + } + }, + "node_modules/react-native-vector-icons/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/react-native-vector-icons/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-native-vector-icons/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/react-native-web": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/react-native-web/-/react-native-web-0.21.2.tgz", diff --git a/package.json b/package.json index 03f7624..6024844 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "@react-navigation/bottom-tabs": "^7.4.0", "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", + "@tanstack/react-query": "^5.90.16", + "axios": "^1.13.2", "expo": "~54.0.30", "expo-constants": "~18.0.12", "expo-font": "~14.0.10", @@ -31,17 +33,18 @@ "react-dom": "19.1.0", "react-native": "0.81.5", "react-native-gesture-handler": "~2.28.0", - "react-native-worklets": "0.5.1", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", "react-native-screens": "~4.16.0", - "react-native-web": "~0.21.0" + "react-native-vector-icons": "^10.3.0", + "react-native-web": "~0.21.0", + "react-native-worklets": "0.5.1" }, "devDependencies": { "@types/react": "~19.1.0", - "typescript": "~5.9.2", "eslint": "^9.25.0", - "eslint-config-expo": "~10.0.0" + "eslint-config-expo": "~10.0.0", + "typescript": "~5.9.2" }, "private": true }