commit fe9dd37c826b79f7482e6d8164ec8ddb80cc2319 Author: Izan Gil <66965250+SrIzan10@users.noreply.github.com> Date: Thu Oct 31 23:43:20 2024 +0100 init 1 diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..eee0782 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +/.expo +node_modules diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..74e1934 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,7 @@ +module.exports = { + root: true, + extends: ['expo', 'eslint:recommended', 'universe/native'], + rules: { + 'react-hooks/exhaustive-deps': 'warn', + }, +} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..833dcff --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,89 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: 'CodeQL' + +on: + push: + branches: ['main'] + pull_request: + branches: ['main'] + schedule: + - cron: '23 19 * * 0' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} + permissions: + # required for all workflows + security-events: write + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: '/language:${{matrix.language}}' diff --git a/.github/workflows/eas-build.yml b/.github/workflows/eas-build.yml new file mode 100644 index 0000000..f945678 --- /dev/null +++ b/.github/workflows/eas-build.yml @@ -0,0 +1,27 @@ +name: EAS Build + +on: + workflow_dispatch: + # push: + # branches: + # - main + +jobs: + build: + name: Install and build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: npm + - name: Setup Expo and EAS + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + - name: Install dependencies + run: npm ci + - name: Build on EAS + run: eas build --platform all --non-interactive --no-wait diff --git a/.github/workflows/eas-reviews.yml b/.github/workflows/eas-reviews.yml new file mode 100644 index 0000000..96bb4c2 --- /dev/null +++ b/.github/workflows/eas-reviews.yml @@ -0,0 +1,36 @@ +name: EAS Review + +on: + workflow_dispatch: + # pull_request: + # branches: + # - main + +jobs: + preview: + runs-on: ubuntu-latest + steps: + - name: 🏗 Setup repo + uses: actions/checkout@v4 + + - name: 🏗 Setup Node + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: yarn + + - name: 🏗 Setup EAS + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + + - name: 📦 Install dependencies + run: yarn install + + - name: 🚀 Create preview + uses: expo/expo-github-action/preview@v8 + with: + # `github.event.pull_request.head.ref` is only available on `pull_request` triggers. + # Use your own, or keep the automatically infered branch name from `--auto`, when using different triggers. + command: eas update --auto --branch ${{ github.event.pull_request.head.ref }} diff --git a/.github/workflows/eas-update.yml b/.github/workflows/eas-update.yml new file mode 100644 index 0000000..17003c2 --- /dev/null +++ b/.github/workflows/eas-update.yml @@ -0,0 +1,32 @@ +name: EAS Update + +on: + workflow_dispatch: + # push: + # branches: + # - main + +jobs: + update: + runs-on: ubuntu-latest + steps: + - name: 🏗 Setup repo + uses: actions/checkout@v4 + + - name: 🏗 Setup Node + uses: actions/setup-node@v4 + with: + node-version: 18.x + cache: yarn + + - name: 🏗 Setup EAS + uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + + - name: 📦 Install dependencies + run: yarn install + + - name: 🚀 Create update + run: eas update --auto --non-interactive diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 0000000..da038f5 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,44 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# ESLint is a tool for identifying and reporting on patterns +# found in ECMAScript/JavaScript code. +# More details at https://github.com/eslint/eslint +# and https://eslint.org + +name: ESLint + +on: + push: + branches: ['main'] + pull_request: + # The branches below must be a subset of the branches above + branches: ['main'] + +jobs: + eslint: + name: Run eslint scanning + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js environment + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: | + npm ci + npm install eslint + + - name: Run ESLint + run: npx eslint . + --config .eslintrc.js + --ext .js,.jsx,.ts,.tsx diff --git a/.github/workflows/prettier.yml b/.github/workflows/prettier.yml new file mode 100644 index 0000000..cf2987e --- /dev/null +++ b/.github/workflows/prettier.yml @@ -0,0 +1,39 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# Prettier is an opinionated code formatter. +# It enforces a consistent style by parsing your code and re-printing it with its own rules +# that take the maximum line length into account, wrapping code when necessary. +# More details at https://github.com/prettier/prettier +# and https://prttier.io + +name: Prettier + +on: + push: + branches: ['main'] + pull_request: + # The branches below must be a subset of the branches above + branches: ['main'] + +jobs: + eslint: + name: Run prettier checking + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js environment + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Run Prettier check + run: npx prettier --check . diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ffb8b3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,76 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js +.yarn/install-state.gz + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb +# The following patterns were generated by expo-cli + +expo-env.d.ts +# @end expo-cli \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..fbacd80 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "semi": false, + "tabWidth": 2, + "useTabs": false, + "trailingComma": "all", + "singleQuote": true, + "endOfLine": "auto" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..de17410 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Yousef Abu Shanab + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0a60626 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# expo-react-native-paper + +[![EAS Build](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-build.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-build.yml) +[![EAS Review](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-reviews.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-reviews.yml) +[![EAS Update](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-update.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-update.yml) +[![CodeQL](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/codeql.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/codeql.yml) +[![ESLint](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eslint.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eslint.yml) +[![Prettier](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/prettier.yml/badge.svg)](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/prettier.yml) + +This repository is a template for React Native Expo applications. It integrates Expo Router and React Native Paper. It also demonstrates how to use Github Actions for linting and formatting. + +## Features + +- Expo +- Expo Router +- Material Design V3 +- Light & Dark modes with custom themes +- Cross Platform +- LTR and RTL support +- Multi lingual (`ar`, `en` and `tr`) +- CI/CD. For more info checkout this [page](https://github.com/expo/expo-github-action/tree/main) + +## Platforms + +- Web +- IOS +- Android + +## Screenshots + +![Home Screen (Tabs)](./screenshots/home-default-light.png) +![Profile Screen (Tabs)](./screenshots/profile-teal-dark.png) +![Settings Screen (Tabs)](./screenshots/settings-lime-light.png) +![Modal Screen (Stack)](./screenshots/modal-light-red.png) +![Search Screen (Stack)](./screenshots/search-orange-dark.png) +![Login Screen (Stack)](./screenshots/login-violet-light.png) +![Signup Screen (Stack)](./screenshots/signup-green-dark.png) +![Home Screen (Drawer)](./screenshots/home-blue-dark.png) +![Profile Screen (Drawer)](./screenshots/profile-olive-light.png) +![Settings Screen (Drawer)](./screenshots/settings-violet-light.png) + +## Getting Started + +These instructions will get you a copy of the project up and running on your local machine for development and testing purposes. + +### Prerequisites + +- Node.js +- npm + +### Installation + +Clone the repo: + +```bash +git clone https://github.com/youzarsiph/expo-react-native-paper.git +``` + +Open `package.json` and update the `name` field to match your app's name: + +```jsonc +{ + // Change the following line + "name": "expo-react-native-paper", + "main": "expo-router/entry", + "version": "1.0.0", + ... +} + +``` + +Install dependencies: + +```bash +npm install +``` + +Run the app: + +```bash +npm start +``` + +## Built With + +- TypeScript +- React +- React Native +- Expo +- Expo Router +- React Native Paper + +## Contributing + +Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated. + +1. Fork the Project +2. Create your Feature Branch (git checkout -b feature/AmazingFeature) +3. Commit your Changes (git commit -m 'Add some AmazingFeature') +4. Push to the Branch (git push origin feature/AmazingFeature) +5. Open a Pull Request + +## License + +Distributed under the MIT License. See LICENSE for more information. diff --git a/app.json b/app.json new file mode 100644 index 0000000..8bf3b10 --- /dev/null +++ b/app.json @@ -0,0 +1,43 @@ +{ + "expo": { + "name": "expo-react-native-paper", + "slug": "expo-react-native-paper", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "myapp", + "userInterfaceStyle": "automatic", + "splash": { + "image": "./assets/images/splash.png", + "resizeMode": "contain", + "backgroundColor": "#ffffff" + }, + "assetBundlePatterns": ["**/*"], + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "static", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + "expo-router", + "expo-secure-store", + "expo-font", + "expo-localization" + ], + "experiments": { + "typedRoutes": true + }, + "extra": { + "supportsRTL": true + } + } +} diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx new file mode 100644 index 0000000..f038d7f --- /dev/null +++ b/app/(auth)/_layout.tsx @@ -0,0 +1,18 @@ +import { Stack } from 'expo-router' + +import Locales from '@/lib/locales' +import { StackHeader } from '@/lib/ui' + +const Layout = () => ( + , + }} + > + + + +) + +export default Layout diff --git a/app/(auth)/login.tsx b/app/(auth)/login.tsx new file mode 100644 index 0000000..45e8f12 --- /dev/null +++ b/app/(auth)/login.tsx @@ -0,0 +1,107 @@ +import { Image } from 'expo-image' +import { router } from 'expo-router' +import { Formik } from 'formik' +import React from 'react' +import { + Button, + Surface, + TextInput, + HelperText, + Text, +} from 'react-native-paper' +import * as Yup from 'yup' + +import { styles } from '@/lib/ui' + +const Login = () => ( + + Logo + + + Welcome to ERNP + + + We're excited to have you back. Please log in to continue. + + + console.log(values)} + validationSchema={Yup.object().shape({ + username: Yup.string() + .min(3, 'Too Short!') + .max(64, 'Too Long!') + .required('Please enter a username.'), + password: Yup.string() + .min(8, 'Too Short! must be at least 8 characters.') + .max(64, 'Too Long!') + .matches( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])/, + 'Must 1 uppercase, 1 lowercase, 1 number and 1 special case character', + ) + .required('Please enter a password'), + })} + > + {({ handleChange, handleBlur, handleSubmit, values, errors }) => ( + <> + + + + {errors.username} + + + + + + + {errors.password} + + + + + + )} + + + + +) + +export default Login diff --git a/app/(auth)/signup.tsx b/app/(auth)/signup.tsx new file mode 100644 index 0000000..6c1ff0d --- /dev/null +++ b/app/(auth)/signup.tsx @@ -0,0 +1,176 @@ +import { Image } from 'expo-image' +import { router } from 'expo-router' +import { Formik } from 'formik' +import React from 'react' +import { ScrollView } from 'react-native' +import { + Button, + Surface, + TextInput, + HelperText, + Text, +} from 'react-native-paper' +import * as Yup from 'yup' + +import { styles } from '@/lib/ui' + +const SignUp = () => ( + + + Logo + + + Join ERNP Today! + + + We're thrilled to have you on board. Let's get you set up. + + + console.log(values)} + validationSchema={Yup.object().shape({ + username: Yup.string() + .min(3, 'Too Short!') + .max(64, 'Too Long!') + .required('Please enter a username.'), + password: Yup.string() + .min(8, 'Too Short! must be at least 8 characters.') + .max(64, 'Too Long!') + .matches( + /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*])/, + 'Must 1 uppercase, 1 lowercase, 1 number and 1 special case character', + ) + .required('Please enter a password'), + email: Yup.string().email().required('Please enter an email.'), + firstName: Yup.string() + .min(3, 'Too Short!') + .max(64, 'Too Long!') + .required('Please enter a first name.'), + lastName: Yup.string() + .min(3, 'Too Short!') + .max(64, 'Too Long!') + .required('Please enter a last name.'), + })} + > + {({ handleChange, handleBlur, handleSubmit, values, errors }) => ( + <> + + + + {errors.username} + + + + + + + {errors.password} + + + + + + + {errors.email} + + + + + + + {errors.firstName} + + + + + + + {errors.lastName} + + + + + + )} + + + + + +) + +export default SignUp diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx new file mode 100644 index 0000000..760dd58 --- /dev/null +++ b/app/(tabs)/_layout.tsx @@ -0,0 +1,126 @@ +import { MaterialCommunityIcons } from '@expo/vector-icons' +import { Tabs, router } from 'expo-router' +import React from 'react' +import { Appbar, Menu, Tooltip } from 'react-native-paper' + +import Locales from '@/lib/locales' +import { TabBar, TabsHeader } from '@/lib/ui' + +const TabLayout = () => { + const [visible, setVisible] = React.useState(false) + + return ( + } + screenOptions={{ + tabBarHideOnKeyboard: true, + header: (props) => , + }} + > + ( + <> + + router.push('/search')} + /> + + setVisible(false)} + anchor={ + + setVisible(true)} + /> + + } + > + router.push('/(tabs)/settings')} + /> + router.push('/modal')} + /> + router.push('/drawer')} + /> + + + ), + tabBarIcon: (props) => ( + + ), + }} + /> + ( + <> + + router.push('/search')} + /> + + + router.push('/(tabs)/settings')} + /> + + + ), + tabBarIcon: (props) => ( + + ), + }} + /> + ( + + router.push('/drawer')} + /> + + ), + tabBarIcon: (props) => ( + + ), + }} + /> + + ) +} + +export default TabLayout diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx new file mode 100644 index 0000000..74042d9 --- /dev/null +++ b/app/(tabs)/index.tsx @@ -0,0 +1,13 @@ +import React from 'react' +import { Surface } from 'react-native-paper' + +import Locales from '@/lib/locales' +import { ScreenInfo, styles } from '@/lib/ui' + +const TabsHome = () => ( + + + +) + +export default TabsHome diff --git a/app/(tabs)/profile.tsx b/app/(tabs)/profile.tsx new file mode 100644 index 0000000..bf89a9d --- /dev/null +++ b/app/(tabs)/profile.tsx @@ -0,0 +1,34 @@ +import { router } from 'expo-router' +import React from 'react' +import { Button, Surface } from 'react-native-paper' + +import Locales from '@/lib/locales' +import { ScreenInfo, styles } from '@/lib/ui' + +const Profile = () => ( + + + + + + + + + +) + +export default Profile diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx new file mode 100644 index 0000000..9acfd02 --- /dev/null +++ b/app/(tabs)/settings.tsx @@ -0,0 +1,310 @@ +import * as SecureStore from 'expo-secure-store' +import React from 'react' +import { Platform, useColorScheme } from 'react-native' +import { + Surface, + List, + Menu, + Button, + IconButton, + Snackbar, + Icon, +} from 'react-native-paper' + +import Locales from '@/lib/locales' +import { Color, Language, Setting } from '@/lib/types' +import { Colors, LoadingIndicator, ScreenInfo, styles } from '@/lib/ui' +import { Languages } from '@/lib/utils' + +const Settings = () => { + const colorScheme = useColorScheme() + const [loading, setLoading] = React.useState(false) + const [message, setMessage] = React.useState({ visible: false, content: '' }) + const [settings, setSettings] = React.useState({ + color: 'default', + language: 'auto', + theme: 'auto', + }) + const [display, setDisplay] = React.useState({ + color: false, + language: false, + theme: false, + }) + + React.useEffect(() => { + setLoading(true) + + if (Platform.OS !== 'web') { + SecureStore.getItemAsync('settings') + .then((result) => + setSettings(JSON.parse(result ?? JSON.stringify(settings))), + ) + .catch((res) => + setMessage({ + visible: true, + content: res.message, + }), + ) + } + + setLoading(false) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + const themeColors = + Colors[ + settings.theme === 'auto' ? (colorScheme ?? 'light') : settings.theme + ] + + return ( + + {loading ? ( + + ) : ( + + + } + > + } + right={(props) => ( + + setDisplay({ ...display, language: false }) + } + anchor={ + + setDisplay({ ...display, language: true }) + } + /> + } + > + { + setSettings({ ...settings, language: 'auto' }) + setDisplay({ ...display, language: false }) + }} + /> + {Object.entries(Languages).map((lang) => ( + { + setSettings({ + ...settings, + language: lang[0] as Language, + }) + setDisplay({ ...display, language: false }) + }} + /> + ))} + + )} + /> + ( + + )} + right={(props) => ( + setDisplay({ ...display, theme: false })} + anchor={ + setDisplay({ ...display, theme: true })} + /> + } + > + { + setSettings({ ...settings, theme: 'auto' }) + setDisplay({ ...display, theme: false }) + }} + /> + { + setSettings({ ...settings, theme: 'light' }) + setDisplay({ ...display, theme: false }) + }} + /> + { + setSettings({ ...settings, theme: 'dark' }) + setDisplay({ ...display, theme: false }) + }} + /> + + )} + /> + ( + + )} + right={(props) => ( + setDisplay({ ...display, color: false })} + anchor={ + setDisplay({ ...display, color: true })} + /> + } + > + {Object.keys(Colors.light).map((color) => ( + + + + + + { + setSettings({ + ...settings, + color: color as Color, + }) + setDisplay({ ...display, color: false }) + }} + /> + + ))} + + )} + /> + + + + )} + + + + + + + + setMessage({ ...message, visible: false })} + onIconPress={() => setMessage({ ...message, visible: false })} + > + {message.content} + + + ) +} + +export default Settings diff --git a/app/+html.tsx b/app/+html.tsx new file mode 100644 index 0000000..69c20c3 --- /dev/null +++ b/app/+html.tsx @@ -0,0 +1,42 @@ +import { ScrollViewStyleReset } from 'expo-router/html' +import React from 'react' + +// This file is web-only and used to configure the root HTML for every +// web page during static rendering. +// The contents of this function only run in Node.js environments and +// do not have access to the DOM or browser APIs. +export default function Root({ children }: { children: React.ReactNode }) { + return ( + + + + + + + {/* + Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. + However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. + */} + + + {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} +