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
+
+[](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-build.yml)
+[](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-reviews.yml)
+[](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eas-update.yml)
+[](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/codeql.yml)
+[](https://github.com/youzarsiph/expo-react-native-paper/actions/workflows/eslint.yml)
+[](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
+
+
+
+
+
+
+
+
+
+
+
+
+## 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 = () => (
+
+
+
+
+ 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 = () => (
+
+
+
+
+
+ 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')}
+ />
+
+
+ >
+ ),
+ 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) => (
+
+ )}
+ />
+ (
+
+ )}
+ right={(props) => (
+
+ )}
+ />
+ (
+
+ )}
+ right={(props) => (
+
+ )}
+ />
+
+
+
+ )}
+
+
+
+
+
+
+
+ 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. */}
+
+ {/* Add any additional elements that you want globally available on web... */}
+
+ {children}
+
+ )
+}
+
+const responsiveBackground = `
+body {
+ background-color: #fff;
+}
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #000;
+ }
+}`
diff --git a/app/+not-found.tsx b/app/+not-found.tsx
new file mode 100644
index 0000000..7f8eac0
--- /dev/null
+++ b/app/+not-found.tsx
@@ -0,0 +1,22 @@
+import { Link, Stack } from 'expo-router'
+import React from 'react'
+import { Surface, Text } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import { styles } from '@/lib/ui'
+
+const NotFound = () => (
+
+
+
+ {Locales.t('titleNotFound')}
+
+ {Locales.t('screen404')}
+
+
+ {Locales.t('goHome')}
+
+
+)
+
+export default NotFound
diff --git a/app/_layout.tsx b/app/_layout.tsx
new file mode 100644
index 0000000..099ad9d
--- /dev/null
+++ b/app/_layout.tsx
@@ -0,0 +1,122 @@
+import { MaterialCommunityIcons } from '@expo/vector-icons'
+import {
+ useFonts,
+ JetBrainsMono_400Regular,
+} from '@expo-google-fonts/jetbrains-mono'
+import { NotoSans_400Regular } from '@expo-google-fonts/noto-sans'
+import * as Localization from 'expo-localization'
+import { SplashScreen, Stack } from 'expo-router'
+import * as SecureStore from 'expo-secure-store'
+import React from 'react'
+import { Platform, useColorScheme } from 'react-native'
+import { PaperProvider } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import { Setting } from '@/lib/types'
+import { StackHeader, Themes } from '@/lib/ui'
+
+export {
+ // Catch any errors thrown by the Layout component.
+ ErrorBoundary,
+} from 'expo-router'
+
+export const unstable_settings = {
+ // Ensure that reloading on `/modal` keeps a back button present.
+ initialRouteName: '(tabs)',
+}
+
+// Prevent the splash screen from auto-hiding before asset loading is complete.
+SplashScreen.preventAutoHideAsync()
+
+const RootLayout = () => {
+ const [loaded, error] = useFonts({
+ NotoSans_400Regular,
+ JetBrainsMono_400Regular,
+ ...MaterialCommunityIcons.font,
+ })
+
+ // Expo Router uses Error Boundaries to catch errors in the navigation tree.
+ React.useEffect(() => {
+ if (error) throw error
+ }, [error])
+
+ React.useEffect(() => {
+ if (loaded) {
+ SplashScreen.hideAsync()
+ }
+ }, [loaded])
+
+ if (!loaded) {
+ return null
+ }
+
+ return
+}
+
+const RootLayoutNav = () => {
+ const colorScheme = useColorScheme()
+ const [settings, setSettings] = React.useState({
+ theme: 'auto',
+ color: 'default',
+ language: 'auto',
+ })
+
+ // Load settings from the device
+ React.useEffect(() => {
+ if (Platform.OS !== 'web') {
+ SecureStore.getItemAsync('settings').then((result) => {
+ if (result === null) {
+ SecureStore.setItemAsync('settings', JSON.stringify(settings)).then(
+ (res) => console.log(res),
+ )
+ }
+
+ setSettings(JSON.parse(result ?? JSON.stringify(settings)))
+ })
+ } else {
+ setSettings({ ...settings, theme: colorScheme ?? 'light' })
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ React.useEffect(() => {
+ if (settings.language === 'auto') {
+ Locales.locale = Localization.getLocales()[0].languageCode ?? 'en'
+ } else {
+ Locales.locale = settings.language
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [])
+
+ return (
+
+ (
+
+ ),
+ }}
+ >
+
+
+
+
+
+
+
+ )
+}
+
+export default RootLayout
diff --git a/app/drawer/_layout.tsx b/app/drawer/_layout.tsx
new file mode 100644
index 0000000..85ee08f
--- /dev/null
+++ b/app/drawer/_layout.tsx
@@ -0,0 +1,124 @@
+import { router } from 'expo-router'
+import { Drawer } from 'expo-router/drawer'
+import React from 'react'
+import { GestureHandlerRootView } from 'react-native-gesture-handler'
+import { Appbar, Menu, Tooltip, useTheme } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import { DrawerContent, DrawerHeader } from '@/lib/ui'
+
+const DrawerLayout = () => {
+ const theme = useTheme()
+ const [visible, setVisible] = React.useState(false)
+
+ return (
+
+ (
+
+ )}
+ screenOptions={{
+ drawerStyle: {
+ backgroundColor: theme.colors.background,
+ paddingTop: 32,
+ },
+ header: (props) => (
+
+ ),
+ }}
+ >
+ (
+ <>
+
+ router.push('/search')}
+ />
+
+
+ >
+ ),
+ }}
+ />
+ (
+ <>
+
+ router.push('/search')}
+ />
+
+
+ router.push('/(tabs)/settings')}
+ />
+
+ >
+ ),
+ }}
+ />
+ (
+
+ router.push('/modal')}
+ />
+
+ ),
+ }}
+ />
+
+
+ )
+}
+
+export default DrawerLayout
diff --git a/app/drawer/index.tsx b/app/drawer/index.tsx
new file mode 100644
index 0000000..eb5ef99
--- /dev/null
+++ b/app/drawer/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 DrawerHome = () => (
+
+
+
+)
+
+export default DrawerHome
diff --git a/app/drawer/profile.tsx b/app/drawer/profile.tsx
new file mode 100644
index 0000000..bf89a9d
--- /dev/null
+++ b/app/drawer/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/drawer/settings.tsx b/app/drawer/settings.tsx
new file mode 100644
index 0000000..9acfd02
--- /dev/null
+++ b/app/drawer/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) => (
+
+ )}
+ />
+ (
+
+ )}
+ right={(props) => (
+
+ )}
+ />
+ (
+
+ )}
+ right={(props) => (
+
+ )}
+ />
+
+
+
+ )}
+
+
+
+
+
+
+
+ setMessage({ ...message, visible: false })}
+ onIconPress={() => setMessage({ ...message, visible: false })}
+ >
+ {message.content}
+
+
+ )
+}
+
+export default Settings
diff --git a/app/modal.tsx b/app/modal.tsx
new file mode 100644
index 0000000..ac7ef77
--- /dev/null
+++ b/app/modal.tsx
@@ -0,0 +1,18 @@
+import { StatusBar } from 'expo-status-bar'
+import React from 'react'
+import { Platform } from 'react-native'
+import { Surface } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import { ScreenInfo, styles } from '@/lib/ui'
+
+const Modal = () => (
+
+
+
+ {/* Use a light status bar on iOS to account for the black space above the modal */}
+
+
+)
+
+export default Modal
diff --git a/app/search.tsx b/app/search.tsx
new file mode 100644
index 0000000..aa337dc
--- /dev/null
+++ b/app/search.tsx
@@ -0,0 +1,39 @@
+import React from 'react'
+import { Searchbar, Surface } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import { ScreenInfo, styles } from '@/lib/ui'
+
+const Search = () => {
+ const [query, setQuery] = React.useState('')
+ const [loading, setLoading] = React.useState(false)
+
+ // Search logic
+ React.useEffect(() => {
+ if (query !== '') {
+ setLoading(true)
+ }
+
+ setTimeout(() => {
+ setLoading(false)
+ }, 1000)
+ }, [query])
+
+ return (
+
+ setQuery(v)}
+ placeholder="Type here to search..."
+ style={{ marginTop: 16, marginHorizontal: 16 }}
+ />
+
+
+
+
+
+ )
+}
+
+export default Search
diff --git a/assets/images/adaptive-icon.png b/assets/images/adaptive-icon.png
new file mode 100644
index 0000000..03d6f6b
Binary files /dev/null and b/assets/images/adaptive-icon.png differ
diff --git a/assets/images/favicon.png b/assets/images/favicon.png
new file mode 100644
index 0000000..e75f697
Binary files /dev/null and b/assets/images/favicon.png differ
diff --git a/assets/images/icon.png b/assets/images/icon.png
new file mode 100644
index 0000000..a0b1526
Binary files /dev/null and b/assets/images/icon.png differ
diff --git a/assets/images/splash.png b/assets/images/splash.png
new file mode 100644
index 0000000..0e89705
Binary files /dev/null and b/assets/images/splash.png differ
diff --git a/babel.config.ts b/babel.config.ts
new file mode 100644
index 0000000..dd0cb76
--- /dev/null
+++ b/babel.config.ts
@@ -0,0 +1,6 @@
+module.exports = function (api: { cache: (arg0: boolean) => void }) {
+ api.cache(true)
+ return {
+ presets: ['babel-preset-expo'],
+ }
+}
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..ddf73ba
Binary files /dev/null and b/bun.lockb differ
diff --git a/lib/locales/ar.ts b/lib/locales/ar.ts
new file mode 100644
index 0000000..61e550f
--- /dev/null
+++ b/lib/locales/ar.ts
@@ -0,0 +1,50 @@
+/**
+ * Arabic Translations
+ */
+
+const Arabic = {
+ login: 'تسجيل الدخول',
+ signup: 'إنشاء حساب',
+ profile: 'الملف الشخصي',
+ options: 'الخيارات',
+ search: 'البحث',
+ stackNav: 'التنقل المكدس',
+ drawerNav: 'التنقل الدرج',
+ appearance: 'المظهر',
+ language: 'اللغة',
+ changeLanguage: 'تغيير لغة التطبيق',
+ system: 'النظام',
+ mode: 'الوضع',
+ changeMode: 'التبديل بين الوضع الفاتح والوضع الداكن',
+ lightMode: 'فاتح',
+ darkMode: 'داكن',
+ color: 'اللون',
+ changeColor: 'تغيير لون السمة',
+ changeScreenCode:
+ 'قم بتغيير أي نص، احفظ الملف، وسيتم تحديث التطبيق الخاص بك تلقائيًا',
+ goHome: 'الذهاب إلى الشاشة الرئيسية',
+ openScreenCode: 'افتح الكود لهذه الشاشة',
+ save: 'حفظ',
+ screen404: 'هذه الشاشة غير موجودة',
+ titleHome: 'الرئيسية',
+ titleModal: 'مشروط',
+ titleNotFound: 'لم يتم العثور',
+ titleSettings: 'الإعدادات',
+ restartApp: 'أعد تشغيل التطبيق لتطبيق التغييرات',
+ notAvailable: 'Expo SecureStore غير متوفر للويب',
+ adaptive: 'تلقائي',
+ default: 'افتراضي',
+ orange: 'برتقالي',
+ red: 'أحمر',
+ violet: 'بنفسجي',
+ indigo: 'أزرق داكن',
+ blue: 'أزرق',
+ teal: 'أزرق فاتح',
+ cyan: 'سماوي',
+ green: 'أخضر',
+ lime: 'ليموني',
+ olive: 'زيتوني',
+ brown: 'بني',
+}
+
+export default Arabic
diff --git a/lib/locales/en.ts b/lib/locales/en.ts
new file mode 100644
index 0000000..9c4e660
--- /dev/null
+++ b/lib/locales/en.ts
@@ -0,0 +1,50 @@
+/**
+ * English Translations
+ */
+
+const English = {
+ login: 'Log in',
+ signup: 'Sign up',
+ profile: 'Profile',
+ options: 'Options',
+ search: 'Search',
+ stackNav: 'Stack Navigation',
+ drawerNav: 'Drawer Navigation',
+ appearance: 'Appearance',
+ language: 'Language',
+ changeLanguage: "Change app's language",
+ system: 'System',
+ mode: 'Mode',
+ changeMode: 'Switch between light and dark mode',
+ lightMode: 'Light',
+ darkMode: 'Dark',
+ color: 'Color',
+ changeColor: 'Change theme color',
+ changeScreenCode:
+ 'Change any of the text, save the file, and your app will automatically update',
+ goHome: 'Go to home screen',
+ openScreenCode: 'Open up the code for this screen',
+ save: 'Save',
+ screen404: "This screen doesn't exist",
+ titleHome: 'Home',
+ titleModal: 'Modal',
+ titleNotFound: 'Not Found',
+ titleSettings: 'Settings',
+ restartApp: 'Restart the app to apply changes',
+ notAvailable: 'Expo SecureStore is not available for web',
+ adaptive: 'adaptive',
+ default: 'default',
+ orange: 'orange',
+ red: 'red',
+ violet: 'violet',
+ indigo: 'indigo',
+ blue: 'blue',
+ teal: 'teal',
+ cyan: 'cyan',
+ green: 'green',
+ lime: 'lime',
+ olive: 'olive',
+ brown: 'brown',
+}
+
+export default English
diff --git a/lib/locales/index.ts b/lib/locales/index.ts
new file mode 100644
index 0000000..8387869
--- /dev/null
+++ b/lib/locales/index.ts
@@ -0,0 +1,19 @@
+/**
+ * Locales
+ */
+
+import { I18n } from 'i18n-js'
+
+import Arabic from '@/lib/locales/ar'
+import English from '@/lib/locales/en'
+import Turkish from '@/lib/locales/tr'
+
+const Locales = new I18n({
+ ar: Arabic,
+ en: English,
+ tr: Turkish,
+})
+
+Locales.enableFallback = true
+
+export default Locales
diff --git a/lib/locales/tr.ts b/lib/locales/tr.ts
new file mode 100644
index 0000000..af9601f
--- /dev/null
+++ b/lib/locales/tr.ts
@@ -0,0 +1,48 @@
+/**
+ * Turkish Translations
+ */
+
+const Turkish = {
+ login: 'Giriş',
+ signup: 'Hesap aç',
+ profile: 'Profil',
+ search: 'Ara',
+ drawerNav: 'Çekmece Navigasyon',
+ appearance: 'Görünüm',
+ language: 'Dil',
+ changeLanguage: 'Uygulamanın dilini değiştir',
+ system: 'Sistem',
+ mode: 'Mod',
+ changeMode: 'Aydınlık ve karanlık mod arasında geçiş yap',
+ lightMode: 'Aydınlık',
+ darkMode: 'Karanlık',
+ color: 'Renk',
+ changeColor: 'Tema rengini değiştir',
+ changeScreenCode:
+ 'Herhangi bir metni değiştirin, dosyayı kaydedin ve uygulamanız otomatik olarak güncellenecektir',
+ goHome: 'Ana ekrana git',
+ openScreenCode: 'Bu ekranın kodunu aç',
+ save: 'Kaydet',
+ screen404: 'Bu ekran mevcut değil',
+ titleHome: 'Ana Sayfa',
+ titleModal: 'Modal',
+ titleNotFound: 'Bulunamadı',
+ titleSettings: 'Ayarlar',
+ restartApp: 'Değişiklikleri uygulamak için uygulamayı yeniden başlatın',
+ notAvailable: 'Expo SecureStore web için kullanılabilir değil',
+ adaptive: 'adaptive',
+ default: 'varsayılan',
+ orange: 'turuncu',
+ red: 'kırmızı',
+ violet: 'mor',
+ indigo: 'lacivert',
+ blue: 'mavi',
+ teal: 'turkuaz',
+ cyan: 'gökyüzü mavi',
+ green: 'yeşil',
+ lime: 'limon yeşili',
+ olive: 'zeytin yeşili',
+ brown: 'kahverengi',
+}
+
+export default Turkish
diff --git a/lib/types/Color.ts b/lib/types/Color.ts
new file mode 100644
index 0000000..c58d2be
--- /dev/null
+++ b/lib/types/Color.ts
@@ -0,0 +1,5 @@
+import { Colors } from '@/lib/ui'
+
+type Color = keyof typeof Colors.light
+
+export default Color
diff --git a/lib/types/Language.ts b/lib/types/Language.ts
new file mode 100644
index 0000000..cb4f6d8
--- /dev/null
+++ b/lib/types/Language.ts
@@ -0,0 +1,5 @@
+import { Languages } from '@/lib/utils'
+
+type Language = keyof typeof Languages
+
+export default Language
diff --git a/lib/types/Setting.ts b/lib/types/Setting.ts
new file mode 100644
index 0000000..05332f8
--- /dev/null
+++ b/lib/types/Setting.ts
@@ -0,0 +1,9 @@
+import { Color, Language } from '@/lib/types'
+
+type Setting = {
+ color: Color
+ theme: 'light' | 'dark' | 'auto'
+ language: Language | 'auto'
+}
+
+export default Setting
diff --git a/lib/types/index.ts b/lib/types/index.ts
new file mode 100644
index 0000000..facd084
--- /dev/null
+++ b/lib/types/index.ts
@@ -0,0 +1,9 @@
+/**
+ * Types
+ */
+
+import Color from '@/lib/types/Color'
+import Language from '@/lib/types/Language'
+import Setting from '@/lib/types/Setting'
+
+export type { Color, Language, Setting }
diff --git a/lib/ui/components/DrawerContent.tsx b/lib/ui/components/DrawerContent.tsx
new file mode 100644
index 0000000..ca95b6d
--- /dev/null
+++ b/lib/ui/components/DrawerContent.tsx
@@ -0,0 +1,40 @@
+import { DrawerContentComponentProps } from '@react-navigation/drawer'
+import { router } from 'expo-router'
+import React from 'react'
+import { Drawer, DrawerSectionProps } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+
+interface DrawerContentProps extends DrawerSectionProps {
+ navProps: DrawerContentComponentProps
+}
+
+const DrawerContent = (props: DrawerContentProps) => (
+
+ router.replace('/')}
+ />
+ router.push('/drawer')}
+ />
+ router.push('/drawer/profile')}
+ />
+ router.push('/drawer/settings')}
+ />
+
+)
+
+export default DrawerContent
diff --git a/lib/ui/components/DrawerHeader.tsx b/lib/ui/components/DrawerHeader.tsx
new file mode 100644
index 0000000..8822aab
--- /dev/null
+++ b/lib/ui/components/DrawerHeader.tsx
@@ -0,0 +1,27 @@
+import { DrawerHeaderProps as BaseProps } from '@react-navigation/drawer'
+import { getHeaderTitle } from '@react-navigation/elements'
+import React from 'react'
+import { Appbar, AppbarProps } from 'react-native-paper'
+
+interface DrawerHeaderProps extends AppbarProps {
+ navProps: BaseProps
+}
+
+const DrawerHeader = (props: DrawerHeaderProps) => (
+
+ props.navProps.navigation.openDrawer()}
+ />
+
+
+
+ {props.navProps.options.headerRight
+ ? props.navProps.options.headerRight({})
+ : undefined}
+
+)
+
+export default DrawerHeader
diff --git a/lib/ui/components/GradientBackground.tsx b/lib/ui/components/GradientBackground.tsx
new file mode 100644
index 0000000..9f44573
--- /dev/null
+++ b/lib/ui/components/GradientBackground.tsx
@@ -0,0 +1,30 @@
+import { Canvas, LinearGradient, Rect, vec } from '@shopify/react-native-skia'
+import { Platform, useWindowDimensions } from 'react-native'
+import { useTheme } from 'react-native-paper'
+
+const GradientBackground = (props: { height?: 'full' }) => {
+ const theme = useTheme()
+ const { height, width } = useWindowDimensions()
+
+ return Platform.OS !== 'web' ? (
+
+ ) : undefined
+}
+
+export default GradientBackground
diff --git a/lib/ui/components/LoadingIndicator.tsx b/lib/ui/components/LoadingIndicator.tsx
new file mode 100644
index 0000000..4e90e80
--- /dev/null
+++ b/lib/ui/components/LoadingIndicator.tsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import { ActivityIndicator, Surface } from 'react-native-paper'
+
+const LoadingIndicator = () => (
+
+
+
+)
+
+export default LoadingIndicator
diff --git a/lib/ui/components/ScreenInfo.tsx b/lib/ui/components/ScreenInfo.tsx
new file mode 100644
index 0000000..f8690c8
--- /dev/null
+++ b/lib/ui/components/ScreenInfo.tsx
@@ -0,0 +1,24 @@
+import { Chip, Text } from 'react-native-paper'
+
+import Locales from '@/lib/locales'
+import GradientBackground from '@/lib/ui/components/GradientBackground'
+
+const ScreenInfo = (props: { title: string; path: string }) => (
+ <>
+
+
+ {props.title}
+
+ {Locales.t('openScreenCode')}
+
+
+ {props.path}
+
+
+
+ {Locales.t('changeScreenCode')}
+
+ >
+)
+
+export default ScreenInfo
diff --git a/lib/ui/components/StackHeader.tsx b/lib/ui/components/StackHeader.tsx
new file mode 100644
index 0000000..a94f126
--- /dev/null
+++ b/lib/ui/components/StackHeader.tsx
@@ -0,0 +1,34 @@
+import { getHeaderTitle } from '@react-navigation/elements'
+import { NativeStackHeaderProps } from '@react-navigation/native-stack'
+import React from 'react'
+import { Appbar, AppbarProps } from 'react-native-paper'
+
+interface StackHeaderProps extends AppbarProps {
+ navProps: NativeStackHeaderProps
+}
+
+const StackHeader = (props: StackHeaderProps) => (
+
+ {props.navProps.options.headerLeft
+ ? props.navProps.options.headerLeft({
+ canGoBack: props.navProps.navigation.canGoBack(),
+ })
+ : undefined}
+
+ {props.navProps.back ? (
+
+ ) : null}
+
+
+
+ {props.navProps.options.headerRight
+ ? props.navProps.options.headerRight({
+ canGoBack: props.navProps.navigation.canGoBack(),
+ })
+ : undefined}
+
+)
+
+export default StackHeader
diff --git a/lib/ui/components/TabBar.tsx b/lib/ui/components/TabBar.tsx
new file mode 100644
index 0000000..8e2db03
--- /dev/null
+++ b/lib/ui/components/TabBar.tsx
@@ -0,0 +1,49 @@
+import { BottomTabBarProps } from '@react-navigation/bottom-tabs'
+import { CommonActions } from '@react-navigation/native'
+import React from 'react'
+import { BottomNavigation } from 'react-native-paper'
+
+const TabBar = (props: BottomTabBarProps) => (
+ {
+ const event = props.navigation.emit({
+ type: 'tabPress',
+ target: route.key,
+ canPreventDefault: true,
+ })
+
+ if (event.defaultPrevented) {
+ preventDefault()
+ } else {
+ props.navigation.dispatch({
+ ...CommonActions.navigate(route.name, route.params),
+ target: props.state.key,
+ })
+ }
+ }}
+ renderIcon={({ route, focused, color }) => {
+ const { options } = props.descriptors[route.key]
+ if (options.tabBarIcon) {
+ return options.tabBarIcon({ focused, color, size: 24 })
+ }
+
+ return null
+ }}
+ getLabelText={({ route }) => {
+ const { options } = props.descriptors[route.key]
+ const label =
+ options.tabBarLabel !== undefined
+ ? options.tabBarLabel
+ : options.title !== undefined
+ ? options.title
+ : route.title
+
+ return label
+ }}
+ />
+)
+
+export default TabBar
diff --git a/lib/ui/components/TabsHeader.tsx b/lib/ui/components/TabsHeader.tsx
new file mode 100644
index 0000000..c139565
--- /dev/null
+++ b/lib/ui/components/TabsHeader.tsx
@@ -0,0 +1,26 @@
+import { BottomTabHeaderProps } from '@react-navigation/bottom-tabs'
+import { getHeaderTitle } from '@react-navigation/elements'
+import React from 'react'
+import { Appbar, AppbarProps } from 'react-native-paper'
+
+interface TabsHeaderProps extends AppbarProps {
+ navProps: BottomTabHeaderProps
+}
+
+const TabsHeader = (props: TabsHeaderProps) => (
+
+ {props.navProps.options.headerLeft
+ ? props.navProps.options.headerLeft({})
+ : undefined}
+
+
+
+ {props.navProps.options.headerRight
+ ? props.navProps.options.headerRight({})
+ : undefined}
+
+)
+
+export default TabsHeader
diff --git a/lib/ui/components/index.ts b/lib/ui/components/index.ts
new file mode 100644
index 0000000..8fd15e4
--- /dev/null
+++ b/lib/ui/components/index.ts
@@ -0,0 +1,23 @@
+/**
+ * Components
+ */
+
+import DrawerContent from '@/lib/ui/components/DrawerContent'
+import DrawerHeader from '@/lib/ui/components/DrawerHeader'
+import GradientBackground from '@/lib/ui/components/GradientBackground'
+import LoadingIndicator from '@/lib/ui/components/LoadingIndicator'
+import ScreenInfo from '@/lib/ui/components/ScreenInfo'
+import StackHeader from '@/lib/ui/components/StackHeader'
+import TabBar from '@/lib/ui/components/TabBar'
+import TabsHeader from '@/lib/ui/components/TabsHeader'
+
+export {
+ DrawerContent,
+ DrawerHeader,
+ GradientBackground,
+ LoadingIndicator,
+ ScreenInfo,
+ StackHeader,
+ TabBar,
+ TabsHeader,
+}
diff --git a/lib/ui/index.tsx b/lib/ui/index.tsx
new file mode 100644
index 0000000..59ffcc5
--- /dev/null
+++ b/lib/ui/index.tsx
@@ -0,0 +1,6 @@
+/**
+ * UI
+ */
+
+export * from '@/lib/ui/components'
+export * from '@/lib/ui/styles'
diff --git a/lib/ui/styles/colors.ts b/lib/ui/styles/colors.ts
new file mode 100644
index 0000000..5785651
--- /dev/null
+++ b/lib/ui/styles/colors.ts
@@ -0,0 +1,902 @@
+/**
+ * Custom colors for using with themes
+ */
+
+import { MD3DarkTheme, MD3LightTheme } from 'react-native-paper'
+
+const Colors = {
+ light: {
+ default: {
+ primary: MD3LightTheme.colors.primary,
+ onPrimary: MD3LightTheme.colors.onPrimary,
+ },
+ orange: {
+ primary: 'rgb(176, 46, 0)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(255, 219, 209)',
+ onPrimaryContainer: 'rgb(59, 9, 0)',
+ secondary: 'rgb(119, 87, 78)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(255, 219, 209)',
+ onSecondaryContainer: 'rgb(44, 21, 15)',
+ tertiary: 'rgb(108, 93, 47)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(246, 225, 166)',
+ onTertiaryContainer: 'rgb(35, 27, 0)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(32, 26, 24)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(32, 26, 24)',
+ surfaceVariant: 'rgb(245, 222, 216)',
+ onSurfaceVariant: 'rgb(83, 67, 63)',
+ outline: 'rgb(133, 115, 110)',
+ outlineVariant: 'rgb(216, 194, 188)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(54, 47, 45)',
+ inverseOnSurface: 'rgb(251, 238, 235)',
+ inversePrimary: 'rgb(255, 181, 160)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(251, 241, 242)',
+ level2: 'rgb(249, 235, 235)',
+ level3: 'rgb(246, 229, 227)',
+ level4: 'rgb(246, 226, 224)',
+ level5: 'rgb(244, 222, 219)',
+ },
+ surfaceDisabled: 'rgba(32, 26, 24, 0.12)',
+ onSurfaceDisabled: 'rgba(32, 26, 24, 0.38)',
+ backdrop: 'rgba(59, 45, 41, 0.4)',
+ },
+ red: {
+ primary: 'rgb(185, 12, 85)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(255, 217, 223)',
+ onPrimaryContainer: 'rgb(63, 0, 24)',
+ secondary: 'rgb(117, 86, 92)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(255, 217, 223)',
+ onSecondaryContainer: 'rgb(43, 21, 26)',
+ tertiary: 'rgb(122, 87, 51)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(255, 220, 189)',
+ onTertiaryContainer: 'rgb(44, 22, 0)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(32, 26, 27)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(32, 26, 27)',
+ surfaceVariant: 'rgb(243, 221, 224)',
+ onSurfaceVariant: 'rgb(82, 67, 70)',
+ outline: 'rgb(132, 115, 117)',
+ outlineVariant: 'rgb(214, 194, 196)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(53, 47, 48)',
+ inverseOnSurface: 'rgb(250, 238, 239)',
+ inversePrimary: 'rgb(255, 177, 194)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(252, 239, 247)',
+ level2: 'rgb(249, 232, 241)',
+ level3: 'rgb(247, 225, 236)',
+ level4: 'rgb(247, 222, 235)',
+ level5: 'rgb(245, 218, 231)',
+ },
+ surfaceDisabled: 'rgba(32, 26, 27, 0.12)',
+ onSurfaceDisabled: 'rgba(32, 26, 27, 0.38)',
+ backdrop: 'rgba(58, 45, 47, 0.4)',
+ },
+ violet: {
+ primary: 'rgb(140, 51, 179)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(248, 216, 255)',
+ onPrimaryContainer: 'rgb(50, 0, 71)',
+ secondary: 'rgb(105, 89, 109)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(241, 220, 244)',
+ onSecondaryContainer: 'rgb(35, 23, 40)',
+ tertiary: 'rgb(129, 82, 80)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(255, 218, 216)',
+ onTertiaryContainer: 'rgb(51, 17, 17)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(30, 27, 30)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(30, 27, 30)',
+ surfaceVariant: 'rgb(235, 223, 233)',
+ onSurfaceVariant: 'rgb(76, 68, 77)',
+ outline: 'rgb(125, 116, 125)',
+ outlineVariant: 'rgb(206, 195, 205)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(51, 47, 51)',
+ inverseOnSurface: 'rgb(246, 239, 243)',
+ inversePrimary: 'rgb(235, 178, 255)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(249, 241, 251)',
+ level2: 'rgb(246, 235, 249)',
+ level3: 'rgb(242, 229, 247)',
+ level4: 'rgb(241, 227, 246)',
+ level5: 'rgb(239, 223, 244)',
+ },
+ surfaceDisabled: 'rgba(30, 27, 30, 0.12)',
+ onSurfaceDisabled: 'rgba(30, 27, 30, 0.38)',
+ backdrop: 'rgba(53, 46, 54, 0.4)',
+ },
+ indigo: {
+ primary: 'rgb(104, 71, 192)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(232, 221, 255)',
+ onPrimaryContainer: 'rgb(33, 0, 93)',
+ secondary: 'rgb(97, 91, 113)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(232, 222, 248)',
+ onSecondaryContainer: 'rgb(29, 25, 43)',
+ tertiary: 'rgb(125, 82, 96)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(255, 217, 227)',
+ onTertiaryContainer: 'rgb(49, 16, 29)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(28, 27, 30)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(28, 27, 30)',
+ surfaceVariant: 'rgb(230, 224, 236)',
+ onSurfaceVariant: 'rgb(72, 69, 78)',
+ outline: 'rgb(121, 117, 127)',
+ outlineVariant: 'rgb(202, 196, 207)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(49, 48, 51)',
+ inverseOnSurface: 'rgb(244, 239, 244)',
+ inversePrimary: 'rgb(206, 189, 255)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(247, 242, 252)',
+ level2: 'rgb(243, 237, 250)',
+ level3: 'rgb(238, 231, 248)',
+ level4: 'rgb(237, 229, 247)',
+ level5: 'rgb(234, 226, 246)',
+ },
+ surfaceDisabled: 'rgba(28, 27, 30, 0.12)',
+ onSurfaceDisabled: 'rgba(28, 27, 30, 0.38)',
+ backdrop: 'rgba(50, 47, 56, 0.4)',
+ },
+ blue: {
+ primary: 'rgb(0, 99, 154)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(206, 229, 255)',
+ onPrimaryContainer: 'rgb(0, 29, 50)',
+ secondary: 'rgb(81, 96, 111)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(213, 228, 247)',
+ onSecondaryContainer: 'rgb(14, 29, 42)',
+ tertiary: 'rgb(104, 88, 122)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(238, 219, 255)',
+ onTertiaryContainer: 'rgb(35, 21, 51)',
+ background: 'rgb(252, 252, 255)',
+ onBackground: 'rgb(26, 28, 30)',
+ surface: 'rgb(252, 252, 255)',
+ onSurface: 'rgb(26, 28, 30)',
+ surfaceVariant: 'rgb(222, 227, 235)',
+ onSurfaceVariant: 'rgb(66, 71, 78)',
+ outline: 'rgb(114, 119, 127)',
+ outlineVariant: 'rgb(194, 199, 207)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(47, 48, 51)',
+ inverseOnSurface: 'rgb(240, 240, 244)',
+ inversePrimary: 'rgb(150, 204, 255)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(239, 244, 250)',
+ level2: 'rgb(232, 240, 247)',
+ level3: 'rgb(224, 235, 244)',
+ level4: 'rgb(222, 234, 243)',
+ level5: 'rgb(217, 231, 241)',
+ },
+ surfaceDisabled: 'rgba(26, 28, 30, 0.12)',
+ onSurfaceDisabled: 'rgba(26, 28, 30, 0.38)',
+ backdrop: 'rgba(44, 49, 55, 0.4)',
+ },
+ teal: {
+ primary: 'rgb(0, 104, 116)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(151, 240, 255)',
+ onPrimaryContainer: 'rgb(0, 31, 36)',
+ secondary: 'rgb(74, 98, 103)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(205, 231, 236)',
+ onSecondaryContainer: 'rgb(5, 31, 35)',
+ tertiary: 'rgb(82, 94, 125)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(218, 226, 255)',
+ onTertiaryContainer: 'rgb(14, 27, 55)',
+ background: 'rgb(250, 253, 253)',
+ onBackground: 'rgb(25, 28, 29)',
+ surface: 'rgb(250, 253, 253)',
+ onSurface: 'rgb(25, 28, 29)',
+ surfaceVariant: 'rgb(219, 228, 230)',
+ onSurfaceVariant: 'rgb(63, 72, 74)',
+ outline: 'rgb(111, 121, 122)',
+ outlineVariant: 'rgb(191, 200, 202)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(46, 49, 50)',
+ inverseOnSurface: 'rgb(239, 241, 241)',
+ inversePrimary: 'rgb(79, 216, 235)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(238, 246, 246)',
+ level2: 'rgb(230, 241, 242)',
+ level3: 'rgb(223, 237, 238)',
+ level4: 'rgb(220, 235, 237)',
+ level5: 'rgb(215, 232, 234)',
+ },
+ surfaceDisabled: 'rgba(25, 28, 29, 0.12)',
+ onSurfaceDisabled: 'rgba(25, 28, 29, 0.38)',
+ backdrop: 'rgba(41, 50, 52, 0.4)',
+ },
+ cyan: {
+ primary: 'rgb(0, 107, 94)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(118, 248, 226)',
+ onPrimaryContainer: 'rgb(0, 32, 27)',
+ secondary: 'rgb(74, 99, 94)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(205, 232, 225)',
+ onSecondaryContainer: 'rgb(6, 32, 27)',
+ tertiary: 'rgb(68, 97, 121)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(202, 230, 255)',
+ onTertiaryContainer: 'rgb(0, 30, 48)',
+ background: 'rgb(250, 253, 251)',
+ onBackground: 'rgb(25, 28, 27)',
+ surface: 'rgb(250, 253, 251)',
+ onSurface: 'rgb(25, 28, 27)',
+ surfaceVariant: 'rgb(218, 229, 225)',
+ onSurfaceVariant: 'rgb(63, 73, 70)',
+ outline: 'rgb(111, 121, 118)',
+ outlineVariant: 'rgb(190, 201, 197)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(45, 49, 48)',
+ inverseOnSurface: 'rgb(239, 241, 239)',
+ inversePrimary: 'rgb(85, 219, 198)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(238, 246, 243)',
+ level2: 'rgb(230, 241, 238)',
+ level3: 'rgb(223, 237, 234)',
+ level4: 'rgb(220, 236, 232)',
+ level5: 'rgb(215, 233, 229)',
+ },
+ surfaceDisabled: 'rgba(25, 28, 27, 0.12)',
+ onSurfaceDisabled: 'rgba(25, 28, 27, 0.38)',
+ backdrop: 'rgba(41, 50, 48, 0.4)',
+ },
+ green: {
+ primary: 'rgb(16, 109, 32)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(157, 248, 152)',
+ onPrimaryContainer: 'rgb(0, 34, 4)',
+ secondary: 'rgb(82, 99, 79)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(213, 232, 206)',
+ onSecondaryContainer: 'rgb(17, 31, 15)',
+ tertiary: 'rgb(56, 101, 106)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(188, 235, 240)',
+ onTertiaryContainer: 'rgb(0, 32, 35)',
+ background: 'rgb(252, 253, 246)',
+ onBackground: 'rgb(26, 28, 25)',
+ surface: 'rgb(252, 253, 246)',
+ onSurface: 'rgb(26, 28, 25)',
+ surfaceVariant: 'rgb(222, 229, 216)',
+ onSurfaceVariant: 'rgb(66, 73, 64)',
+ outline: 'rgb(114, 121, 111)',
+ outlineVariant: 'rgb(194, 201, 189)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(47, 49, 45)',
+ inverseOnSurface: 'rgb(240, 241, 235)',
+ inversePrimary: 'rgb(130, 219, 126)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(240, 246, 235)',
+ level2: 'rgb(233, 242, 229)',
+ level3: 'rgb(226, 237, 223)',
+ level4: 'rgb(224, 236, 220)',
+ level5: 'rgb(219, 233, 216)',
+ },
+ surfaceDisabled: 'rgba(26, 28, 25, 0.12)',
+ onSurfaceDisabled: 'rgba(26, 28, 25, 0.38)',
+ backdrop: 'rgba(44, 50, 42, 0.4)',
+ },
+ lime: {
+ primary: 'rgb(56, 107, 1)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(183, 244, 129)',
+ onPrimaryContainer: 'rgb(13, 32, 0)',
+ secondary: 'rgb(87, 98, 74)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(218, 231, 201)',
+ onSecondaryContainer: 'rgb(21, 30, 12)',
+ tertiary: 'rgb(56, 102, 100)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(187, 236, 233)',
+ onTertiaryContainer: 'rgb(0, 32, 31)',
+ background: 'rgb(253, 253, 245)',
+ onBackground: 'rgb(26, 28, 24)',
+ surface: 'rgb(253, 253, 245)',
+ onSurface: 'rgb(26, 28, 24)',
+ surfaceVariant: 'rgb(224, 228, 214)',
+ onSurfaceVariant: 'rgb(68, 72, 62)',
+ outline: 'rgb(116, 121, 109)',
+ outlineVariant: 'rgb(196, 200, 186)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(47, 49, 44)',
+ inverseOnSurface: 'rgb(241, 241, 234)',
+ inversePrimary: 'rgb(156, 215, 105)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(243, 246, 233)',
+ level2: 'rgb(237, 241, 226)',
+ level3: 'rgb(231, 237, 218)',
+ level4: 'rgb(229, 236, 216)',
+ level5: 'rgb(225, 233, 211)',
+ },
+ surfaceDisabled: 'rgba(26, 28, 24, 0.12)',
+ onSurfaceDisabled: 'rgba(26, 28, 24, 0.38)',
+ backdrop: 'rgba(45, 50, 40, 0.4)',
+ },
+ olive: {
+ primary: 'rgb(95, 98, 0)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(229, 234, 93)',
+ onPrimaryContainer: 'rgb(28, 29, 0)',
+ secondary: 'rgb(96, 96, 67)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(229, 229, 192)',
+ onSecondaryContainer: 'rgb(28, 29, 6)',
+ tertiary: 'rgb(61, 102, 88)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(191, 236, 218)',
+ onTertiaryContainer: 'rgb(0, 33, 24)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(28, 28, 23)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(28, 28, 23)',
+ surfaceVariant: 'rgb(229, 227, 209)',
+ onSurfaceVariant: 'rgb(72, 71, 59)',
+ outline: 'rgb(121, 120, 105)',
+ outlineVariant: 'rgb(201, 199, 182)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(49, 49, 43)',
+ inverseOnSurface: 'rgb(244, 240, 232)',
+ inversePrimary: 'rgb(200, 206, 68)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(247, 243, 242)',
+ level2: 'rgb(242, 239, 235)',
+ level3: 'rgb(237, 234, 227)',
+ level4: 'rgb(236, 233, 224)',
+ level5: 'rgb(233, 230, 219)',
+ },
+ surfaceDisabled: 'rgba(28, 28, 23, 0.12)',
+ onSurfaceDisabled: 'rgba(28, 28, 23, 0.38)',
+ backdrop: 'rgba(49, 49, 37, 0.4)',
+ },
+ brown: {
+ primary: 'rgb(155, 68, 39)',
+ onPrimary: 'rgb(255, 255, 255)',
+ primaryContainer: 'rgb(255, 219, 208)',
+ onPrimaryContainer: 'rgb(58, 11, 0)',
+ secondary: 'rgb(119, 87, 77)',
+ onSecondary: 'rgb(255, 255, 255)',
+ secondaryContainer: 'rgb(255, 219, 208)',
+ onSecondaryContainer: 'rgb(44, 22, 14)',
+ tertiary: 'rgb(107, 94, 47)',
+ onTertiary: 'rgb(255, 255, 255)',
+ tertiaryContainer: 'rgb(244, 226, 167)',
+ onTertiaryContainer: 'rgb(34, 27, 0)',
+ background: 'rgb(255, 251, 255)',
+ onBackground: 'rgb(32, 26, 24)',
+ surface: 'rgb(255, 251, 255)',
+ onSurface: 'rgb(32, 26, 24)',
+ surfaceVariant: 'rgb(245, 222, 215)',
+ onSurfaceVariant: 'rgb(83, 67, 63)',
+ outline: 'rgb(133, 115, 110)',
+ outlineVariant: 'rgb(216, 194, 188)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(54, 47, 45)',
+ inverseOnSurface: 'rgb(251, 238, 234)',
+ inversePrimary: 'rgb(255, 181, 158)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(250, 242, 244)',
+ level2: 'rgb(247, 236, 238)',
+ level3: 'rgb(244, 231, 231)',
+ level4: 'rgb(243, 229, 229)',
+ level5: 'rgb(241, 225, 225)',
+ },
+ surfaceDisabled: 'rgba(32, 26, 24, 0.12)',
+ onSurfaceDisabled: 'rgba(32, 26, 24, 0.38)',
+ backdrop: 'rgba(59, 45, 41, 0.4)',
+ },
+ },
+ dark: {
+ default: {
+ primary: MD3DarkTheme.colors.primary,
+ onPrimary: MD3DarkTheme.colors.onPrimary,
+ },
+ orange: {
+ primary: 'rgb(255, 183, 134)',
+ onPrimary: 'rgb(80, 36, 0)',
+ primaryContainer: 'rgb(114, 54, 0)',
+ onPrimaryContainer: 'rgb(255, 220, 198)',
+ secondary: 'rgb(229, 191, 168)',
+ onSecondary: 'rgb(66, 43, 27)',
+ secondaryContainer: 'rgb(91, 65, 48)',
+ onSecondaryContainer: 'rgb(255, 220, 198)',
+ tertiary: 'rgb(201, 202, 147)',
+ onTertiary: 'rgb(49, 50, 10)',
+ tertiaryContainer: 'rgb(72, 73, 31)',
+ onTertiaryContainer: 'rgb(229, 230, 173)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(32, 26, 23)',
+ onBackground: 'rgb(236, 224, 218)',
+ surface: 'rgb(32, 26, 23)',
+ onSurface: 'rgb(236, 224, 218)',
+ surfaceVariant: 'rgb(82, 68, 60)',
+ onSurfaceVariant: 'rgb(215, 195, 183)',
+ outline: 'rgb(159, 141, 131)',
+ outlineVariant: 'rgb(82, 68, 60)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(236, 224, 218)',
+ inverseOnSurface: 'rgb(54, 47, 43)',
+ inversePrimary: 'rgb(150, 73, 0)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(43, 34, 29)',
+ level2: 'rgb(50, 39, 32)',
+ level3: 'rgb(57, 43, 35)',
+ level4: 'rgb(59, 45, 36)',
+ level5: 'rgb(63, 48, 39)',
+ },
+ surfaceDisabled: 'rgba(236, 224, 218, 0.12)',
+ onSurfaceDisabled: 'rgba(236, 224, 218, 0.38)',
+ backdrop: 'rgba(58, 46, 38, 0.4)',
+ },
+ red: {
+ primary: 'rgb(255, 177, 194)',
+ onPrimary: 'rgb(102, 0, 43)',
+ primaryContainer: 'rgb(143, 0, 63)',
+ onPrimaryContainer: 'rgb(255, 217, 223)',
+ secondary: 'rgb(228, 189, 195)',
+ onSecondary: 'rgb(67, 41, 47)',
+ secondaryContainer: 'rgb(91, 63, 69)',
+ onSecondaryContainer: 'rgb(255, 217, 223)',
+ tertiary: 'rgb(236, 190, 145)',
+ onTertiary: 'rgb(70, 42, 9)',
+ tertiaryContainer: 'rgb(96, 64, 29)',
+ onTertiaryContainer: 'rgb(255, 220, 189)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(32, 26, 27)',
+ onBackground: 'rgb(236, 224, 224)',
+ surface: 'rgb(32, 26, 27)',
+ onSurface: 'rgb(236, 224, 224)',
+ surfaceVariant: 'rgb(82, 67, 70)',
+ onSurfaceVariant: 'rgb(214, 194, 196)',
+ outline: 'rgb(158, 140, 143)',
+ outlineVariant: 'rgb(82, 67, 70)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(236, 224, 224)',
+ inverseOnSurface: 'rgb(53, 47, 48)',
+ inversePrimary: 'rgb(185, 12, 85)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(43, 34, 35)',
+ level2: 'rgb(50, 38, 40)',
+ level3: 'rgb(57, 43, 45)',
+ level4: 'rgb(59, 44, 47)',
+ level5: 'rgb(63, 47, 50)',
+ },
+ surfaceDisabled: 'rgba(236, 224, 224, 0.12)',
+ onSurfaceDisabled: 'rgba(236, 224, 224, 0.38)',
+ backdrop: 'rgba(58, 45, 47, 0.4)',
+ },
+ violet: {
+ primary: 'rgb(235, 178, 255)',
+ onPrimary: 'rgb(82, 0, 113)',
+ primaryContainer: 'rgb(114, 17, 153)',
+ onPrimaryContainer: 'rgb(248, 216, 255)',
+ secondary: 'rgb(212, 192, 215)',
+ onSecondary: 'rgb(57, 44, 61)',
+ secondaryContainer: 'rgb(80, 66, 85)',
+ onSecondaryContainer: 'rgb(241, 220, 244)',
+ tertiary: 'rgb(245, 183, 181)',
+ onTertiary: 'rgb(76, 37, 36)',
+ tertiaryContainer: 'rgb(102, 59, 57)',
+ onTertiaryContainer: 'rgb(255, 218, 216)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(30, 27, 30)',
+ onBackground: 'rgb(232, 224, 229)',
+ surface: 'rgb(30, 27, 30)',
+ onSurface: 'rgb(232, 224, 229)',
+ surfaceVariant: 'rgb(76, 68, 77)',
+ onSurfaceVariant: 'rgb(206, 195, 205)',
+ outline: 'rgb(151, 142, 151)',
+ outlineVariant: 'rgb(76, 68, 77)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(232, 224, 229)',
+ inverseOnSurface: 'rgb(51, 47, 51)',
+ inversePrimary: 'rgb(140, 51, 179)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(40, 35, 41)',
+ level2: 'rgb(46, 39, 48)',
+ level3: 'rgb(53, 44, 55)',
+ level4: 'rgb(55, 45, 57)',
+ level5: 'rgb(59, 48, 62)',
+ },
+ surfaceDisabled: 'rgba(232, 224, 229, 0.12)',
+ onSurfaceDisabled: 'rgba(232, 224, 229, 0.38)',
+ backdrop: 'rgba(53, 46, 54, 0.4)',
+ },
+ indigo: {
+ primary: 'rgb(206, 189, 255)',
+ onPrimary: 'rgb(57, 5, 144)',
+ primaryContainer: 'rgb(80, 43, 167)',
+ onPrimaryContainer: 'rgb(232, 221, 255)',
+ secondary: 'rgb(203, 195, 220)',
+ onSecondary: 'rgb(51, 45, 65)',
+ secondaryContainer: 'rgb(73, 68, 88)',
+ onSecondaryContainer: 'rgb(232, 222, 248)',
+ tertiary: 'rgb(239, 184, 201)',
+ onTertiary: 'rgb(73, 37, 50)',
+ tertiaryContainer: 'rgb(99, 59, 73)',
+ onTertiaryContainer: 'rgb(255, 217, 227)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(28, 27, 30)',
+ onBackground: 'rgb(230, 225, 230)',
+ surface: 'rgb(28, 27, 30)',
+ onSurface: 'rgb(230, 225, 230)',
+ surfaceVariant: 'rgb(72, 69, 78)',
+ onSurfaceVariant: 'rgb(202, 196, 207)',
+ outline: 'rgb(148, 143, 153)',
+ outlineVariant: 'rgb(72, 69, 78)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(230, 225, 230)',
+ inverseOnSurface: 'rgb(49, 48, 51)',
+ inversePrimary: 'rgb(104, 71, 192)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(37, 35, 41)',
+ level2: 'rgb(42, 40, 48)',
+ level3: 'rgb(48, 45, 55)',
+ level4: 'rgb(49, 46, 57)',
+ level5: 'rgb(53, 50, 62)',
+ },
+ surfaceDisabled: 'rgba(230, 225, 230, 0.12)',
+ onSurfaceDisabled: 'rgba(230, 225, 230, 0.38)',
+ backdrop: 'rgba(50, 47, 56, 0.4)',
+ },
+ blue: {
+ primary: 'rgb(150, 204, 255)',
+ onPrimary: 'rgb(0, 51, 83)',
+ primaryContainer: 'rgb(0, 74, 117)',
+ onPrimaryContainer: 'rgb(206, 229, 255)',
+ secondary: 'rgb(185, 200, 218)',
+ onSecondary: 'rgb(35, 50, 64)',
+ secondaryContainer: 'rgb(58, 72, 87)',
+ onSecondaryContainer: 'rgb(213, 228, 247)',
+ tertiary: 'rgb(211, 191, 230)',
+ onTertiary: 'rgb(56, 42, 73)',
+ tertiaryContainer: 'rgb(79, 64, 97)',
+ onTertiaryContainer: 'rgb(238, 219, 255)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(26, 28, 30)',
+ onBackground: 'rgb(226, 226, 229)',
+ surface: 'rgb(26, 28, 30)',
+ onSurface: 'rgb(226, 226, 229)',
+ surfaceVariant: 'rgb(66, 71, 78)',
+ onSurfaceVariant: 'rgb(194, 199, 207)',
+ outline: 'rgb(140, 145, 152)',
+ outlineVariant: 'rgb(66, 71, 78)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(226, 226, 229)',
+ inverseOnSurface: 'rgb(47, 48, 51)',
+ inversePrimary: 'rgb(0, 99, 154)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(32, 37, 41)',
+ level2: 'rgb(36, 42, 48)',
+ level3: 'rgb(40, 47, 55)',
+ level4: 'rgb(41, 49, 57)',
+ level5: 'rgb(43, 53, 62)',
+ },
+ surfaceDisabled: 'rgba(226, 226, 229, 0.12)',
+ onSurfaceDisabled: 'rgba(226, 226, 229, 0.38)',
+ backdrop: 'rgba(44, 49, 55, 0.4)',
+ },
+ teal: {
+ primary: 'rgb(79, 216, 235)',
+ onPrimary: 'rgb(0, 54, 61)',
+ primaryContainer: 'rgb(0, 79, 88)',
+ onPrimaryContainer: 'rgb(151, 240, 255)',
+ secondary: 'rgb(177, 203, 208)',
+ onSecondary: 'rgb(28, 52, 56)',
+ secondaryContainer: 'rgb(51, 75, 79)',
+ onSecondaryContainer: 'rgb(205, 231, 236)',
+ tertiary: 'rgb(186, 198, 234)',
+ onTertiary: 'rgb(36, 48, 77)',
+ tertiaryContainer: 'rgb(59, 70, 100)',
+ onTertiaryContainer: 'rgb(218, 226, 255)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(25, 28, 29)',
+ onBackground: 'rgb(225, 227, 227)',
+ surface: 'rgb(25, 28, 29)',
+ onSurface: 'rgb(225, 227, 227)',
+ surfaceVariant: 'rgb(63, 72, 74)',
+ onSurfaceVariant: 'rgb(191, 200, 202)',
+ outline: 'rgb(137, 146, 148)',
+ outlineVariant: 'rgb(63, 72, 74)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(225, 227, 227)',
+ inverseOnSurface: 'rgb(46, 49, 50)',
+ inversePrimary: 'rgb(0, 104, 116)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(28, 37, 39)',
+ level2: 'rgb(29, 43, 46)',
+ level3: 'rgb(31, 49, 52)',
+ level4: 'rgb(32, 51, 54)',
+ level5: 'rgb(33, 54, 58)',
+ },
+ surfaceDisabled: 'rgba(225, 227, 227, 0.12)',
+ onSurfaceDisabled: 'rgba(225, 227, 227, 0.38)',
+ backdrop: 'rgba(41, 50, 52, 0.4)',
+ },
+ cyan: {
+ primary: 'rgb(85, 219, 198)',
+ onPrimary: 'rgb(0, 55, 48)',
+ primaryContainer: 'rgb(0, 80, 71)',
+ onPrimaryContainer: 'rgb(118, 248, 226)',
+ secondary: 'rgb(177, 204, 197)',
+ onSecondary: 'rgb(28, 53, 48)',
+ secondaryContainer: 'rgb(51, 75, 70)',
+ onSecondaryContainer: 'rgb(205, 232, 225)',
+ tertiary: 'rgb(172, 202, 229)',
+ onTertiary: 'rgb(19, 51, 72)',
+ tertiaryContainer: 'rgb(44, 74, 96)',
+ onTertiaryContainer: 'rgb(202, 230, 255)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(25, 28, 27)',
+ onBackground: 'rgb(224, 227, 225)',
+ surface: 'rgb(25, 28, 27)',
+ onSurface: 'rgb(224, 227, 225)',
+ surfaceVariant: 'rgb(63, 73, 70)',
+ onSurfaceVariant: 'rgb(190, 201, 197)',
+ outline: 'rgb(137, 147, 144)',
+ outlineVariant: 'rgb(63, 73, 70)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(224, 227, 225)',
+ inverseOnSurface: 'rgb(45, 49, 48)',
+ inversePrimary: 'rgb(0, 107, 94)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(28, 38, 36)',
+ level2: 'rgb(30, 43, 41)',
+ level3: 'rgb(32, 49, 46)',
+ level4: 'rgb(32, 51, 48)',
+ level5: 'rgb(33, 55, 51)',
+ },
+ surfaceDisabled: 'rgba(224, 227, 225, 0.12)',
+ onSurfaceDisabled: 'rgba(224, 227, 225, 0.38)',
+ backdrop: 'rgba(41, 50, 48, 0.4)',
+ },
+ green: {
+ primary: 'rgb(130, 219, 126)',
+ onPrimary: 'rgb(0, 57, 10)',
+ primaryContainer: 'rgb(0, 83, 18)',
+ onPrimaryContainer: 'rgb(157, 248, 152)',
+ secondary: 'rgb(186, 204, 179)',
+ onSecondary: 'rgb(37, 52, 35)',
+ secondaryContainer: 'rgb(59, 75, 56)',
+ onSecondaryContainer: 'rgb(213, 232, 206)',
+ tertiary: 'rgb(160, 207, 212)',
+ onTertiary: 'rgb(0, 54, 59)',
+ tertiaryContainer: 'rgb(31, 77, 82)',
+ onTertiaryContainer: 'rgb(188, 235, 240)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(26, 28, 25)',
+ onBackground: 'rgb(226, 227, 221)',
+ surface: 'rgb(26, 28, 25)',
+ onSurface: 'rgb(226, 227, 221)',
+ surfaceVariant: 'rgb(66, 73, 64)',
+ onSurfaceVariant: 'rgb(194, 201, 189)',
+ outline: 'rgb(140, 147, 136)',
+ outlineVariant: 'rgb(66, 73, 64)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(226, 227, 221)',
+ inverseOnSurface: 'rgb(47, 49, 45)',
+ inversePrimary: 'rgb(16, 109, 32)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(31, 38, 30)',
+ level2: 'rgb(34, 43, 33)',
+ level3: 'rgb(37, 49, 36)',
+ level4: 'rgb(39, 51, 37)',
+ level5: 'rgb(41, 55, 39)',
+ },
+ surfaceDisabled: 'rgba(226, 227, 221, 0.12)',
+ onSurfaceDisabled: 'rgba(226, 227, 221, 0.38)',
+ backdrop: 'rgba(44, 50, 42, 0.4)',
+ },
+ lime: {
+ primary: 'rgb(156, 215, 105)',
+ onPrimary: 'rgb(26, 55, 0)',
+ primaryContainer: 'rgb(40, 80, 0)',
+ onPrimaryContainer: 'rgb(183, 244, 129)',
+ secondary: 'rgb(190, 203, 174)',
+ onSecondary: 'rgb(41, 52, 31)',
+ secondaryContainer: 'rgb(63, 74, 52)',
+ onSecondaryContainer: 'rgb(218, 231, 201)',
+ tertiary: 'rgb(160, 207, 205)',
+ onTertiary: 'rgb(0, 55, 54)',
+ tertiaryContainer: 'rgb(30, 78, 77)',
+ onTertiaryContainer: 'rgb(187, 236, 233)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(26, 28, 24)',
+ onBackground: 'rgb(227, 227, 220)',
+ surface: 'rgb(26, 28, 24)',
+ onSurface: 'rgb(227, 227, 220)',
+ surfaceVariant: 'rgb(68, 72, 62)',
+ onSurfaceVariant: 'rgb(196, 200, 186)',
+ outline: 'rgb(142, 146, 134)',
+ outlineVariant: 'rgb(68, 72, 62)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(227, 227, 220)',
+ inverseOnSurface: 'rgb(47, 49, 44)',
+ inversePrimary: 'rgb(56, 107, 1)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(33, 37, 28)',
+ level2: 'rgb(36, 43, 31)',
+ level3: 'rgb(40, 49, 33)',
+ level4: 'rgb(42, 50, 34)',
+ level5: 'rgb(44, 54, 35)',
+ },
+ surfaceDisabled: 'rgba(227, 227, 220, 0.12)',
+ onSurfaceDisabled: 'rgba(227, 227, 220, 0.38)',
+ backdrop: 'rgba(45, 50, 40, 0.4)',
+ },
+ olive: {
+ primary: 'rgb(200, 206, 68)',
+ onPrimary: 'rgb(49, 51, 0)',
+ primaryContainer: 'rgb(71, 74, 0)',
+ onPrimaryContainer: 'rgb(229, 234, 93)',
+ secondary: 'rgb(201, 201, 165)',
+ onSecondary: 'rgb(49, 50, 25)',
+ secondaryContainer: 'rgb(72, 72, 45)',
+ onSecondaryContainer: 'rgb(229, 229, 192)',
+ tertiary: 'rgb(163, 208, 190)',
+ onTertiary: 'rgb(9, 55, 43)',
+ tertiaryContainer: 'rgb(36, 78, 65)',
+ onTertiaryContainer: 'rgb(191, 236, 218)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(28, 28, 23)',
+ onBackground: 'rgb(229, 226, 218)',
+ surface: 'rgb(28, 28, 23)',
+ onSurface: 'rgb(229, 226, 218)',
+ surfaceVariant: 'rgb(72, 71, 59)',
+ onSurfaceVariant: 'rgb(201, 199, 182)',
+ outline: 'rgb(147, 145, 130)',
+ outlineVariant: 'rgb(72, 71, 59)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(229, 226, 218)',
+ inverseOnSurface: 'rgb(49, 49, 43)',
+ inversePrimary: 'rgb(95, 98, 0)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(37, 37, 25)',
+ level2: 'rgb(42, 42, 27)',
+ level3: 'rgb(47, 48, 28)',
+ level4: 'rgb(49, 49, 28)',
+ level5: 'rgb(52, 53, 29)',
+ },
+ surfaceDisabled: 'rgba(229, 226, 218, 0.12)',
+ onSurfaceDisabled: 'rgba(229, 226, 218, 0.38)',
+ backdrop: 'rgba(49, 49, 37, 0.4)',
+ },
+ brown: {
+ primary: 'rgb(255, 181, 158)',
+ onPrimary: 'rgb(93, 24, 0)',
+ primaryContainer: 'rgb(124, 45, 18)',
+ onPrimaryContainer: 'rgb(255, 219, 208)',
+ secondary: 'rgb(231, 189, 177)',
+ onSecondary: 'rgb(68, 42, 34)',
+ secondaryContainer: 'rgb(93, 64, 55)',
+ onSecondaryContainer: 'rgb(255, 219, 208)',
+ tertiary: 'rgb(215, 198, 141)',
+ onTertiary: 'rgb(58, 48, 5)',
+ tertiaryContainer: 'rgb(82, 70, 26)',
+ onTertiaryContainer: 'rgb(244, 226, 167)',
+ error: 'rgb(255, 180, 171)',
+ onError: 'rgb(105, 0, 5)',
+ errorContainer: 'rgb(147, 0, 10)',
+ onErrorContainer: 'rgb(255, 180, 171)',
+ background: 'rgb(32, 26, 24)',
+ onBackground: 'rgb(237, 224, 220)',
+ surface: 'rgb(32, 26, 24)',
+ onSurface: 'rgb(237, 224, 220)',
+ surfaceVariant: 'rgb(83, 67, 63)',
+ onSurfaceVariant: 'rgb(216, 194, 188)',
+ outline: 'rgb(160, 141, 135)',
+ outlineVariant: 'rgb(83, 67, 63)',
+ shadow: 'rgb(0, 0, 0)',
+ scrim: 'rgb(0, 0, 0)',
+ inverseSurface: 'rgb(237, 224, 220)',
+ inverseOnSurface: 'rgb(54, 47, 45)',
+ inversePrimary: 'rgb(155, 68, 39)',
+ elevation: {
+ level0: 'transparent',
+ level1: 'rgb(43, 34, 31)',
+ level2: 'rgb(50, 38, 35)',
+ level3: 'rgb(57, 43, 39)',
+ level4: 'rgb(59, 45, 40)',
+ level5: 'rgb(63, 48, 43)',
+ },
+ surfaceDisabled: 'rgba(237, 224, 220, 0.12)',
+ onSurfaceDisabled: 'rgba(237, 224, 220, 0.38)',
+ backdrop: 'rgba(59, 45, 41, 0.4)',
+ },
+ },
+}
+
+export default Colors
diff --git a/lib/ui/styles/index.ts b/lib/ui/styles/index.ts
new file mode 100644
index 0000000..c11854e
--- /dev/null
+++ b/lib/ui/styles/index.ts
@@ -0,0 +1,20 @@
+/**
+ * Styles
+ */
+
+import { StyleSheet } from 'react-native'
+
+import Colors from '@/lib/ui/styles/colors'
+import Themes from '@/lib/ui/styles/themes'
+
+const styles = StyleSheet.create({
+ screen: {
+ flex: 1,
+ gap: 16,
+ padding: 32,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+})
+
+export { Colors, Themes, styles }
diff --git a/lib/ui/styles/themes.ts b/lib/ui/styles/themes.ts
new file mode 100644
index 0000000..d56d6c7
--- /dev/null
+++ b/lib/ui/styles/themes.ts
@@ -0,0 +1,212 @@
+/**
+ * Themes
+ */
+
+import {
+ DarkTheme as NavigationDarkTheme,
+ DefaultTheme as NavigationDefaultTheme,
+} from '@react-navigation/native'
+import {
+ adaptNavigationTheme,
+ MD3LightTheme,
+ MD3DarkTheme,
+ configureFonts,
+} from 'react-native-paper'
+
+import Colors from '@/lib/ui/styles/colors'
+
+const { LightTheme, DarkTheme } = adaptNavigationTheme({
+ reactNavigationLight: NavigationDefaultTheme,
+ reactNavigationDark: NavigationDarkTheme,
+})
+
+const fonts = configureFonts({ config: { fontFamily: 'NotoSans_400Regular' } })
+
+const BaseLightTheme = {
+ ...LightTheme,
+ ...MD3LightTheme,
+ fonts,
+}
+
+const BaseDarkTheme = {
+ ...DarkTheme,
+ ...MD3DarkTheme,
+ fonts,
+}
+
+const Themes = {
+ light: {
+ default: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.default,
+ },
+ },
+ orange: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.orange,
+ },
+ },
+ red: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.red,
+ },
+ },
+ violet: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.violet,
+ },
+ },
+ indigo: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.indigo,
+ },
+ },
+ blue: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.blue,
+ },
+ },
+ teal: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.teal,
+ },
+ },
+ cyan: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.cyan,
+ },
+ },
+ green: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.green,
+ },
+ },
+ lime: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.lime,
+ },
+ },
+ olive: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.olive,
+ },
+ },
+ brown: {
+ ...BaseLightTheme,
+ colors: {
+ ...BaseLightTheme.colors,
+ ...Colors.light.brown,
+ },
+ },
+ },
+ dark: {
+ default: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.default,
+ },
+ },
+ red: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.red,
+ },
+ },
+ orange: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.orange,
+ },
+ },
+ violet: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.violet,
+ },
+ },
+ indigo: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.indigo,
+ },
+ },
+ blue: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.blue,
+ },
+ },
+ teal: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.teal,
+ },
+ },
+ cyan: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.cyan,
+ },
+ },
+ green: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.green,
+ },
+ },
+ lime: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.lime,
+ },
+ },
+ olive: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.olive,
+ },
+ },
+ brown: {
+ ...BaseDarkTheme,
+ colors: {
+ ...BaseDarkTheme.colors,
+ ...Colors.dark.brown,
+ },
+ },
+ },
+}
+
+export default Themes
diff --git a/lib/utils/index.ts b/lib/utils/index.ts
new file mode 100644
index 0000000..5f11243
--- /dev/null
+++ b/lib/utils/index.ts
@@ -0,0 +1,7 @@
+/**
+ * Utilities
+ */
+
+import Languages from '@/lib/utils/languages'
+
+export { Languages }
diff --git a/lib/utils/languages.ts b/lib/utils/languages.ts
new file mode 100644
index 0000000..2017a74
--- /dev/null
+++ b/lib/utils/languages.ts
@@ -0,0 +1,20 @@
+/**
+ * Available languages
+ */
+
+const Languages = {
+ ar: {
+ name: 'Arabic',
+ nativeName: 'العربية',
+ },
+ en: {
+ name: 'English',
+ nativeName: 'English',
+ },
+ tr: {
+ name: 'Turkish',
+ nativeName: 'Türkçe',
+ },
+}
+
+export default Languages
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..4308858
--- /dev/null
+++ b/package.json
@@ -0,0 +1,65 @@
+{
+ "name": "expo-react-native-paper",
+ "main": "expo-router/entry",
+ "version": "1.0.0",
+ "scripts": {
+ "start": "expo start",
+ "android": "expo start --android",
+ "ios": "expo start --ios",
+ "web": "expo start --web",
+ "test": "jest --watchAll",
+ "lint": "eslint . --fix",
+ "expo:fix": "expo install --fix",
+ "expo:lint": "expo lint",
+ "format": "prettier -w ."
+ },
+ "jest": {
+ "preset": "jest-expo"
+ },
+ "dependencies": {
+ "@expo-google-fonts/jetbrains-mono": "^0.2.3",
+ "@expo-google-fonts/noto-sans": "^0.2.3",
+ "@expo/vector-icons": "^14.0.0",
+ "@react-navigation/drawer": "^6.6.15",
+ "@react-navigation/native": "^6.0.2",
+ "@shopify/flash-list": "1.6.4",
+ "@shopify/react-native-skia": "1.2.3",
+ "expo": "~51.0.31",
+ "expo-font": "~12.0.5",
+ "expo-linking": "~6.3.1",
+ "expo-localization": "~15.0.3",
+ "expo-router": "~3.5.23",
+ "expo-secure-store": "~13.0.1",
+ "expo-splash-screen": "~0.27.4",
+ "expo-status-bar": "~1.12.1",
+ "expo-system-ui": "~3.0.4",
+ "expo-web-browser": "~13.0.3",
+ "formik": "^2.4.6",
+ "i18n-js": "^4.4.3",
+ "react": "18.2.0",
+ "react-dom": "18.2.0",
+ "react-native": "0.74.5",
+ "react-native-gesture-handler": "~2.16.1",
+ "react-native-paper": "^5.12.3",
+ "react-native-reanimated": "~3.10.1",
+ "react-native-safe-area-context": "4.10.5",
+ "react-native-screens": "3.31.1",
+ "react-native-web": "~0.19.6",
+ "yup": "^1.4.0",
+ "expo-image": "~1.12.15"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.20.0",
+ "@types/react": "~18.2.45",
+ "@typescript-eslint/eslint-plugin": "^7.8.0",
+ "eslint": "^8.57.0",
+ "eslint-config-expo": "^7.1.2",
+ "eslint-config-universe": "^12.0.0",
+ "jest": "^29.2.1",
+ "jest-expo": "~51.0.4",
+ "prettier": "^3.2.5",
+ "react-test-renderer": "18.2.0",
+ "typescript": "~5.3.3"
+ },
+ "private": true
+}
diff --git a/screenshots/home-blue-dark.png b/screenshots/home-blue-dark.png
new file mode 100644
index 0000000..27f9686
Binary files /dev/null and b/screenshots/home-blue-dark.png differ
diff --git a/screenshots/home-default-light.png b/screenshots/home-default-light.png
new file mode 100644
index 0000000..4dcf979
Binary files /dev/null and b/screenshots/home-default-light.png differ
diff --git a/screenshots/login-violet-light.png b/screenshots/login-violet-light.png
new file mode 100644
index 0000000..20d0ea0
Binary files /dev/null and b/screenshots/login-violet-light.png differ
diff --git a/screenshots/modal-light-red.png b/screenshots/modal-light-red.png
new file mode 100644
index 0000000..deb2c7c
Binary files /dev/null and b/screenshots/modal-light-red.png differ
diff --git a/screenshots/profile-olive-light.png b/screenshots/profile-olive-light.png
new file mode 100644
index 0000000..847719c
Binary files /dev/null and b/screenshots/profile-olive-light.png differ
diff --git a/screenshots/profile-teal-dark.png b/screenshots/profile-teal-dark.png
new file mode 100644
index 0000000..f6e3a2f
Binary files /dev/null and b/screenshots/profile-teal-dark.png differ
diff --git a/screenshots/search-orange-dark.png b/screenshots/search-orange-dark.png
new file mode 100644
index 0000000..c83b32c
Binary files /dev/null and b/screenshots/search-orange-dark.png differ
diff --git a/screenshots/settings-lime-light.png b/screenshots/settings-lime-light.png
new file mode 100644
index 0000000..556d06a
Binary files /dev/null and b/screenshots/settings-lime-light.png differ
diff --git a/screenshots/settings-violet-light.png b/screenshots/settings-violet-light.png
new file mode 100644
index 0000000..e6e023a
Binary files /dev/null and b/screenshots/settings-violet-light.png differ
diff --git a/screenshots/signup-green-dark.png b/screenshots/signup-green-dark.png
new file mode 100644
index 0000000..e459ef6
Binary files /dev/null and b/screenshots/signup-green-dark.png differ
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..ce27fee
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,10 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true,
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
+}