├── .bundle
└── config
├── .env.development
├── .env.production
├── .env.staging
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .husky
└── pre-commit
├── .nvmrc
├── .prettierrc.js
├── .vscode
└── settings.json
├── .watchmanconfig
├── .yarnrc
├── Gemfile
├── LICENSE
├── README.md
├── __mocks__
├── react-native-config.js
└── svgMock.js
├── __tests__
└── app-test.tsx
├── android
├── app
│ ├── build.gradle
│ ├── build_defs.bzl
│ ├── debug.keystore
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── starter
│ │ │ └── ReactNativeFlipper.java
│ │ ├── development
│ │ └── res
│ │ │ └── values
│ │ │ └── strings.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── starter
│ │ │ │ ├── MainActivity.java
│ │ │ │ └── MainApplication.java
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── rn_edit_text_material.xml
│ │ │ ├── font
│ │ │ ├── poppins.xml
│ │ │ ├── poppins_black.ttf
│ │ │ ├── poppins_blackitalic.ttf
│ │ │ ├── poppins_bold.ttf
│ │ │ ├── poppins_bolditalic.ttf
│ │ │ ├── poppins_extrabold.ttf
│ │ │ ├── poppins_extrabolditalic.ttf
│ │ │ ├── poppins_extralight.ttf
│ │ │ ├── poppins_extralightitalic.ttf
│ │ │ ├── poppins_italic.ttf
│ │ │ ├── poppins_light.ttf
│ │ │ ├── poppins_lightitalic.ttf
│ │ │ ├── poppins_medium.ttf
│ │ │ ├── poppins_mediumitalic.ttf
│ │ │ ├── poppins_regular.ttf
│ │ │ ├── poppins_semibold.ttf
│ │ │ ├── poppins_semibolditalic.ttf
│ │ │ ├── poppins_thin.ttf
│ │ │ └── poppins_thinitalic.ttf
│ │ │ ├── mipmap-hdpi
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── ic_launcher.png
│ │ │ └── ic_launcher_round.png
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ ├── release
│ │ └── java
│ │ │ └── starter
│ │ │ └── ReactNativeFlipper.java
│ │ └── staging
│ │ └── res
│ │ └── values
│ │ └── strings.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── link-assets-manifest.json
└── settings.gradle
├── app.json
├── assets
├── bootsplash_logo.png
├── bootsplash_logo@1,5x.png
├── bootsplash_logo@2x.png
├── bootsplash_logo@3x.png
├── bootsplash_logo@4x.png
└── bootsplash_logo_original.png
├── babel.config.js
├── bin
└── podInstall.js
├── dist
└── demo.gif
├── docs
├── 01-how-to-change-app-logo.md
├── 02-how-to-change-splash-screen.md
└── 03-how-to-keep-code-changed-from-node-modules.md
├── index.js
├── ios
├── .xcode.env
├── Config.xcconfig
├── DevelopmentExportOptions.plist
├── Podfile
├── Podfile.lock
├── ProductionExportOptions.plist
├── StagingExportOptions.plist
├── development
│ └── Info.plist
├── link-assets-manifest.json
├── staging
│ └── Info.plist
├── starter.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── development.xcscheme
│ │ ├── production.xcscheme
│ │ └── staging.xcscheme
├── starter.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── starter
│ ├── AppDelegate.h
│ ├── AppDelegate.mm
│ ├── BootSplash.storyboard
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── BootSplashLogo.imageset
│ │ │ ├── Contents.json
│ │ │ ├── bootsplash_logo.png
│ │ │ ├── bootsplash_logo@2x.png
│ │ │ └── bootsplash_logo@3x.png
│ │ └── Contents.json
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ └── main.m
└── starterTests
│ ├── Info.plist
│ └── starterTests.m
├── jest.config.js
├── jest
└── setup.js
├── metro.config.js
├── package.json
├── patches
└── .gitignore
├── react-native.config.js
├── scripts
├── deploy.sh
└── fixfonts.sh
├── src
├── assets
│ ├── fonts
│ │ ├── Poppins-Black.ttf
│ │ ├── Poppins-BlackItalic.ttf
│ │ ├── Poppins-Bold.ttf
│ │ ├── Poppins-BoldItalic.ttf
│ │ ├── Poppins-ExtraBold.ttf
│ │ ├── Poppins-ExtraBoldItalic.ttf
│ │ ├── Poppins-ExtraLight.ttf
│ │ ├── Poppins-ExtraLightItalic.ttf
│ │ ├── Poppins-Italic.ttf
│ │ ├── Poppins-Light.ttf
│ │ ├── Poppins-LightItalic.ttf
│ │ ├── Poppins-Medium.ttf
│ │ ├── Poppins-MediumItalic.ttf
│ │ ├── Poppins-Regular.ttf
│ │ ├── Poppins-SemiBold.ttf
│ │ ├── Poppins-SemiBoldItalic.ttf
│ │ ├── Poppins-Thin.ttf
│ │ └── Poppins-ThinItalic.ttf
│ ├── images
│ │ └── index.ts
│ ├── index.ts
│ └── jsons
│ │ ├── index.ts
│ │ └── spinner.json
├── common
│ ├── config
│ │ └── index.ts
│ ├── constants
│ │ ├── dimension.ts
│ │ ├── index.ts
│ │ ├── platform.ts
│ │ ├── styles.ts
│ │ └── transitions.ts
│ ├── hooks
│ │ ├── __tests__
│ │ │ └── useDisclosure.test.ts
│ │ ├── index.ts
│ │ ├── useBarStyle.ts
│ │ ├── useDisclosure.ts
│ │ ├── usePrevious.ts
│ │ ├── useTranslation.ts
│ │ └── useWhyYouUpdate.ts
│ ├── stores
│ │ ├── __tests__
│ │ │ └── notifications.test.ts
│ │ ├── index.ts
│ │ ├── useNotificationStore.ts
│ │ └── useThemeStore.ts
│ ├── themes
│ │ ├── color.ts
│ │ ├── index.ts
│ │ ├── palette.ts
│ │ ├── spacing.ts
│ │ ├── theme.ts
│ │ ├── timing.ts
│ │ └── typography.ts
│ ├── types
│ │ ├── api.ts
│ │ ├── function.ts
│ │ ├── index.ts
│ │ └── ui.ts
│ └── utils
│ │ ├── axios.ts
│ │ ├── dialog.tsx
│ │ ├── event-register.ts
│ │ ├── global-props.ts
│ │ ├── helpers.ts
│ │ ├── logger.ts
│ │ ├── magic-memo.ts
│ │ ├── mmvk.ts
│ │ ├── navigation-utilities.ts
│ │ ├── never-rerender.ts
│ │ ├── refresh-token-multi-request.ts
│ │ ├── responsive.ts
│ │ └── storage.ts
├── components
│ ├── forms
│ │ └── index.ts
│ ├── modals
│ │ ├── confirm
│ │ │ ├── confirm.tsx
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── tabs
│ │ ├── tab-animation-fixed.tsx
│ │ └── tab-animation-scrollable.tsx
│ └── widgets
│ │ ├── align
│ │ ├── align.tsx
│ │ └── index.ts
│ │ ├── app-bar
│ │ ├── app-bar.tsx
│ │ └── index.ts
│ │ ├── box
│ │ ├── box.tsx
│ │ └── index.ts
│ │ ├── button
│ │ ├── button.tsx
│ │ └── index.ts
│ │ ├── card
│ │ ├── card.tsx
│ │ └── index.ts
│ │ ├── center
│ │ ├── center.tsx
│ │ └── index.ts
│ │ ├── col
│ │ ├── col.tsx
│ │ └── index.ts
│ │ ├── fade-view
│ │ ├── fade-view.tsx
│ │ └── index.ts
│ │ ├── if
│ │ ├── if.tsx
│ │ └── index.ts
│ │ ├── image
│ │ ├── image.tsx
│ │ └── index.ts
│ │ ├── index.ts
│ │ ├── input-field
│ │ ├── index.ts
│ │ └── input-field.tsx
│ │ ├── moti-color
│ │ ├── index.ts
│ │ └── moti-color.tsx
│ │ ├── positioned
│ │ ├── index.ts
│ │ └── positioned.tsx
│ │ ├── row
│ │ ├── index.ts
│ │ └── row.tsx
│ │ ├── screen
│ │ ├── index.ts
│ │ └── screen.tsx
│ │ ├── space
│ │ ├── index.ts
│ │ └── space.tsx
│ │ ├── spinner
│ │ ├── index.ts
│ │ └── spinner.tsx
│ │ ├── stack
│ │ ├── index.ts
│ │ └── stack.tsx
│ │ ├── switch
│ │ ├── index.ts
│ │ └── switch.tsx
│ │ ├── text-button
│ │ ├── index.ts
│ │ └── text-button.tsx
│ │ ├── text-input
│ │ ├── index.ts
│ │ └── text-input.tsx
│ │ ├── text
│ │ ├── index.ts
│ │ └── text.tsx
│ │ ├── touchable
│ │ ├── index.ts
│ │ └── touchable.tsx
│ │ └── wrap
│ │ ├── index.ts
│ │ └── wrap.tsx
├── localization
│ ├── en
│ │ ├── auth.ts
│ │ ├── common.ts
│ │ ├── index.ts
│ │ └── navigate.ts
│ ├── i18n.ts
│ ├── language.ts
│ └── vi
│ │ ├── auth.ts
│ │ ├── common.ts
│ │ ├── index.ts
│ │ └── navigate.ts
├── modules
│ ├── auth
│ │ ├── api
│ │ │ ├── auth.ts
│ │ │ └── index.ts
│ │ ├── assets
│ │ │ └── index.ts
│ │ ├── components
│ │ │ ├── index.ts
│ │ │ ├── layout
│ │ │ │ ├── index.ts
│ │ │ │ └── layout.tsx
│ │ │ ├── login-form
│ │ │ │ ├── index.ts
│ │ │ │ ├── login-form.tsx
│ │ │ │ └── useLoginForm.ts
│ │ │ └── register-form
│ │ │ │ ├── index.ts
│ │ │ │ ├── register-form.tsx
│ │ │ │ └── useRegisterForm.ts
│ │ ├── hooks
│ │ │ ├── index.ts
│ │ │ └── useAuth.ts
│ │ ├── index.ts
│ │ ├── routes
│ │ │ └── index.tsx
│ │ ├── screens
│ │ │ ├── index.ts
│ │ │ ├── landing.tsx
│ │ │ ├── login.tsx
│ │ │ └── register.tsx
│ │ ├── stores
│ │ │ ├── index.ts
│ │ │ └── useAuthStore.ts
│ │ ├── types
│ │ │ ├── api.ts
│ │ │ ├── index.ts
│ │ │ ├── models
│ │ │ │ ├── index.ts
│ │ │ │ └── user.ts
│ │ │ └── payload.ts
│ │ └── utils
│ │ │ └── index.tsx
│ ├── error
│ │ ├── components
│ │ │ ├── error-boundary
│ │ │ │ ├── error-boundary.tsx
│ │ │ │ └── index.ts
│ │ │ └── index.ts
│ │ └── screens
│ │ │ ├── crash.tsx
│ │ │ └── index.ts
│ └── home
│ │ ├── components
│ │ ├── index.ts
│ │ └── layout
│ │ │ ├── index.ts
│ │ │ └── layout.tsx
│ │ ├── routes
│ │ └── index.tsx
│ │ └── screens
│ │ ├── home.tsx
│ │ └── index.ts
├── providers
│ └── index.tsx
├── routes
│ ├── index.tsx
│ └── router-name.ts
└── starter.tsx
├── templates
├── form
│ ├── NAME.tsx.ejs
│ └── index.ts.ejs
├── modal
│ ├── NAME.tsx.ejs
│ └── index.ts.ejs
├── module
│ ├── api
│ │ ├── demo.ts
│ │ └── index.ts
│ ├── assets
│ │ └── index.ts
│ ├── components
│ │ └── index.ts
│ ├── hooks
│ │ └── index.ts
│ ├── index.ts
│ ├── routes
│ │ └── index.tsx
│ ├── screens
│ │ └── index.ts
│ ├── stores
│ │ ├── index.ts
│ │ └── useDemoStore.ts
│ ├── types
│ │ ├── api.ts
│ │ ├── index.ts
│ │ ├── models
│ │ │ ├── demo.ts
│ │ │ └── index.ts
│ │ └── payload.ts
│ └── utils
│ │ └── index.tsx
├── screen
│ └── NAME.tsx.ejs
├── store
│ └── useNAME.tsx.ejs
└── widget
│ ├── NAME.tsx.ejs
│ └── index.ts.ejs
├── tsconfig.json
├── tsconfig.paths.json
├── types
└── declarations.d.ts
└── yarn.lock
/.bundle/config:
--------------------------------------------------------------------------------
1 | BUNDLE_PATH: "vendor/bundle"
2 | BUNDLE_FORCE_RUBY_PLATFORM: 1
3 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | API_URL=https://myapi.com-dev
2 | GOOGLE_MAPS_API_KEY=abcdefgh
3 | APP_NAME=Starter Development
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | API_URL=https://myapi.com-prod
2 | GOOGLE_MAPS_API_KEY=abcdefgh
3 | APP_NAME=Starter
4 |
--------------------------------------------------------------------------------
/.env.staging:
--------------------------------------------------------------------------------
1 | API_URL=https://myapi.com-staging
2 | GOOGLE_MAPS_API_KEY=abcdefgh
3 | APP_NAME=Starter Staging
4 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/*.js
2 | **/*.js
3 | bin
4 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | 'airbnb',
4 | 'airbnb/hooks',
5 | 'plugin:@typescript-eslint/recommended',
6 | 'prettier',
7 | 'plugin:prettier/recommended',
8 | ],
9 | plugins: ['@typescript-eslint', 'react', 'prettier', 'simple-import-sort'],
10 | parser: '@typescript-eslint/parser',
11 | parserOptions: {
12 | ecmaFeatures: {
13 | jsx: true,
14 | },
15 | ecmaVersion: 'latest',
16 | sourceType: 'module',
17 | project: './tsconfig.json',
18 | },
19 | rules: {
20 | 'import/no-unresolved': 0,
21 | 'react/jsx-filename-extension': [
22 | 1,
23 | {
24 | extensions: ['.ts', '.tsx'],
25 | },
26 | ],
27 | 'no-use-before-define': 0,
28 | '@typescript-eslint/no-use-before-define': ['error'],
29 | 'import/extensions': [
30 | 'error',
31 | 'never',
32 | {
33 | json: 'always',
34 | },
35 | ],
36 | 'react/prop-types': 0,
37 | 'no-shadow': 0,
38 | '@typescript-eslint/no-shadow': 2,
39 | 'import/prefer-default-export': 0,
40 | 'react/require-default-props': 0,
41 | 'react/jsx-props-no-spreading': 0,
42 | 'func-names': 0,
43 | '@typescript-eslint/no-explicit-any': 0,
44 | 'no-restricted-imports': 0,
45 | 'prettier/prettier': ['error', { bracketSpacing: true }],
46 | 'react/jsx-no-bind': 2,
47 | 'no-empty-function': 0,
48 | 'no-extra-semi': 0,
49 | 'no-console': 0,
50 | 'simple-import-sort/imports': [
51 | 'error',
52 | {
53 | groups: [['^react-?'], ['^@?react-?'], ['^@?\\w']],
54 | },
55 | ],
56 | },
57 | }
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | ios/.xcode.env.local
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 | .cxx/
34 | *.keystore
35 | !debug.keystore
36 |
37 | # node.js
38 | #
39 | node_modules/
40 | npm-debug.log
41 | yarn-error.log
42 |
43 | # fastlane
44 | #
45 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
46 | # screenshots whenever they are needed.
47 | # For more information about the recommended setup visit:
48 | # https://docs.fastlane.tools/best-practices/source-control/
49 |
50 | **/fastlane/report.xml
51 | **/fastlane/Preview.html
52 | **/fastlane/screenshots
53 | **/fastlane/test_output
54 |
55 | # Bundle artifact
56 | *.jsbundle
57 |
58 | # Ruby / CocoaPods
59 | /ios/Pods/
60 | /vendor/bundle/
61 |
62 | # Temporary files created by Metro to check the health of the file watcher
63 | .metro-health-check*
64 | # testing
65 | /coverage
66 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | yarn validate
5 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v16.17.0
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | arrowParens: 'avoid',
3 | bracketSameLine: true,
4 | bracketSpacing: true,
5 | singleQuote: true,
6 | trailingComma: 'all',
7 | printWidth: 100,
8 | tabWidth: 2,
9 | useTabs: false,
10 | semi: false,
11 | };
12 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": "explicit"
5 | },
6 | "eslint.validate": ["javascript", "typescript"]
7 | }
8 |
--------------------------------------------------------------------------------
/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/.yarnrc:
--------------------------------------------------------------------------------
1 | 1.22.19
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
4 | ruby '2.7.5'
5 |
6 | gem 'cocoapods', '~> 1.11', '>= 1.11.2'
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 bonnguyenitc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/__mocks__/react-native-config.js:
--------------------------------------------------------------------------------
1 | export default {}
2 |
--------------------------------------------------------------------------------
/__mocks__/svgMock.js:
--------------------------------------------------------------------------------
1 | module.exports = 'SvgMock'
2 | module.exports.ReactComponent = 'SvgMock'
3 |
--------------------------------------------------------------------------------
/__tests__/app-test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * @format
3 | */
4 |
5 | import React from 'react'
6 |
7 | import { render } from '@testing-library/react-native'
8 |
9 | import 'react-native'
10 | import { Starter } from '../src/starter'
11 |
12 | // Note: test renderer must be required after react-native.
13 |
14 | it('renders correctly', () => {
15 | render()
16 | })
17 |
--------------------------------------------------------------------------------
/android/app/build_defs.bzl:
--------------------------------------------------------------------------------
1 | """Helper definitions to glob .aar and .jar targets"""
2 |
3 | def create_aar_targets(aarfiles):
4 | for aarfile in aarfiles:
5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6 | lib_deps.append(":" + name)
7 | android_prebuilt_aar(
8 | name = name,
9 | aar = aarfile,
10 | )
11 |
12 | def create_jar_targets(jarfiles):
13 | for jarfile in jarfiles:
14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")]
15 | lib_deps.append(":" + name)
16 | prebuilt_jar(
17 | name = name,
18 | binary_jar = jarfile,
19 | )
20 |
--------------------------------------------------------------------------------
/android/app/debug.keystore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/debug.keystore
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 | -keep class com.starter.BuildConfig { *; }
12 |
13 | -keep class com.swmansion.reanimated.** { *; }
14 | -keep class com.facebook.react.turbomodule.** { *; }
15 |
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/android/app/src/debug/java/com/starter/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | *
This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.starter;
8 |
9 | import android.content.Context;
10 | import com.facebook.flipper.android.AndroidFlipperClient;
11 | import com.facebook.flipper.android.utils.FlipperUtils;
12 | import com.facebook.flipper.core.FlipperClient;
13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping;
17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
20 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
21 | import com.facebook.react.ReactInstanceEventListener;
22 | import com.facebook.react.ReactInstanceManager;
23 | import com.facebook.react.bridge.ReactContext;
24 | import com.facebook.react.modules.network.NetworkingModule;
25 | import okhttp3.OkHttpClient;
26 |
27 | /**
28 | * Class responsible of loading Flipper inside your React Native application. This is the debug
29 | * flavor of it. Here you can add your own plugins and customize the Flipper setup.
30 | */
31 | public class ReactNativeFlipper {
32 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
33 | if (FlipperUtils.shouldEnableFlipper(context)) {
34 | final FlipperClient client = AndroidFlipperClient.getInstance(context);
35 |
36 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
37 | client.addPlugin(new DatabasesFlipperPlugin(context));
38 | client.addPlugin(new SharedPreferencesFlipperPlugin(context));
39 | client.addPlugin(CrashReporterPlugin.getInstance());
40 |
41 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
42 | NetworkingModule.setCustomClientBuilder(
43 | new NetworkingModule.CustomClientBuilder() {
44 | @Override
45 | public void apply(OkHttpClient.Builder builder) {
46 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
47 | }
48 | });
49 | client.addPlugin(networkFlipperPlugin);
50 | client.start();
51 |
52 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
53 | // Hence we run if after all native modules have been initialized
54 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
55 | if (reactContext == null) {
56 | reactInstanceManager.addReactInstanceEventListener(
57 | new ReactInstanceEventListener() {
58 | @Override
59 | public void onReactContextInitialized(ReactContext reactContext) {
60 | reactInstanceManager.removeReactInstanceEventListener(this);
61 | reactContext.runOnNativeModulesQueueThread(
62 | new Runnable() {
63 | @Override
64 | public void run() {
65 | client.addPlugin(new FrescoFlipperPlugin());
66 | }
67 | });
68 | }
69 | });
70 | } else {
71 | client.addPlugin(new FrescoFlipperPlugin());
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/android/app/src/development/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Starter Dev
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/starter/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.starter;
2 |
3 | import com.facebook.react.ReactActivity;
4 | import com.facebook.react.ReactActivityDelegate;
5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
6 | import com.facebook.react.defaults.DefaultReactActivityDelegate;
7 | import android.os.Bundle;
8 | import com.zoontek.rnbootsplash.RNBootSplash;
9 |
10 | public class MainActivity extends ReactActivity {
11 |
12 | /**
13 | * Returns the name of the main component registered from JavaScript. This is used to schedule
14 | * rendering of the component.
15 | */
16 | @Override
17 | protected String getMainComponentName() {
18 | return "starter";
19 | }
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | RNBootSplash.init(this, R.style.BootTheme); // ⬅️ initialize the splash screen
24 | super.onCreate(savedInstanceState); // or super.onCreate(null) with react-native-screens
25 | }
26 |
27 | /**
28 | * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
29 | * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
30 | * (aka React 18) with two boolean flags.
31 | */
32 | @Override
33 | protected ReactActivityDelegate createReactActivityDelegate() {
34 | return new DefaultReactActivityDelegate(
35 | this,
36 | getMainComponentName(),
37 | // If you opted-in for the New Architecture, we enable the Fabric Renderer.
38 | DefaultNewArchitectureEntryPoint.getFabricEnabled());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/starter/MainApplication.java:
--------------------------------------------------------------------------------
1 | package com.starter;
2 |
3 | import android.app.Application;
4 | import com.facebook.react.PackageList;
5 | import com.facebook.react.ReactApplication;
6 | import com.facebook.react.ReactNativeHost;
7 | import com.facebook.react.ReactPackage;
8 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
9 | import com.facebook.react.defaults.DefaultReactNativeHost;
10 | import com.facebook.soloader.SoLoader;
11 | import java.util.List;
12 |
13 | import com.facebook.react.views.text.ReactFontManager;
14 |
15 | public class MainApplication extends Application implements ReactApplication {
16 |
17 | private final ReactNativeHost mReactNativeHost =
18 | new DefaultReactNativeHost(this) {
19 | @Override
20 | public boolean getUseDeveloperSupport() {
21 | return BuildConfig.DEBUG;
22 | }
23 |
24 | @Override
25 | protected List getPackages() {
26 | @SuppressWarnings("UnnecessaryLocalVariable")
27 | List packages = new PackageList(this).getPackages();
28 | // Packages that cannot be autolinked yet can be added manually here, for example:
29 | // packages.add(new MyReactNativePackage());
30 | return packages;
31 | }
32 |
33 | @Override
34 | protected String getJSMainModuleName() {
35 | return "index";
36 | }
37 |
38 | @Override
39 | protected boolean isNewArchEnabled() {
40 | return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
41 | }
42 |
43 | @Override
44 | protected Boolean isHermesEnabled() {
45 | return BuildConfig.IS_HERMES_ENABLED;
46 | }
47 | };
48 |
49 | @Override
50 | public ReactNativeHost getReactNativeHost() {
51 | return mReactNativeHost;
52 | }
53 |
54 | @Override
55 | public void onCreate() {
56 | super.onCreate();
57 | ReactFontManager.getInstance().addCustomFont(this, "Poppins", R.font.poppins);
58 | SoLoader.init(this, /* native exopackage */ false);
59 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
60 | // If you opted-in for the New Architecture, we load the native entry point for this app.
61 | DefaultNewArchitectureEntryPoint.load();
62 | }
63 | ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/rn_edit_text_material.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
21 |
22 |
23 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_black.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_blackitalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_blackitalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_bold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_bolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_bolditalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_extrabold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_extrabold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_extrabolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_extrabolditalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_extralight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_extralight.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_extralightitalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_extralightitalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_italic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_light.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_lightitalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_lightitalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_medium.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_mediumitalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_mediumitalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_regular.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_semibold.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_semibolditalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_semibolditalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_thin.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/font/poppins_thinitalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/font/poppins_thinitalic.ttf
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-hdpi/bootsplash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-mdpi/bootsplash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xhdpi/bootsplash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxhdpi/bootsplash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxxhdpi/bootsplash_logo.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 | #F5FCFF
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Starter
3 |
4 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/android/app/src/release/java/starter/ReactNativeFlipper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) Meta Platforms, Inc. and affiliates.
3 | *
4 | * This source code is licensed under the MIT license found in the LICENSE file in the root
5 | * directory of this source tree.
6 | */
7 | package com.starter;
8 |
9 | import android.content.Context;
10 | import com.facebook.react.ReactInstanceManager;
11 |
12 | /**
13 | * Class responsible of loading Flipper inside your React Native application. This is the release
14 | * flavor of it so it's empty as we don't want to load Flipper.
15 | */
16 | public class ReactNativeFlipper {
17 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
18 | // Do nothing as we don't want to initialize Flipper on Release.
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/android/app/src/staging/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Starter Staging
3 |
4 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | import org.apache.tools.ant.taskdefs.condition.Os
2 |
3 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
4 |
5 | buildscript {
6 | ext {
7 | buildToolsVersion = "33.0.0"
8 | minSdkVersion = 23
9 | compileSdkVersion = 33
10 | targetSdkVersion = 33
11 | // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
12 | ndkVersion = "23.1.7779620"
13 | }
14 | repositories {
15 | google()
16 | mavenCentral()
17 | }
18 | dependencies {
19 | classpath("com.android.tools.build:gradle")
20 | classpath("com.facebook.react:react-native-gradle-plugin")
21 | }
22 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
13 | org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | # AndroidX package structure to make it clearer which packages are bundled with the
21 | # Android operating system, and which are packaged with your app's APK
22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
23 | android.useAndroidX=true
24 | # Automatically convert third-party libraries to use AndroidX
25 | android.enableJetifier=true
26 |
27 | # Version of flipper SDK to use with React Native
28 | FLIPPER_VERSION=0.182.0
29 |
30 | # Use this property to specify which architecture you want to build.
31 | # You can also override it from the CLI using
32 | # ./gradlew -PreactNativeArchitectures=x86_64
33 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
34 |
35 | # Use this property to enable support to the new architecture.
36 | # This will allow you to use TurboModules and the Fabric render in
37 | # your application. You should enable this flag either if you want
38 | # to write custom TurboModules/Fabric components OR use libraries that
39 | # are providing them.
40 | newArchEnabled=false
41 |
42 | # Use this property to enable or disable the Hermes JS engine.
43 | # If set to false, you will be using JSC instead.
44 | hermesEnabled=true
45 |
46 | # development keystore
47 | DEBUG_STORE_FILE=debug.keystore
48 | DEBUG_KEY_ALIAS=androiddebugkey
49 | DEBUG_STORE_PASSWORD=android
50 | DEBUG_KEY_PASSWORD=android
51 | # staging keystore
52 | STAGING_STORE_FILE=debug.keystore
53 | STAGING_KEY_ALIAS=androiddebugkey
54 | STAGING_STORE_PASSWORD=android
55 | STAGING_KEY_PASSWORD=android
56 | # product keystore
57 | PRODUCT_STORE_FILE=debug.keystore
58 | PRODUCT_KEY_ALIAS=androiddebugkey
59 | PRODUCT_STORE_PASSWORD=android
60 | PRODUCT_KEY_PASSWORD=android
61 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
4 | networkTimeout=10000
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%"=="" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%"=="" set DIRNAME=.
29 | @rem This is normally unused
30 | set APP_BASE_NAME=%~n0
31 | set APP_HOME=%DIRNAME%
32 |
33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
35 |
36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
38 |
39 | @rem Find java.exe
40 | if defined JAVA_HOME goto findJavaFromJavaHome
41 |
42 | set JAVA_EXE=java.exe
43 | %JAVA_EXE% -version >NUL 2>&1
44 | if %ERRORLEVEL% equ 0 goto execute
45 |
46 | echo.
47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
48 | echo.
49 | echo Please set the JAVA_HOME variable in your environment to match the
50 | echo location of your Java installation.
51 |
52 | goto fail
53 |
54 | :findJavaFromJavaHome
55 | set JAVA_HOME=%JAVA_HOME:"=%
56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
57 |
58 | if exist "%JAVA_EXE%" goto execute
59 |
60 | echo.
61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
62 | echo.
63 | echo Please set the JAVA_HOME variable in your environment to match the
64 | echo location of your Java installation.
65 |
66 | goto fail
67 |
68 | :execute
69 | @rem Setup the command line
70 |
71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
72 |
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if %ERRORLEVEL% equ 0 goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | set EXIT_CODE=%ERRORLEVEL%
85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
87 | exit /b %EXIT_CODE%
88 |
89 | :mainEnd
90 | if "%OS%"=="Windows_NT" endlocal
91 |
92 | :omega
93 |
--------------------------------------------------------------------------------
/android/link-assets-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "migIndex": 1,
3 | "data": [
4 | {
5 | "path": "src/assets/fonts/Poppins-Black.ttf",
6 | "sha1": "645e04c53c6b5b35bce654a811ebce16af8aa721"
7 | },
8 | {
9 | "path": "src/assets/fonts/Poppins-BlackItalic.ttf",
10 | "sha1": "be4ef2aac7775a4b0179c0ecfce6cea8f7783f63"
11 | },
12 | {
13 | "path": "src/assets/fonts/Poppins-Bold.ttf",
14 | "sha1": "875cf0cecd647bcf22e79d633d868c1b1ec98dfa"
15 | },
16 | {
17 | "path": "src/assets/fonts/Poppins-BoldItalic.ttf",
18 | "sha1": "6de8952ca08d27ee77f57347d2cef4b4e26aa78f"
19 | },
20 | {
21 | "path": "src/assets/fonts/Poppins-ExtraBold.ttf",
22 | "sha1": "4b5c0750f073abd576413a0898d3b95adaf199c8"
23 | },
24 | {
25 | "path": "src/assets/fonts/Poppins-ExtraBoldItalic.ttf",
26 | "sha1": "94d3ee3acdbd00e37a715a25f2b5237666b5b8a3"
27 | },
28 | {
29 | "path": "src/assets/fonts/Poppins-ExtraLight.ttf",
30 | "sha1": "85af6582a7e6155917c605f9d3fed68c02b23b06"
31 | },
32 | {
33 | "path": "src/assets/fonts/Poppins-ExtraLightItalic.ttf",
34 | "sha1": "6fa0418f91032680d2d8fe4843009f2c80faa61d"
35 | },
36 | {
37 | "path": "src/assets/fonts/Poppins-Italic.ttf",
38 | "sha1": "e2bfcd56016c3c915221ef1c5fc1a17b0776eb91"
39 | },
40 | {
41 | "path": "src/assets/fonts/Poppins-Light.ttf",
42 | "sha1": "e247a92158e112f8bf7b638c8d95381d66b00dbb"
43 | },
44 | {
45 | "path": "src/assets/fonts/Poppins-LightItalic.ttf",
46 | "sha1": "f76102b361e705751a83b7b22fe954daf1f27dd4"
47 | },
48 | {
49 | "path": "src/assets/fonts/Poppins-Medium.ttf",
50 | "sha1": "283f21b44efbdbf276ba802be2d949a36bbc4233"
51 | },
52 | {
53 | "path": "src/assets/fonts/Poppins-MediumItalic.ttf",
54 | "sha1": "b592c62fdf88c40cfd0f031bba6423b1d4430472"
55 | },
56 | {
57 | "path": "src/assets/fonts/Poppins-Regular.ttf",
58 | "sha1": "fdd3002e7d814ee47c1c1b8487c72c6bbb3a2d00"
59 | },
60 | {
61 | "path": "src/assets/fonts/Poppins-SemiBold.ttf",
62 | "sha1": "8a4ace9392d06bcb7f8ea2f5169b07e4c383a90d"
63 | },
64 | {
65 | "path": "src/assets/fonts/Poppins-SemiBoldItalic.ttf",
66 | "sha1": "0c8bed0a5942b2388d36b11c115685d9f01700b0"
67 | },
68 | {
69 | "path": "src/assets/fonts/Poppins-Thin.ttf",
70 | "sha1": "09ba4dcd5509c8085bf88c665dcc51cbdfced27b"
71 | },
72 | {
73 | "path": "src/assets/fonts/Poppins-ThinItalic.ttf",
74 | "sha1": "fb8fccca21550e71135f92bbaf8a2e8a12c90465"
75 | }
76 | ]
77 | }
78 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'starter'
2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3 | include ':app'
4 | includeBuild('../node_modules/@react-native/gradle-plugin')
--------------------------------------------------------------------------------
/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "starter",
3 | "displayName": "starter"
4 | }
5 |
--------------------------------------------------------------------------------
/assets/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo.png
--------------------------------------------------------------------------------
/assets/bootsplash_logo@1,5x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo@1,5x.png
--------------------------------------------------------------------------------
/assets/bootsplash_logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo@2x.png
--------------------------------------------------------------------------------
/assets/bootsplash_logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo@3x.png
--------------------------------------------------------------------------------
/assets/bootsplash_logo@4x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo@4x.png
--------------------------------------------------------------------------------
/assets/bootsplash_logo_original.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/assets/bootsplash_logo_original.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | plugins: [
4 | [
5 | 'module-resolver',
6 | {
7 | root: ['./src'],
8 | alias: {
9 | /**
10 | * Regular expression is used to match all files inside `./src` directory and map each `.src/folder/[..]` to `~folder/[..]` path
11 | */
12 | '@': './src',
13 | },
14 | extensions: ['.tsx', '.ts'],
15 | },
16 | ],
17 | 'react-native-reanimated/plugin',
18 | ],
19 | env: {
20 | production: {
21 | plugins: ['transform-remove-console'],
22 | },
23 | },
24 | }
25 |
--------------------------------------------------------------------------------
/bin/podInstall.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // Husky install
4 | run('npx husky install');
5 |
6 | // Patch any packages that need patching
7 | run('npx patch-package');
8 |
9 | // Kill the metro bundler if it's running.
10 | if (['darwin', 'linux'].includes(process.platform)) {
11 | run('pkill -f "cli.js start" || set exit 0');
12 | }
13 |
14 | // Make sure our Android native modules are androidX-happy
15 | run('npx jetify');
16 |
17 | // On iOS, make sure CocoaPods are installed
18 | if (process.platform === 'darwin') {
19 | run('if [ -d "ios" ]; then cd ios && pod install && cd -; fi');
20 | }
21 |
22 | // Run baby run
23 | function run(command) {
24 | console.log(`./bin/postInstall script running: ${command}`);
25 |
26 | try {
27 | require('child_process').execSync(command, { stdio: 'inherit' });
28 | } catch (error) {
29 | console.error(`./bin/postInstall failed on command:\n ${command}`);
30 | process.exit(error.status);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/dist/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/dist/demo.gif
--------------------------------------------------------------------------------
/docs/01-how-to-change-app-logo.md:
--------------------------------------------------------------------------------
1 | ## How to change app logo
2 |
3 | 1. Go to https://www.appicon.co/from https://www.appicon.co/ to generate logo for iOS, Android
4 | 2. Go to https://icon.kitchen/ to generate logo rounded for Android
5 | 3. After generate
6 |
7 | #### iOS
8 |
9 | 1. Unzip file from https://www.appicon.co/
10 | 2. Copy Dowloads/Assets.xcassets/AppIcon.appiconset to react-native-starter/ios/starter/Images.xcassets
11 | 3. Done
12 |
13 | #### Android
14 |
15 | 1. Unzip downloaded file from https://www.appicon.co/
16 | 2. Copy all Downloads/android/\* to react-native-starter/android/app/src/main/res
17 | 3. Unzip downloaded file from https://icon.kitchen/
18 | 4. Copy merge folder Downloads/ic_launcher/res/\* to react-native-starter/android/app/src/main/res
19 | 5. Done
20 |
--------------------------------------------------------------------------------
/docs/02-how-to-change-splash-screen.md:
--------------------------------------------------------------------------------
1 | ## How to change plash screen logo
2 |
3 | 1. Copy image to assets folder
4 | 2. Fix params and run CMD
5 |
6 | ```
7 | yarn react-native generate-bootsplash assets/ \
8 | --platforms=android,ios \
9 | --background=FFFFFF \
10 | --logo-width=150 \
11 | --assets-output=assets \
12 | --flavor=main \
13 | ```
14 |
15 | 3. Done
16 |
--------------------------------------------------------------------------------
/docs/03-how-to-keep-code-changed-from-node-modules.md:
--------------------------------------------------------------------------------
1 | ## How to keep code changed from node_modules
2 |
3 | 1. Run cmd
4 |
5 | ```
6 | npx patch-package
7 | ```
8 |
9 | 2. Check patch file at react-native-starter/patches
10 | 3. Run cmd to patch
11 |
12 | ```
13 | yarn patch
14 | ```
15 |
16 | In project set up auto run patch-package after install package, so can ignore step 3
17 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler'
2 | import './src/localization/i18n'
3 | import { AppRegistry, LogBox } from 'react-native'
4 | import { name as appName } from './app.json'
5 | import { Starter } from './src/starter'
6 |
7 | const IGNORED_LOGS = ['Warning: Function components cannot be given refs']
8 |
9 | LogBox.ignoreLogs(IGNORED_LOGS)
10 |
11 | if (__DEV__) {
12 | const withoutIgnored =
13 | logger =>
14 | (...args) => {
15 | const output = args.join(' ')
16 | if (!IGNORED_LOGS.some(log => output.includes(log))) {
17 | logger(...args)
18 | }
19 | }
20 |
21 | console.log = withoutIgnored(console.log)
22 | console.info = withoutIgnored(console.info)
23 | console.warn = withoutIgnored(console.warn)
24 | console.error = withoutIgnored(console.error)
25 | }
26 |
27 | AppRegistry.registerComponent(appName, () => Starter)
28 |
--------------------------------------------------------------------------------
/ios/.xcode.env:
--------------------------------------------------------------------------------
1 | # This `.xcode.env` file is versioned and is used to source the environment
2 | # used when running script phases inside Xcode.
3 | # To customize your local environment, you can create an `.xcode.env.local`
4 | # file that is not versioned.
5 |
6 | # NODE_BINARY variable contains the PATH to the node executable.
7 | #
8 | # Customize the NODE_BINARY variable here.
9 | # For example, to use nvm with brew, add the following line
10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use
11 | export NODE_BINARY=$(command -v node)
12 |
--------------------------------------------------------------------------------
/ios/Config.xcconfig:
--------------------------------------------------------------------------------
1 | //
2 | // Config.xcconfig
3 | // starter
4 | //
5 | // Created by BAPVN-THOAINT on 10/07/2022.
6 | //
7 |
8 | // Configuration settings file format documentation can be found at:
9 | // https://help.apple.com/xcode/#/dev745c5c974
10 |
--------------------------------------------------------------------------------
/ios/DevelopmentExportOptions.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | destination
6 | export
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Resolve react_native_pods.rb with node to allow for hoisting
2 | require Pod::Executable.execute_command('node', ['-p',
3 | 'require.resolve(
4 | "react-native/scripts/react_native_pods.rb",
5 | {paths: [process.argv[1]]},
6 | )', __dir__]).strip
7 |
8 | platform :ios, min_ios_version_supported
9 | prepare_react_native_project!
10 |
11 | # If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
12 | # because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
13 | #
14 | # To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
15 | # ```js
16 | # module.exports = {
17 | # dependencies: {
18 | # ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
19 | # ```
20 | flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled
21 |
22 | linkage = ENV['USE_FRAMEWORKS']
23 | if linkage != nil
24 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
25 | use_frameworks! :linkage => linkage.to_sym
26 | end
27 |
28 | project 'starter',
29 | 'Debug' => :debug,
30 | 'Release' => :release
31 |
32 | abstract_target 'starter' do
33 | config = use_native_modules!
34 |
35 | # Flags change depending on the env values.
36 | flags = get_default_flags()
37 |
38 | use_react_native!(
39 | :path => config[:reactNativePath],
40 | # Hermes is now enabled by default. Disable by setting this flag to false.
41 | :hermes_enabled => flags[:hermes_enabled],
42 | :fabric_enabled => flags[:fabric_enabled],
43 | # Enables Flipper.
44 | #
45 | # Note that if you have use_frameworks! enabled, Flipper will not work and
46 | # you should disable the next line.
47 | :flipper_configuration => flipper_config,
48 | # An absolute path to your application root.
49 | :app_path => "#{Pod::Config.instance.installation_root}/.."
50 | )
51 |
52 | target 'production' do
53 | end
54 | target 'staging' do
55 | end
56 | target 'development' do
57 | end
58 |
59 | post_install do |installer|
60 | installer.pods_project.targets.each do |target|
61 | target.build_configurations.each do |config|
62 | if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
63 | target.build_configurations.each do |config|
64 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
65 | end
66 | end
67 | end
68 | end
69 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
70 | react_native_post_install(
71 | installer,
72 | config[:reactNativePath],
73 | :mac_catalyst_enabled => false
74 | )
75 | __apply_Xcode_12_5_M1_post_install_workaround(installer)
76 | end
77 | end
78 |
--------------------------------------------------------------------------------
/ios/ProductionExportOptions.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | destination
6 | export
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/StagingExportOptions.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | destination
6 | export
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/development/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Starter Development
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UIAppFonts
41 |
42 | Poppins-Black.ttf
43 | Poppins-BlackItalic.ttf
44 | Poppins-Bold.ttf
45 | Poppins-BoldItalic.ttf
46 | Poppins-ExtraBold.ttf
47 | Poppins-ExtraBoldItalic.ttf
48 | Poppins-ExtraLight.ttf
49 | Poppins-ExtraLightItalic.ttf
50 | Poppins-Italic.ttf
51 | Poppins-Light.ttf
52 | Poppins-LightItalic.ttf
53 | Poppins-Medium.ttf
54 | Poppins-MediumItalic.ttf
55 | Poppins-Regular.ttf
56 | Poppins-SemiBold.ttf
57 | Poppins-SemiBoldItalic.ttf
58 | Poppins-Thin.ttf
59 | Poppins-ThinItalic.ttf
60 |
61 | UILaunchStoryboardName
62 | BootSplash
63 | UIRequiredDeviceCapabilities
64 |
65 | armv7
66 |
67 | UISupportedInterfaceOrientations
68 |
69 | UIInterfaceOrientationPortrait
70 |
71 | UISupportedInterfaceOrientations~ipad
72 |
73 | UIInterfaceOrientationLandscapeLeft
74 | UIInterfaceOrientationLandscapeRight
75 | UIInterfaceOrientationPortrait
76 |
77 | UIViewControllerBasedStatusBarAppearance
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/link-assets-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "migIndex": 1,
3 | "data": [
4 | {
5 | "path": "src/assets/fonts/Poppins-Black.ttf",
6 | "sha1": "645e04c53c6b5b35bce654a811ebce16af8aa721"
7 | },
8 | {
9 | "path": "src/assets/fonts/Poppins-BlackItalic.ttf",
10 | "sha1": "be4ef2aac7775a4b0179c0ecfce6cea8f7783f63"
11 | },
12 | {
13 | "path": "src/assets/fonts/Poppins-Bold.ttf",
14 | "sha1": "875cf0cecd647bcf22e79d633d868c1b1ec98dfa"
15 | },
16 | {
17 | "path": "src/assets/fonts/Poppins-BoldItalic.ttf",
18 | "sha1": "6de8952ca08d27ee77f57347d2cef4b4e26aa78f"
19 | },
20 | {
21 | "path": "src/assets/fonts/Poppins-ExtraBold.ttf",
22 | "sha1": "4b5c0750f073abd576413a0898d3b95adaf199c8"
23 | },
24 | {
25 | "path": "src/assets/fonts/Poppins-ExtraBoldItalic.ttf",
26 | "sha1": "94d3ee3acdbd00e37a715a25f2b5237666b5b8a3"
27 | },
28 | {
29 | "path": "src/assets/fonts/Poppins-ExtraLight.ttf",
30 | "sha1": "85af6582a7e6155917c605f9d3fed68c02b23b06"
31 | },
32 | {
33 | "path": "src/assets/fonts/Poppins-ExtraLightItalic.ttf",
34 | "sha1": "6fa0418f91032680d2d8fe4843009f2c80faa61d"
35 | },
36 | {
37 | "path": "src/assets/fonts/Poppins-Italic.ttf",
38 | "sha1": "e2bfcd56016c3c915221ef1c5fc1a17b0776eb91"
39 | },
40 | {
41 | "path": "src/assets/fonts/Poppins-Light.ttf",
42 | "sha1": "e247a92158e112f8bf7b638c8d95381d66b00dbb"
43 | },
44 | {
45 | "path": "src/assets/fonts/Poppins-LightItalic.ttf",
46 | "sha1": "f76102b361e705751a83b7b22fe954daf1f27dd4"
47 | },
48 | {
49 | "path": "src/assets/fonts/Poppins-Medium.ttf",
50 | "sha1": "283f21b44efbdbf276ba802be2d949a36bbc4233"
51 | },
52 | {
53 | "path": "src/assets/fonts/Poppins-MediumItalic.ttf",
54 | "sha1": "b592c62fdf88c40cfd0f031bba6423b1d4430472"
55 | },
56 | {
57 | "path": "src/assets/fonts/Poppins-Regular.ttf",
58 | "sha1": "fdd3002e7d814ee47c1c1b8487c72c6bbb3a2d00"
59 | },
60 | {
61 | "path": "src/assets/fonts/Poppins-SemiBold.ttf",
62 | "sha1": "8a4ace9392d06bcb7f8ea2f5169b07e4c383a90d"
63 | },
64 | {
65 | "path": "src/assets/fonts/Poppins-SemiBoldItalic.ttf",
66 | "sha1": "0c8bed0a5942b2388d36b11c115685d9f01700b0"
67 | },
68 | {
69 | "path": "src/assets/fonts/Poppins-Thin.ttf",
70 | "sha1": "09ba4dcd5509c8085bf88c665dcc51cbdfced27b"
71 | },
72 | {
73 | "path": "src/assets/fonts/Poppins-ThinItalic.ttf",
74 | "sha1": "fb8fccca21550e71135f92bbaf8a2e8a12c90465"
75 | }
76 | ]
77 | }
78 |
--------------------------------------------------------------------------------
/ios/staging/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Starter Staging
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UIAppFonts
41 |
42 | Poppins-Black.ttf
43 | Poppins-BlackItalic.ttf
44 | Poppins-Bold.ttf
45 | Poppins-BoldItalic.ttf
46 | Poppins-ExtraBold.ttf
47 | Poppins-ExtraBoldItalic.ttf
48 | Poppins-ExtraLight.ttf
49 | Poppins-ExtraLightItalic.ttf
50 | Poppins-Italic.ttf
51 | Poppins-Light.ttf
52 | Poppins-LightItalic.ttf
53 | Poppins-Medium.ttf
54 | Poppins-MediumItalic.ttf
55 | Poppins-Regular.ttf
56 | Poppins-SemiBold.ttf
57 | Poppins-SemiBoldItalic.ttf
58 | Poppins-Thin.ttf
59 | Poppins-ThinItalic.ttf
60 |
61 | UILaunchStoryboardName
62 | BootSplash
63 | UIRequiredDeviceCapabilities
64 |
65 | armv7
66 |
67 | UISupportedInterfaceOrientations
68 |
69 | UIInterfaceOrientationPortrait
70 |
71 | UISupportedInterfaceOrientations~ipad
72 |
73 | UIInterfaceOrientationLandscapeLeft
74 | UIInterfaceOrientationLandscapeRight
75 | UIInterfaceOrientationPortrait
76 |
77 | UIViewControllerBasedStatusBarAppearance
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/starter.xcodeproj/xcshareddata/xcschemes/development.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/starter.xcodeproj/xcshareddata/xcschemes/production.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/starter.xcodeproj/xcshareddata/xcschemes/staging.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
11 |
14 |
15 |
16 |
17 |
18 |
24 |
30 |
31 |
32 |
33 |
34 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/starter.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/starter.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/starter/AppDelegate.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @interface AppDelegate : RCTAppDelegate
5 |
6 | @end
7 |
--------------------------------------------------------------------------------
/ios/starter/AppDelegate.mm:
--------------------------------------------------------------------------------
1 | #import "AppDelegate.h"
2 | #import "RNBootSplash.h"
3 |
4 | #import
5 |
6 | @implementation AppDelegate
7 |
8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
9 | {
10 | self.moduleName = @"starter";
11 | // You can add your custom initial props in the dictionary below.
12 | // They will be passed down to the ViewController used by React Native.
13 | self.initialProps = @{};
14 |
15 | return [super application:application didFinishLaunchingWithOptions:launchOptions];
16 | }
17 |
18 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
19 | {
20 | #if DEBUG
21 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
22 | #else
23 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
24 | #endif
25 | }
26 |
27 | - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge
28 | moduleName:(NSString *)moduleName
29 | initProps:(NSDictionary *)initProps {
30 | UIView *rootView = [super createRootViewWithBridge:bridge
31 | moduleName:moduleName
32 | initProps:initProps];
33 |
34 | [RNBootSplash initWithStoryboard:@"BootSplash" rootView:rootView]; // ⬅️ initialize the splash screen
35 |
36 | return rootView;
37 | }
38 |
39 | @end
40 |
--------------------------------------------------------------------------------
/ios/starter/BootSplash.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "iphone",
5 | "scale": "2x",
6 | "size": "20x20"
7 | },
8 | {
9 | "idiom": "iphone",
10 | "scale": "3x",
11 | "size": "20x20"
12 | },
13 | {
14 | "idiom": "iphone",
15 | "scale": "2x",
16 | "size": "29x29"
17 | },
18 | {
19 | "idiom": "iphone",
20 | "scale": "3x",
21 | "size": "29x29"
22 | },
23 | {
24 | "idiom": "iphone",
25 | "scale": "2x",
26 | "size": "40x40"
27 | },
28 | {
29 | "idiom": "iphone",
30 | "scale": "3x",
31 | "size": "40x40"
32 | },
33 | {
34 | "idiom": "iphone",
35 | "scale": "2x",
36 | "size": "60x60"
37 | },
38 | {
39 | "idiom": "iphone",
40 | "scale": "3x",
41 | "size": "60x60"
42 | },
43 | {
44 | "idiom": "ios-marketing",
45 | "scale": "1x",
46 | "size": "1024x1024"
47 | }
48 | ],
49 | "info": {
50 | "author": "xcode",
51 | "version": 1
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/BootSplashLogo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images": [
3 | {
4 | "idiom": "universal",
5 | "filename": "bootsplash_logo.png",
6 | "scale": "1x"
7 | },
8 | {
9 | "idiom": "universal",
10 | "filename": "bootsplash_logo@2x.png",
11 | "scale": "2x"
12 | },
13 | {
14 | "idiom": "universal",
15 | "filename": "bootsplash_logo@3x.png",
16 | "scale": "3x"
17 | }
18 | ],
19 | "info": {
20 | "version": 1,
21 | "author": "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo.png
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo@2x.png
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/ios/starter/Images.xcassets/BootSplashLogo.imageset/bootsplash_logo@3x.png
--------------------------------------------------------------------------------
/ios/starter/Images.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info": {
3 | "version": 1,
4 | "author": "xcode"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ios/starter/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Starter
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSExceptionDomains
30 |
31 | localhost
32 |
33 | NSExceptionAllowsInsecureHTTPLoads
34 |
35 |
36 |
37 |
38 | NSLocationWhenInUseUsageDescription
39 |
40 | UIAppFonts
41 |
42 | Poppins-Black.ttf
43 | Poppins-BlackItalic.ttf
44 | Poppins-Bold.ttf
45 | Poppins-BoldItalic.ttf
46 | Poppins-ExtraBold.ttf
47 | Poppins-ExtraBoldItalic.ttf
48 | Poppins-ExtraLight.ttf
49 | Poppins-ExtraLightItalic.ttf
50 | Poppins-Italic.ttf
51 | Poppins-Light.ttf
52 | Poppins-LightItalic.ttf
53 | Poppins-Medium.ttf
54 | Poppins-MediumItalic.ttf
55 | Poppins-Regular.ttf
56 | Poppins-SemiBold.ttf
57 | Poppins-SemiBoldItalic.ttf
58 | Poppins-Thin.ttf
59 | Poppins-ThinItalic.ttf
60 |
61 | UILaunchStoryboardName
62 | BootSplash
63 | UIRequiredDeviceCapabilities
64 |
65 | armv7
66 |
67 | UISupportedInterfaceOrientations
68 |
69 | UIInterfaceOrientationPortrait
70 |
71 | UISupportedInterfaceOrientations~ipad
72 |
73 | UIInterfaceOrientationLandscapeLeft
74 | UIInterfaceOrientationLandscapeRight
75 | UIInterfaceOrientationPortrait
76 |
77 | UIViewControllerBasedStatusBarAppearance
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/ios/starter/main.m:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | #import "AppDelegate.h"
4 |
5 | int main(int argc, char *argv[])
6 | {
7 | @autoreleasepool {
8 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ios/starterTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/ios/starterTests/starterTests.m:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | #import
5 | #import
6 |
7 | #define TIMEOUT_SECONDS 600
8 | #define TEXT_TO_LOOK_FOR @"Welcome to React"
9 |
10 | @interface starterTests : XCTestCase
11 |
12 | @end
13 |
14 | @implementation starterTests
15 |
16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
17 | {
18 | if (test(view)) {
19 | return YES;
20 | }
21 | for (UIView *subview in [view subviews]) {
22 | if ([self findSubviewInView:subview matching:test]) {
23 | return YES;
24 | }
25 | }
26 | return NO;
27 | }
28 |
29 | - (void)testRendersWelcomeScreen
30 | {
31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
33 | BOOL foundElement = NO;
34 |
35 | __block NSString *redboxError = nil;
36 | #ifdef DEBUG
37 | RCTSetLogFunction(
38 | ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
39 | if (level >= RCTLogLevelError) {
40 | redboxError = message;
41 | }
42 | });
43 | #endif
44 |
45 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
46 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
47 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
48 |
49 | foundElement = [self findSubviewInView:vc.view
50 | matching:^BOOL(UIView *view) {
51 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
52 | return YES;
53 | }
54 | return NO;
55 | }];
56 | }
57 |
58 | #ifdef DEBUG
59 | RCTSetLogFunction(RCTDefaultLogFunction);
60 | #endif
61 |
62 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
63 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | preset: 'react-native',
3 | setupFiles: ['/jest/setup.js'],
4 | setupFilesAfterEnv: ['@testing-library/jest-native/extend-expect'],
5 | transformIgnorePatterns: [
6 | 'node_modules/(?!(jest-)?react-native|react-clone-referenced-element|@react-native-community|@react-native|@react-navigation|moti/.*)',
7 | ],
8 | };
9 |
--------------------------------------------------------------------------------
/jest/setup.js:
--------------------------------------------------------------------------------
1 | import 'react-native-gesture-handler/jestSetup';
2 |
3 | jest.mock('react-native-reanimated', () => {
4 | const Reanimated = require('react-native-reanimated/mock');
5 |
6 | // The mock for `call` immediately calls the callback which is incorrect
7 | // So we override it with a no-op
8 | Reanimated.default.call = () => {};
9 |
10 | return Reanimated;
11 | });
12 |
13 | // Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing
14 | jest.mock('react-native/Libraries/Animated/NativeAnimatedHelper');
15 |
16 | jest.mock('react-native-bootsplash', () => {
17 | return {
18 | hide: jest.fn().mockResolvedValueOnce(),
19 | getVisibilityStatus: jest.fn().mockResolvedValue('hidden'),
20 | };
21 | });
22 |
--------------------------------------------------------------------------------
/metro.config.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Metro configuration for React Native
3 | * https://github.com/facebook/react-native
4 | *
5 | * @format
6 | */
7 |
8 | const { getDefaultConfig } = require('metro-config')
9 |
10 | module.exports = (async () => {
11 | const {
12 | resolver: { sourceExts, assetExts },
13 | } = await getDefaultConfig()
14 | return {
15 | transformer: {
16 | getTransformOptions: async () => ({
17 | transform: {
18 | experimentalImportSupport: false,
19 | inlineRequires: true,
20 | },
21 | }),
22 | babelTransformerPath: require.resolve('react-native-svg-transformer'),
23 | },
24 | resolver: {
25 | assetExts: assetExts.filter(ext => ext !== 'svg'),
26 | sourceExts: [...sourceExts, 'svg'],
27 | },
28 | }
29 | })()
30 |
--------------------------------------------------------------------------------
/patches/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/patches/.gitignore
--------------------------------------------------------------------------------
/react-native.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | project: {
3 | ios: {},
4 | android: {},
5 | },
6 | assets: ['./src/assets/fonts/'],
7 | };
8 |
--------------------------------------------------------------------------------
/scripts/fixfonts.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # fixfonts.sh
3 | # android/app/src/main/res/font
4 | typeset folder="$1"
5 | if [[ -d "$folder" && ! -z "$folder" ]]; then
6 | pushd "$folder";
7 | for file in *.ttf; do
8 | typeset normalized="${file//-/_}";
9 | normalized="$(tr [A-Z] [a-z] <<< "$normalized")";
10 | mv "$file" "$normalized";
11 | done
12 | popd
13 | fi
14 |
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Black.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-BlackItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Bold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-BoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-ExtraBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-ExtraBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-ExtraBoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-ExtraLight.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-ExtraLightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-ExtraLightItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Italic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Light.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-LightItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-MediumItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Regular.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-SemiBold.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-SemiBoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-SemiBoldItalic.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-Thin.ttf
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bonnguyenitc/react-native-starter/ead2b4a5b8448ccf3d5baa34974b0d65f8d1f0d6/src/assets/fonts/Poppins-ThinItalic.ttf
--------------------------------------------------------------------------------
/src/assets/images/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/src/assets/index.ts:
--------------------------------------------------------------------------------
1 | export * from './images'
2 | export * from './jsons'
3 |
--------------------------------------------------------------------------------
/src/assets/jsons/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/src/assets/jsons/spinner.json:
--------------------------------------------------------------------------------
1 | {"v":"5.5.5","fr":25,"ip":0,"op":79,"w":300,"h":150,"nm":"Loading-2","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"icon 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[-90]},{"t":79,"s":[270]}],"ix":10},"p":{"a":0,"k":[150,75,0],"ix":2},"a":{"a":0,"k":[53,53,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-15.464],[15.464,0],[0,15.464],[-15.464,0]],"o":[[0,15.464],[-15.464,0],[0,-15.464],[15.464,0]],"v":[[28,0],[0,28],[-28,0],[0,-28]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.058823529411764705,0.3843137254901961,0.996078431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[53,53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":51,"s":[0]},{"t":79,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[0]},{"t":64,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":39,"op":79,"st":39,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"icon","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[-90]},{"t":40,"s":[270]}],"ix":10},"p":{"a":0,"k":[150,75,0],"ix":2},"a":{"a":0,"k":[53,53,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-15.464],[15.464,0],[0,15.464],[-15.464,0]],"o":[[0,15.464],[-15.464,0],[0,-15.464],[15.464,0]],"v":[[28,0],[0,28],[-28,0],[0,-28]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.058823529411764705,0.3843137254901961,0.996078431372549,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":2,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[53,53],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12,"s":[0]},{"t":40,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":25,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":40,"st":0,"bm":0}],"markers":[]}
--------------------------------------------------------------------------------
/src/common/config/index.ts:
--------------------------------------------------------------------------------
1 | import Config from 'react-native-config'
2 |
3 | export const { APP_NAME, API_URL } = Config
4 |
--------------------------------------------------------------------------------
/src/common/constants/dimension.ts:
--------------------------------------------------------------------------------
1 | import { Dimensions } from 'react-native'
2 |
3 | export const WIDTH = Dimensions.get('window').width
4 | export const HEIGHT = Dimensions.get('window').height
5 |
--------------------------------------------------------------------------------
/src/common/constants/index.ts:
--------------------------------------------------------------------------------
1 | export * from './dimension'
2 | export * from './platform'
3 | export * from './styles'
4 | export * from './transitions'
5 |
--------------------------------------------------------------------------------
/src/common/constants/platform.ts:
--------------------------------------------------------------------------------
1 | import { Platform } from 'react-native'
2 |
3 | export const Android = Platform.OS === 'android'
4 | export const iOS = Platform.OS === 'ios'
5 |
--------------------------------------------------------------------------------
/src/common/constants/styles.ts:
--------------------------------------------------------------------------------
1 | import { ViewStyle } from 'react-native'
2 |
3 | import { ViewProps } from '../types'
4 |
5 | export type StyleName = 'flex_1'
6 |
7 | export const styles: { [key in StyleName]: ViewStyle } = {
8 | flex_1: {
9 | flex: 1,
10 | },
11 | }
12 |
13 | export type ShadowName = 'normal'
14 |
15 | export const shadows: { [key in ShadowName]: ViewProps } = {
16 | normal: {
17 | elevation: 5,
18 | shadowColor: 'shadow.default',
19 | shadowOffset: { width: 0, height: 0 },
20 | shadowOpacity: 0.3,
21 | shadowRadius: 10,
22 | },
23 | }
24 |
25 | export const borderRadiusSizes = {
26 | tiny: 4,
27 | small: 8,
28 | medium: 12,
29 | large: 24,
30 | xl: 32,
31 | huge: 64,
32 | }
33 |
--------------------------------------------------------------------------------
/src/common/constants/transitions.ts:
--------------------------------------------------------------------------------
1 | import { Easing } from 'react-native-reanimated'
2 |
3 | import { MotiTransitionProp } from 'moti'
4 |
5 | export const transitions: { [key: string]: MotiTransitionProp } = {
6 | screen: { type: 'timing', duration: 450, easing: Easing.inOut(Easing.ease) },
7 | }
8 |
--------------------------------------------------------------------------------
/src/common/hooks/__tests__/useDisclosure.test.ts:
--------------------------------------------------------------------------------
1 | import { act, renderHook } from '@testing-library/react-hooks'
2 |
3 | import { useDisclosure } from '../useDisclosure'
4 |
5 | test('should open the state', () => {
6 | const { result } = renderHook(() => useDisclosure())
7 |
8 | expect(result.current.isOpen).toBe(false)
9 |
10 | act(() => {
11 | result.current.open()
12 | })
13 |
14 | expect(result.current.isOpen).toBe(true)
15 | })
16 |
17 | test('should close the state', () => {
18 | const { result } = renderHook(() => useDisclosure())
19 |
20 | expect(result.current.isOpen).toBe(false)
21 |
22 | act(() => {
23 | result.current.close()
24 | })
25 |
26 | expect(result.current.isOpen).toBe(false)
27 | })
28 |
29 | test('should toggle the state', () => {
30 | const { result } = renderHook(() => useDisclosure())
31 |
32 | expect(result.current.isOpen).toBe(false)
33 |
34 | act(() => {
35 | result.current.toggle()
36 | })
37 |
38 | expect(result.current.isOpen).toBe(true)
39 |
40 | act(() => {
41 | result.current.toggle()
42 | })
43 |
44 | expect(result.current.isOpen).toBe(false)
45 | })
46 |
47 | test('should define initial state', () => {
48 | const { result } = renderHook(() => useDisclosure(true))
49 |
50 | expect(result.current.isOpen).toBe(true)
51 |
52 | act(() => {
53 | result.current.toggle()
54 | })
55 |
56 | expect(result.current.isOpen).toBe(false)
57 | })
58 |
--------------------------------------------------------------------------------
/src/common/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useBarStyle'
2 | export * from './useDisclosure'
3 | export * from './usePrevious'
4 | export * from './useTranslation'
5 | export * from './useWhyYouUpdate'
6 |
--------------------------------------------------------------------------------
/src/common/hooks/useBarStyle.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from 'react'
2 | import { StatusBar } from 'react-native'
3 |
4 | import { useThemeStore } from '@/common/stores'
5 |
6 | export const useBarStyle = () => {
7 | const isDarkMode = useThemeStore(state => state.isDarkMode)
8 |
9 | useEffect(() => {
10 | StatusBar.setBarStyle(isDarkMode ? 'light-content' : 'dark-content')
11 | }, [isDarkMode])
12 | }
13 |
--------------------------------------------------------------------------------
/src/common/hooks/useDisclosure.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from 'react'
2 |
3 | export const useDisclosure = (initial = false) => {
4 | const [isOpen, setIsOpen] = useState(initial)
5 |
6 | const open = useCallback(() => setIsOpen(true), [])
7 | const close = useCallback(() => setIsOpen(false), [])
8 | const toggle = useCallback(() => setIsOpen(state => !state), [])
9 |
10 | return { isOpen, open, close, toggle }
11 | }
12 |
--------------------------------------------------------------------------------
/src/common/hooks/usePrevious.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | export const usePrevious = (value: T): T | undefined => {
4 | const previousValue = useRef()
5 |
6 | useEffect(() => {
7 | previousValue.current = value
8 | }, [value])
9 |
10 | return previousValue.current
11 | }
12 |
--------------------------------------------------------------------------------
/src/common/hooks/useTranslation.ts:
--------------------------------------------------------------------------------
1 | import { useTranslation as useTranslationLib } from 'react-i18next'
2 |
3 | export const useTranslation = () => {
4 | const { t, i18n } = useTranslationLib()
5 | return { i18n, t }
6 | }
7 |
--------------------------------------------------------------------------------
/src/common/hooks/useWhyYouUpdate.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from 'react'
2 |
3 | import { logger } from '../utils/logger'
4 |
5 | export const useWhyYouUpdate = (name: string, props: any) => {
6 | const previousProps = useRef()
7 |
8 | useEffect(() => {
9 | if (previousProps.current) {
10 | const allKeys = Object.keys({ ...previousProps.current, ...props })
11 | const changesObj: { [key: string]: any } = {}
12 | allKeys.forEach(key => {
13 | if (previousProps.current[key] !== props[key]) {
14 | changesObj[key] = {
15 | from: previousProps.current[key],
16 | to: props[key],
17 | }
18 | }
19 | })
20 | if (Object.keys(changesObj).length) {
21 | logger.log('[why-did-you-update]', name, changesObj)
22 | }
23 | }
24 | previousProps.current = props
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/src/common/stores/__tests__/notifications.test.ts:
--------------------------------------------------------------------------------
1 | import { act, renderHook } from '@testing-library/react-hooks'
2 |
3 | import { Notification, useNotificationStore } from '../useNotificationStore'
4 |
5 | test('should add and remove notifications', () => {
6 | const { result } = renderHook(() => useNotificationStore())
7 |
8 | expect(result.current.notifications.length).toBe(0)
9 |
10 | const notification: Notification = {
11 | id: '123',
12 | title: 'Hello World',
13 | type: 'info',
14 | message: 'This is a notification',
15 | }
16 |
17 | act(() => {
18 | result.current.addNotification(notification)
19 | })
20 |
21 | expect(result.current.notifications).toContainEqual(notification)
22 |
23 | act(() => {
24 | result.current.dismissNotification(notification.id)
25 | })
26 |
27 | expect(result.current.notifications).not.toContainEqual(notification)
28 | })
29 |
--------------------------------------------------------------------------------
/src/common/stores/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useNotificationStore'
2 | export * from './useThemeStore'
3 |
--------------------------------------------------------------------------------
/src/common/stores/useNotificationStore.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | export type Notification = {
4 | id: string
5 | type: 'info' | 'warning' | 'success' | 'error'
6 | title: string
7 | message?: string
8 | }
9 |
10 | type State = {
11 | notifications: Notification[]
12 | }
13 |
14 | type Action = {
15 | addNotification: (notification: Omit) => void
16 | dismissNotification: (id: string) => void
17 | }
18 |
19 | export const useNotificationStore = create(set => ({
20 | notifications: [],
21 | addNotification: notification =>
22 | set(state => ({
23 | notifications: [...state.notifications, { id: Math.random().toString(), ...notification }],
24 | })),
25 | dismissNotification: id =>
26 | set(state => ({
27 | notifications: state.notifications.filter(notification => notification.id !== id),
28 | })),
29 | }))
30 |
--------------------------------------------------------------------------------
/src/common/stores/useThemeStore.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 | import { createJSONStorage, persist } from 'zustand/middleware'
3 |
4 | import { MMKVStorage } from '@/common/utils/storage'
5 |
6 | export type State = {
7 | isDarkMode: boolean
8 | }
9 |
10 | export type Action = {
11 | toggleMode: () => void
12 | }
13 |
14 | export const useThemeStore = create(
15 | persist(
16 | set => ({
17 | isDarkMode: false,
18 | toggleMode: () => {
19 | set(state => {
20 | return {
21 | isDarkMode: !state.isDarkMode,
22 | }
23 | })
24 | },
25 | }),
26 | {
27 | name: 'theme-store',
28 | storage: createJSONStorage(() => MMKVStorage),
29 | partialize: state => ({ isDarkMode: state.isDarkMode } as State & Action),
30 | },
31 | ),
32 | )
33 |
--------------------------------------------------------------------------------
/src/common/themes/color.ts:
--------------------------------------------------------------------------------
1 | import { palette } from './palette'
2 |
3 | export type ColorName =
4 | | 'transparent'
5 | | 'background.default'
6 | | 'primary'
7 | | 'secondary'
8 | | 'line'
9 | | 'text.default'
10 | | 'dim'
11 | | 'error'
12 | | 'highlight'
13 | | 'background.switchTrack'
14 | | 'background.switch'
15 | | 'shadow.default'
16 |
17 | export const color: { [key in ColorName]: string } = {
18 | transparent: 'rgba(0, 0, 0, 0)',
19 |
20 | 'background.default': palette.white,
21 |
22 | primary: palette.white,
23 |
24 | secondary: palette.black,
25 |
26 | line: palette.offWhite,
27 |
28 | 'text.default': palette.black,
29 |
30 | dim: palette.lightGrey,
31 |
32 | error: palette.angry,
33 |
34 | highlight: palette.blue,
35 |
36 | 'background.switch': palette.black,
37 | 'background.switchTrack': palette.white,
38 | 'shadow.default': palette.black,
39 | }
40 |
--------------------------------------------------------------------------------
/src/common/themes/index.ts:
--------------------------------------------------------------------------------
1 | export * from './color'
2 | export * from './spacing'
3 | export * from './theme'
4 | export * from './timing'
5 | export * from './typography'
6 |
--------------------------------------------------------------------------------
/src/common/themes/palette.ts:
--------------------------------------------------------------------------------
1 | type PaletteName =
2 | | 'black'
3 | | 'white'
4 | | 'offWhite'
5 | | 'orange'
6 | | 'orangeDarker'
7 | | 'lightGrey'
8 | | 'lighterGrey'
9 | | 'angry'
10 | | 'deepPurple'
11 | | 'blue'
12 | | 'black05'
13 |
14 | export const palette: { [key in PaletteName]: string } = {
15 | black: '#252525',
16 | white: '#ffffff',
17 | offWhite: '#e6e6e6',
18 | orange: '#FBA928',
19 | orangeDarker: '#EB9918',
20 | lightGrey: '#939AA4',
21 | lighterGrey: '#CDD4DA',
22 | angry: '#dd3333',
23 | deepPurple: '#5D2555',
24 | blue: 'blue',
25 | black05: 'rgba(0,0,0,0.5)',
26 | }
27 |
--------------------------------------------------------------------------------
/src/common/themes/spacing.ts:
--------------------------------------------------------------------------------
1 | export type SpacingName = 'tiny' | 'small' | 'medium' | 'large' | 'huge' | '16px'
2 |
3 | export const spacing: { [key in SpacingName]: number } = {
4 | tiny: 4,
5 | small: 8,
6 | medium: 12,
7 | large: 24,
8 | huge: 64,
9 | '16px': 16,
10 | }
11 |
--------------------------------------------------------------------------------
/src/common/themes/theme.ts:
--------------------------------------------------------------------------------
1 | import { createTheme, useTheme as useThemeRS } from '@shopify/restyle'
2 |
3 | import { color } from './color'
4 | import { palette } from './palette'
5 | import { spacing } from './spacing'
6 | import { typography } from './typography'
7 |
8 | export const lightTheme = createTheme({
9 | colors: {
10 | ...color,
11 | },
12 | spacing: {
13 | ...spacing,
14 | },
15 | textInputVariants: {
16 | defaults: {
17 | flex: 1,
18 | fontSize: 16,
19 | fontFamily: 'Poppins',
20 | lineHeight: 16 * 1.5,
21 | },
22 | normal: {
23 | fontSize: 16,
24 | color: 'text.default',
25 | fontFamily: 'Poppins',
26 | fontWeight: 'bold',
27 | },
28 | },
29 | textVariants: {
30 | defaults: {
31 | fontSize: 16,
32 | color: 'text.default',
33 | fontFamily: 'Poppins',
34 | },
35 | ...typography,
36 | },
37 | breakpoints: {
38 | phone: 0,
39 | tablet: 768,
40 | largeTablet: 1024,
41 | },
42 | isDark: false,
43 | })
44 |
45 | export type Theme = typeof lightTheme
46 |
47 | export const darkTheme: Theme = {
48 | ...lightTheme,
49 | isDark: true,
50 | colors: {
51 | ...lightTheme.colors,
52 | 'background.default': palette.black,
53 | highlight: palette.white,
54 | 'text.default': palette.white,
55 | primary: palette.black,
56 | secondary: palette.white,
57 | 'background.switch': palette.white,
58 | 'background.switchTrack': palette.black,
59 | },
60 | }
61 |
62 | export const useTheme = () => useThemeRS()
63 |
--------------------------------------------------------------------------------
/src/common/themes/timing.ts:
--------------------------------------------------------------------------------
1 | export const timing = {
2 | quick: 300,
3 | }
4 |
--------------------------------------------------------------------------------
/src/common/themes/typography.ts:
--------------------------------------------------------------------------------
1 | import { TextStyle } from 'react-native'
2 |
3 | export type TypoName = 'emoji' | 'normal' | 'light' | 'h1' | 'h2' | 'subTitle'
4 |
5 | export const typography: { [key in TypoName]: TextStyle } = {
6 | emoji: {
7 | fontSize: 50,
8 | color: 'text.default',
9 | fontFamily: 'Poppins',
10 | },
11 | normal: {
12 | fontWeight: 'bold',
13 | fontSize: 16,
14 | color: 'text.default',
15 | fontFamily: 'Poppins',
16 | },
17 | light: {
18 | fontSize: 16,
19 | color: 'text.default',
20 | fontFamily: 'Poppins',
21 | },
22 | h1: {
23 | fontWeight: 'bold',
24 | fontSize: 28,
25 | color: 'text.default',
26 | fontFamily: 'Poppins',
27 | },
28 | h2: {
29 | fontSize: 28,
30 | color: 'text.default',
31 | fontFamily: 'Poppins',
32 | },
33 | subTitle: {
34 | fontSize: 14,
35 | color: 'text.default',
36 | fontFamily: 'Poppins',
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/src/common/types/api.ts:
--------------------------------------------------------------------------------
1 | export type ResponseApi = {
2 | ok: boolean
3 | data?: T
4 | error?: {
5 | message: string
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/common/types/function.ts:
--------------------------------------------------------------------------------
1 | export type CallbackFunction = (param?: T) => V
2 | export type CallbackFunctionRequired = (param: T) => V
3 | export type CallbackFunctionParams = (param1?: T1, param2?: T2) => V
4 | export type CallbackFunctionParamsRequired = (param1: T1, param2: T2) => V
5 | export type AsyncCallbackFunction = (param?: T) => Promise
6 | export type AsyncCallbackFunctionRequired = (param: T) => Promise
7 | export type AsyncCallbackFunctionParams = (arg1?: T1, arg2?: T2) => Promise
8 | export type AsyncCallbackFunctionParamsRequired = (arg1: T1, arg2: T2) => Promise
9 | export type CallbackFunctionVariadicAnyReturn = (...args: any[]) => any
10 | export type CallbackFunctionVariadicAnyReturnAsync = (...args: any[]) => Promise
11 |
--------------------------------------------------------------------------------
/src/common/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './api'
2 | export * from './function'
3 | export * from './ui'
4 |
--------------------------------------------------------------------------------
/src/common/types/ui.ts:
--------------------------------------------------------------------------------
1 | import { StyleProp, ViewProps as ViewPropsRN, ViewStyle } from 'react-native'
2 |
3 | import {
4 | BackgroundColorProps,
5 | BorderProps,
6 | LayoutProps,
7 | PositionProps,
8 | ShadowProps,
9 | SpacingProps,
10 | } from '@shopify/restyle'
11 |
12 | import { Theme } from '@/common/themes'
13 |
14 | export interface ChildrenProps {
15 | children?: React.ReactNode
16 | }
17 | export type ViewProps = { style?: StyleProp } & SpacingProps &
18 | BorderProps &
19 | BackgroundColorProps &
20 | LayoutProps &
21 | PositionProps &
22 | ShadowProps &
23 | ViewPropsRN
24 |
--------------------------------------------------------------------------------
/src/common/utils/axios.ts:
--------------------------------------------------------------------------------
1 | import Axios, { AxiosError, AxiosRequestConfig } from 'axios'
2 |
3 | import { ResponseApi } from '../types/api'
4 | import { API_URL } from '@/common/config'
5 | import { useNotificationStore } from '@/common/stores'
6 | import storage from '@/common/utils/storage'
7 |
8 | function authRequestInterceptor(config: AxiosRequestConfig) {
9 | const token = storage.getAccessToken()
10 | const newConfig = config
11 | if (newConfig && newConfig?.headers) {
12 | if (token) {
13 | newConfig.headers.authorization = `${token}`
14 | }
15 | newConfig.headers.Accept = 'application/json'
16 | newConfig.headers['Content-Type'] = 'application/json'
17 | }
18 | return newConfig
19 | }
20 |
21 | export const axios = Axios.create({
22 | baseURL: API_URL,
23 | timeout: 5000,
24 | })
25 |
26 | axios.interceptors.request.use(authRequestInterceptor)
27 | axios.interceptors.response.use(
28 | response => {
29 | return response.data
30 | },
31 | error => {
32 | const message = error.response?.data?.message || error.message
33 | useNotificationStore.getState().addNotification({
34 | type: 'error',
35 | title: 'Error',
36 | message,
37 | })
38 |
39 | return Promise.reject(error)
40 | },
41 | )
42 |
43 | export const NONE = null
44 | export const CLIENT_ERROR = 'CLIENT_ERROR'
45 | export const SERVER_ERROR = 'SERVER_ERROR'
46 | export const TIMEOUT_ERROR = 'TIMEOUT_ERROR'
47 | export const CONNECTION_ERROR = 'CONNECTION_ERROR'
48 | export const NETWORK_ERROR = 'NETWORK_ERROR'
49 | export const UNKNOWN_ERROR = 'UNKNOWN_ERROR'
50 | export const CANCEL_ERROR = 'CANCEL_ERROR'
51 |
52 | const TIMEOUT_ERROR_CODES = ['ECONNABORTED']
53 | const NODEJS_CONNECTION_ERROR_CODES = ['ENOTFOUND', 'ECONNREFUSED', 'ECONNRESET']
54 |
55 | const inRange = (min: number, max: number, value: number): boolean => value >= min && value <= max
56 |
57 | const in200s = (n: number): boolean => inRange(200, 299, n)
58 | const in400s = (n: number): boolean => inRange(400, 499, n)
59 | const in500s = (n: number): boolean => inRange(500, 599, n)
60 |
61 | type StatusCodes = undefined | number | null
62 |
63 | export const getProblemFromStatus = (status: StatusCodes) => {
64 | if (!status) return UNKNOWN_ERROR
65 | if (in200s(status)) return NONE
66 | if (in400s(status)) return CLIENT_ERROR
67 | if (in500s(status)) return SERVER_ERROR
68 | return UNKNOWN_ERROR
69 | }
70 |
71 | export const getProblemFromError = (error: AxiosError | any) => {
72 | if (error.message === 'Network Error') return NETWORK_ERROR
73 | if (Axios.isCancel(error)) return CANCEL_ERROR
74 | if (!error.code) return getProblemFromStatus(error.response ? error.response.status : null)
75 | if (TIMEOUT_ERROR_CODES.includes(error.code)) return TIMEOUT_ERROR
76 | if (NODEJS_CONNECTION_ERROR_CODES.includes(error.code)) return CONNECTION_ERROR
77 | return UNKNOWN_ERROR
78 | }
79 |
80 | export const generateErrorData = (error: any): ResponseApi => {
81 | return {
82 | ok: false,
83 | error: {
84 | problem: getProblemFromError(error),
85 | ...(typeof error?.response?.data === 'string'
86 | ? { message: error?.response?.data }
87 | : error?.response?.data),
88 | },
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/common/utils/dialog.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleSheet, TouchableWithoutFeedback } from 'react-native'
3 | import RootSiblings from 'react-native-root-siblings'
4 |
5 | import { HEIGHT, WIDTH } from '../constants'
6 | import { palette } from '../themes/palette'
7 | import { FadeView, Spinner } from '@/components/widgets'
8 |
9 | const styles = StyleSheet.create({
10 | backDrop: {
11 | alignItems: 'center',
12 | backgroundColor: palette.black05,
13 | justifyContent: 'center',
14 | position: 'absolute',
15 | },
16 | })
17 |
18 | export const siblings: any = []
19 |
20 | export function hideCustomDialog() {
21 | siblings.forEach((sib: any) => sib?.destroy())
22 | siblings.length = 0
23 | }
24 |
25 | export function showCustomDialog(component: React.ReactNode, isHide = false, bottom = false) {
26 | const style = {
27 | width: WIDTH,
28 | height: HEIGHT,
29 | }
30 |
31 | const sibling = new RootSiblings(
32 | (
33 | // eslint-disable-next-line react/jsx-no-useless-fragment
34 | <>
35 | {isHide ? (
36 |
37 |
46 | {component}
47 |
48 |
49 | ) : (
50 |
59 | {component}
60 |
61 | )}
62 | >
63 | ),
64 | )
65 | siblings.push(sibling as never)
66 | }
67 |
68 | export function hideLoading() {
69 | const current = siblings.shift()
70 | current?.destroy()
71 | }
72 |
73 | export function showLoading() {
74 | showCustomDialog()
75 | }
76 |
--------------------------------------------------------------------------------
/src/common/utils/event-register.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-restricted-syntax */
2 |
3 | import { CallbackFunction } from '../types'
4 |
5 | export class EventRegister {
6 | static listeners: {
7 | count: number
8 | refs: Map>
9 | } = {
10 | count: 0,
11 | refs: new Map(),
12 | }
13 |
14 | static addEventListener(eventName: string, callback: CallbackFunction) {
15 | if (!eventName || !callback || typeof callback !== 'function') return false
16 | EventRegister.listeners.refs.set(eventName, callback)
17 | return true
18 | }
19 |
20 | static removeEventListener(eventName: string) {
21 | if (!eventName) return false
22 | return EventRegister.listeners.refs.delete(eventName)
23 | }
24 |
25 | static removeAllListeners() {
26 | EventRegister.listeners.refs.clear()
27 | }
28 |
29 | static emitEvent(eventName: string, data?: any) {
30 | if (EventRegister.listeners.refs.has(eventName)) {
31 | const callback = EventRegister.listeners.refs.get(eventName)
32 | // eslint-disable-next-line no-unused-expressions
33 | callback && callback(data)
34 | }
35 | }
36 |
37 | static on(eventName: string, callback: CallbackFunction) {
38 | return EventRegister.addEventListener(eventName, callback)
39 | }
40 |
41 | static off(eventName: string) {
42 | return EventRegister.removeEventListener(eventName)
43 | }
44 |
45 | static offAll() {
46 | return EventRegister.removeAllListeners()
47 | }
48 |
49 | static emit(eventName: string, data?: any) {
50 | EventRegister.emitEvent(eventName, data)
51 | }
52 | }
53 |
54 | export const EVENTS = {
55 | EVENT_LOGOUT: '@EVENT_LOGOUT',
56 | }
57 |
--------------------------------------------------------------------------------
/src/common/utils/global-props.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | import { FlatList, ScrollView } from 'react-native'
3 |
4 | const setCustomProps = (component: any, customProps: any) => {
5 | const Component = component.render
6 | const initialDefaultProps = component.defaultProps
7 | component.defaultProps = {
8 | ...initialDefaultProps,
9 | ...customProps,
10 | }
11 | component.render = function render(props: any) {
12 | const oldProps = props
13 | props = { ...props, style: [customProps.style, props.style] }
14 | try {
15 | // eslint-disable-next-line prefer-rest-params
16 | return Component.apply(this, arguments)
17 | } finally {
18 | props = oldProps
19 | }
20 | }
21 | }
22 |
23 | const indicatorProps = {
24 | showsHorizontalScrollIndicator: false,
25 | showsVerticalScrollIndicator: false,
26 | }
27 |
28 | export const hideIndicatorList = () => {
29 | setCustomProps(ScrollView, indicatorProps)
30 | setCustomProps(FlatList, indicatorProps)
31 | }
32 |
--------------------------------------------------------------------------------
/src/common/utils/helpers.ts:
--------------------------------------------------------------------------------
1 | import { Insets } from 'react-native'
2 |
3 | import { AxiosResponse } from 'axios'
4 |
5 | import { ResponseApi } from '../types'
6 | import { generateErrorData } from './axios'
7 |
8 | export const delay = (ms: number) => {
9 | return new Promise(resolve => {
10 | setTimeout(() => resolve(true), ms)
11 | })
12 | }
13 |
14 | export const generateHitSlop = ({
15 | bottom = 10,
16 | left = 10,
17 | right = 10,
18 | top = 10,
19 | }: Insets): Insets => {
20 | return { bottom, left, right, top }
21 | }
22 |
23 | type UploadFileToS3Args = {
24 | url: string
25 | data: any
26 | onError: () => void
27 | onLoad: () => void
28 | onProcess: () => void
29 | }
30 |
31 | export const uploadFileToS3 = ({ url, data, onError, onLoad, onProcess }: UploadFileToS3Args) => {
32 | const photo = {
33 | uri: url,
34 | type: 'image/png',
35 | name: url,
36 | }
37 |
38 | const xhr = new XMLHttpRequest()
39 | xhr.open('PUT', data.preSignedURL)
40 | xhr.setRequestHeader('Content-Type', photo.type)
41 | xhr.upload.onprogress = onProcess
42 | xhr.onload = onLoad
43 | xhr.onerror = onError
44 | xhr.send(photo)
45 | }
46 |
47 | export const wrapApiCall = async (
48 | apiCall: () => Promise>,
49 | ): Promise> => {
50 | try {
51 | const response = await apiCall()
52 | return {
53 | ok: true,
54 | data: response.data,
55 | }
56 | } catch (error) {
57 | // You can customize the error handling as needed
58 | // console.error('API error:', error)
59 | return generateErrorData(error)
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/common/utils/logger.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable consistent-return */
2 | /* eslint-disable prefer-rest-params */
3 |
4 | export const logger = {
5 | debug(...args: any[]) {
6 | const date = new Date().toLocaleTimeString()
7 | Array.prototype.unshift.call(args, `[${date}] ⚡⚡⚡ `)
8 | console.log(...args)
9 | },
10 | error(...args: any[]) {
11 | if (__DEV__) {
12 | console.error(...args)
13 | }
14 | },
15 | log(...args: any[]) {
16 | const date = new Date().toLocaleTimeString()
17 | Array.prototype.unshift.call(args, `[${date}]`)
18 | console.log(...args)
19 | },
20 | warn(...args: any[]) {
21 | console.warn(...args)
22 | },
23 | info(...args: any[]) {
24 | console.info(...args)
25 | },
26 | table(...args: any[]) {
27 | console.table(...args)
28 | },
29 | json(obj: any) {
30 | const date = new Date().toLocaleTimeString()
31 | console.log(`[${date}] JSON ⚡⚡⚡`, JSON.stringify(obj, null, 2))
32 | },
33 | }
34 |
--------------------------------------------------------------------------------
/src/common/utils/magic-memo.ts:
--------------------------------------------------------------------------------
1 | import React, { ComponentProps, ComponentType, MemoExoticComponent } from 'react'
2 | import isEqual from 'react-fast-compare'
3 |
4 | import { pick } from 'lodash'
5 |
6 | type DeepPartial = {
7 | [P in keyof T]?: DeepPartial
8 | }
9 |
10 | export const magicMemo = >(
11 | Component: C,
12 | deps?: string | string[], // This is list keys of props
13 | customComparisonFunc?: (props: DeepPartial>) => boolean,
14 | ): MemoExoticComponent => {
15 | return React.memo(Component, (prev, next) => {
16 | if (!deps) {
17 | if (customComparisonFunc) {
18 | return customComparisonFunc(prev) === customComparisonFunc(next)
19 | }
20 |
21 | return isEqual(prev, next)
22 | }
23 |
24 | const magicDeps = typeof deps === 'string' ? [deps] : deps
25 | const magicPrev = pick(prev, magicDeps) as DeepPartial>
26 | const magicNext = pick(next, magicDeps) as DeepPartial>
27 |
28 | if (customComparisonFunc) {
29 | return customComparisonFunc(magicPrev) === customComparisonFunc(magicNext)
30 | }
31 |
32 | return isEqual(magicPrev, magicNext)
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/src/common/utils/mmvk.ts:
--------------------------------------------------------------------------------
1 | import { MMKV } from 'react-native-mmkv'
2 |
3 | export const mmkv = new MMKV()
4 |
--------------------------------------------------------------------------------
/src/common/utils/never-rerender.ts:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const alwaysTrue = () => true
4 |
5 | export const neverRerender = (Component: React.FC) => {
6 | return React.memo(Component, alwaysTrue)
7 | }
8 |
--------------------------------------------------------------------------------
/src/common/utils/refresh-token-multi-request.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-underscore-dangle */
2 | import axios from 'axios'
3 |
4 | // for multiple requests
5 | let isRefreshing = false
6 | let failedQueue: any[] = []
7 |
8 | const processQueue = (error: any, token = null) => {
9 | failedQueue.forEach(prom => {
10 | if (error) {
11 | prom.reject(error)
12 | } else {
13 | prom.resolve(token)
14 | }
15 | })
16 |
17 | failedQueue = []
18 | }
19 |
20 | export const interceptorOnError = (error: any) => {
21 | const _axios = axios
22 | const originalRequest = error.config
23 |
24 | if (error.response.status === 401 && !originalRequest?._retry) {
25 | if (isRefreshing) {
26 | return new Promise(function (resolve, reject) {
27 | failedQueue.push({ resolve, reject })
28 | })
29 | .then(token => {
30 | originalRequest.headers.Authorization = `Bearer ${token}`
31 | return _axios.request(originalRequest)
32 | })
33 | .catch(err => {
34 | return Promise.reject(err)
35 | })
36 | }
37 |
38 | originalRequest._retry = true
39 | isRefreshing = true
40 | // call api refresh token
41 | return new Promise((resolve, reject) => {
42 | _axios
43 | .post(`${originalRequest?.baseURL}/refresh`)
44 | .then(({ data }) => {
45 | const newToken = data?.data?.token
46 | // save token to local
47 | processQueue(null, newToken)
48 | resolve(_axios(originalRequest))
49 | })
50 | .catch(err => {
51 | processQueue(err, null)
52 | reject(err)
53 | })
54 | .finally(() => {
55 | isRefreshing = false
56 | })
57 | })
58 | }
59 |
60 | return Promise.reject(error)
61 | }
62 |
--------------------------------------------------------------------------------
/src/common/utils/responsive.ts:
--------------------------------------------------------------------------------
1 | import { PixelRatio } from 'react-native'
2 | import { RFValue } from 'react-native-responsive-fontsize'
3 |
4 | import { HEIGHT, WIDTH } from '@/common/constants'
5 |
6 | // change it
7 | const DESIGN_SCREEN_WIDTH = 0
8 | const DESIGN_SCREEN_HEIGHT = 0
9 |
10 | const rWidth = (designWidth: number) => {
11 | return PixelRatio.roundToNearestPixel((WIDTH * designWidth) / DESIGN_SCREEN_WIDTH)
12 | }
13 |
14 | const rHeight = (designHeight: number) => {
15 | return PixelRatio.roundToNearestPixel((HEIGHT * designHeight) / DESIGN_SCREEN_HEIGHT)
16 | }
17 |
18 | const rFontSize = (fontDesign: number) => RFValue(fontDesign, DESIGN_SCREEN_HEIGHT)
19 |
20 | export const responsive = {
21 | rWidth,
22 | rHeight,
23 | rFontSize,
24 | }
25 |
--------------------------------------------------------------------------------
/src/common/utils/storage.ts:
--------------------------------------------------------------------------------
1 | import { StateStorage } from 'zustand/middleware'
2 |
3 | import { mmkv } from './mmvk'
4 |
5 | const KEYS = {
6 | TOKEN: '@TOKEN',
7 | THEME: '@THEME',
8 | }
9 |
10 | export const MMKVStorage: StateStorage = {
11 | getItem: (name: string) => mmkv.getString(name) || null,
12 | setItem: (name: string, value: string) => mmkv.set(name, value),
13 | removeItem: (name: string) => mmkv.delete(name),
14 | }
15 |
16 | const storage = {
17 | saveAccessToken: (data: string) => {
18 | mmkv.set(KEYS.TOKEN, data)
19 | },
20 | getAccessToken: () => {
21 | return mmkv.getString(KEYS.TOKEN)
22 | },
23 | }
24 |
25 | export default storage
26 |
--------------------------------------------------------------------------------
/src/components/forms/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/src/components/modals/confirm/confirm.tsx:
--------------------------------------------------------------------------------
1 | import React, { memo, useCallback } from 'react'
2 |
3 | import { borderRadiusSizes, WIDTH } from '@/common/constants'
4 | import { hideCustomDialog } from '@/common/utils/dialog'
5 | import { Button, Col, Row, Space, Text } from '@/components/widgets'
6 |
7 | type Props = {
8 | onClose?: () => void
9 | onConfirm?: () => void
10 | title: string
11 | content: string
12 | labelConfirm?: string
13 | labelCancel?: string
14 | }
15 |
16 | export const ConfirmModal: React.FC = memo(function ({
17 | onClose,
18 | onConfirm,
19 | title,
20 | content,
21 | labelCancel = 'Cancel',
22 | labelConfirm = 'OK',
23 | }) {
24 | const handleClose = useCallback(() => {
25 | onClose?.()
26 | hideCustomDialog()
27 | }, [onClose])
28 |
29 | const handleConfirm = useCallback(() => {
30 | onConfirm?.()
31 | hideCustomDialog()
32 | }, [onConfirm])
33 |
34 | return (
35 |
40 |
41 |
42 | {title}
43 |
44 |
45 |
46 |
47 | {content}
48 |
49 |
50 |
51 |
61 |
62 |
65 |
66 |
67 | )
68 | })
69 |
--------------------------------------------------------------------------------
/src/components/modals/confirm/index.ts:
--------------------------------------------------------------------------------
1 | export * from './confirm'
2 |
--------------------------------------------------------------------------------
/src/components/modals/index.ts:
--------------------------------------------------------------------------------
1 | export * from './confirm'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/align/align.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | const Alignment = {
7 | bottomCenter: {
8 | justifyContent: 'flex-end',
9 | alignItems: 'center',
10 | },
11 | bottomLeft: {
12 | justifyContent: 'flex-end',
13 | alignItems: 'flex-start',
14 | },
15 | bottomRight: {
16 | justifyContent: 'flex-end',
17 | alignItems: 'flex-end',
18 | },
19 | center: {
20 | justifyContent: 'center',
21 | alignItems: 'center',
22 | },
23 | centerLeft: {
24 | justifyContent: 'center',
25 | alignItems: 'flex-start',
26 | },
27 | centerRight: {
28 | justifyContent: 'center',
29 | alignItems: 'flex-end',
30 | },
31 | topCenter: {
32 | justifyContent: 'flex-start',
33 | alignItems: 'center',
34 | },
35 | topLeft: {
36 | justifyContent: 'flex-start',
37 | alignItems: 'flex-start',
38 | },
39 | topRight: {
40 | justifyContent: 'flex-start',
41 | alignItems: 'flex-end',
42 | },
43 | }
44 |
45 | type Props = {
46 | alignment?: keyof typeof Alignment
47 | }
48 |
49 | export const Align: React.FC = function ({
50 | children,
51 | alignment = 'center',
52 | ...props
53 | }) {
54 | return (
55 |
56 | {children}
57 |
58 | )
59 | }
60 |
--------------------------------------------------------------------------------
/src/components/widgets/align/index.ts:
--------------------------------------------------------------------------------
1 | export * from './align'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/app-bar/app-bar.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react'
2 | import { useSafeAreaInsets } from 'react-native-safe-area-context'
3 |
4 | import { Center } from '../center'
5 | import { Col } from '../col'
6 | import { Row } from '../row'
7 | import { Space } from '../space'
8 | import { Text } from '../text'
9 | import { shadows, WIDTH } from '@/common/constants'
10 | import { ViewProps } from '@/common/types'
11 |
12 | type Props = {
13 | leading?: React.ReactNode
14 | actions?: React.ReactNode
15 | title?: string
16 | safe?: boolean
17 | }
18 |
19 | export const AppBar: React.FC = function ({ leading, actions, title, safe }) {
20 | const insets = useSafeAreaInsets()
21 | const topSpace = useMemo(() => (!safe ? insets.top : 0), [insets.top, safe])
22 | return (
23 |
29 |
30 |
31 | {leading && {leading}}
32 |
33 |
34 | {title}
35 |
36 |
37 | {actions && {actions}
}
38 |
39 |
40 | )
41 | }
42 |
--------------------------------------------------------------------------------
/src/components/widgets/app-bar/index.ts:
--------------------------------------------------------------------------------
1 | export * from './app-bar'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/box/box.tsx:
--------------------------------------------------------------------------------
1 | import { createBox } from '@shopify/restyle'
2 |
3 | import { Theme } from '@/common/themes'
4 |
5 | export const Box = createBox()
6 |
--------------------------------------------------------------------------------
/src/components/widgets/box/index.ts:
--------------------------------------------------------------------------------
1 | export * from './box'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/button/button.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react'
2 | import { StyleProp, TouchableOpacity, ViewStyle } from 'react-native'
3 |
4 | import { MotiPressable } from 'moti/interactions'
5 |
6 | import { Center } from '../center'
7 | import { Text } from '../text'
8 | import { borderRadiusSizes } from '@/common/constants'
9 | import { ColorName, TypoName } from '@/common/themes'
10 | import { palette } from '@/common/themes/palette'
11 | import { ViewProps } from '@/common/types'
12 |
13 | const styleDefault: ViewStyle = {
14 | padding: 8,
15 | paddingHorizontal: 16,
16 | borderRadius: borderRadiusSizes.xl,
17 | }
18 |
19 | const SHADOW: ViewStyle = {
20 | elevation: 5,
21 | shadowColor: palette.black,
22 | shadowOffset: { width: 0, height: 0 },
23 | shadowOpacity: 0.4,
24 | shadowRadius: 10,
25 | }
26 |
27 | type ButtonProps = {
28 | onPress?: () => void
29 | isModal?: boolean
30 | style?: StyleProp
31 | labelColor?: ColorName
32 | shadow?: boolean
33 | labelVariant?: TypoName
34 | custom?: boolean
35 | disabled?: boolean
36 | }
37 |
38 | export const Button: React.FC = function ({
39 | children,
40 | style = {},
41 | onPress,
42 | isModal = false,
43 | labelColor = 'primary',
44 | shadow = false,
45 | labelVariant = 'normal',
46 | custom = false,
47 | disabled = false,
48 | ...props
49 | }) {
50 | if (isModal) {
51 | return (
52 |
53 |
54 | {typeof children === 'string' ? (
55 |
56 | {children}
57 |
58 | ) : (
59 | children
60 | )}
61 |
62 |
63 | )
64 | }
65 | return (
66 |
72 | ({ hovered, pressed }) => {
73 | 'worklet'
74 |
75 | return {
76 | opacity: hovered || pressed ? 0.6 : 1,
77 | }
78 | },
79 | [],
80 | )}>
81 |
82 | {typeof children === 'string' ? (
83 |
84 | {children}
85 |
86 | ) : (
87 | children
88 | )}
89 |
90 |
91 | )
92 | }
93 |
--------------------------------------------------------------------------------
/src/components/widgets/button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './button'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/card/card.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { borderRadiusSizes } from '@/common/constants'
5 | import { ViewProps } from '@/common/types'
6 |
7 | export const Card: React.FC = function (props) {
8 | return (
9 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/widgets/card/index.ts:
--------------------------------------------------------------------------------
1 | export * from './card'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/center/center.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | export interface CenterProps {
7 | horizontal?: boolean
8 | vertical?: boolean
9 | }
10 |
11 | export const Center: React.FC = function ({
12 | horizontal = false,
13 | vertical = false,
14 | children = null,
15 | ...props
16 | }) {
17 | return (
18 |
24 | {children}
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/widgets/center/index.ts:
--------------------------------------------------------------------------------
1 | export * from './center'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/col/col.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | export const Col: React.FC = function ({ children, ...props }) {
7 | return (
8 |
9 | {children}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/widgets/col/index.ts:
--------------------------------------------------------------------------------
1 | export * from './col'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/fade-view/fade-view.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Animated, {
3 | FadeIn,
4 | FadeInDown,
5 | FadeInLeft,
6 | FadeInRight,
7 | FadeInUp,
8 | FadeOut,
9 | FadeOutRight,
10 | SlideInLeft,
11 | SlideOutRight,
12 | } from 'react-native-reanimated'
13 |
14 | import { ViewProps } from '@/common/types'
15 |
16 | type Props = {
17 | // components's props
18 | children: React.ReactNode
19 | type?: 'left' | 'right' | 'bottom' | 'slideLeft' | 'fade'
20 | }
21 |
22 | /**
23 | * Describe your component here
24 | */
25 |
26 | export const FadeView: React.FC = function ({
27 | children,
28 | type = 'fade',
29 | ...props
30 | }) {
31 | const entering = React.useMemo(() => {
32 | const anims = {
33 | fade: FadeIn,
34 | left: FadeInLeft,
35 | right: FadeInRight,
36 | bottom: FadeInUp,
37 | slideLeft: SlideInLeft,
38 | }
39 | return anims?.[type]
40 | }, [type])
41 |
42 | const exiting = React.useMemo(() => {
43 | const anims = {
44 | left: FadeOutRight,
45 | right: FadeOut,
46 | bottom: FadeInDown,
47 | slideLeft: SlideOutRight,
48 | fade: FadeOut,
49 | }
50 | return anims?.[type]
51 | }, [type])
52 |
53 | return (
54 |
55 | {children}
56 |
57 | )
58 | }
59 |
--------------------------------------------------------------------------------
/src/components/widgets/fade-view/index.ts:
--------------------------------------------------------------------------------
1 | export * from './fade-view'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/if/if.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | type Props = {
4 | condition: boolean
5 | component: React.ReactElement
6 | fallback?: React.ReactElement
7 | }
8 |
9 | export const If: React.FC = props => {
10 | const { condition, component, fallback = null } = props
11 | return condition ? component : fallback
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/widgets/if/index.ts:
--------------------------------------------------------------------------------
1 | export * from './if'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/image/image.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react'
2 | import {
3 | ImageResizeMode,
4 | ImageSourcePropType,
5 | ImageStyle,
6 | StyleProp,
7 | ViewStyle,
8 | } from 'react-native'
9 | import Reanimated, {
10 | Easing,
11 | useAnimatedStyle,
12 | useSharedValue,
13 | withTiming,
14 | } from 'react-native-reanimated'
15 |
16 | import { Box } from '../box'
17 |
18 | type Props = {
19 | style?: StyleProp
20 | source: ImageSourcePropType
21 | resizeMode?: ImageResizeMode
22 | }
23 |
24 | const IMAGE: ImageStyle = {
25 | width: undefined,
26 | height: undefined,
27 | flex: 1,
28 | }
29 |
30 | export const Image: React.FC = function (props) {
31 | const { style, source, resizeMode = 'contain', ...rest } = props
32 | const x = useSharedValue(0)
33 |
34 | const opacity = useAnimatedStyle(() => ({
35 | opacity: x.value,
36 | }))
37 |
38 | const handleOnLayout = useCallback(() => {
39 | x.value = withTiming(1, {
40 | duration: 1200,
41 | easing: Easing.in(Easing.ease),
42 | })
43 | }, [x])
44 |
45 | return (
46 |
47 |
53 |
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/widgets/image/index.ts:
--------------------------------------------------------------------------------
1 | export * from './image'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/index.ts:
--------------------------------------------------------------------------------
1 | export * from './align'
2 | export * from './app-bar'
3 | export * from './box'
4 | export * from './button'
5 | export * from './card'
6 | export * from './center'
7 | export * from './col'
8 | export * from './if'
9 | export * from './image'
10 | export * from './positioned'
11 | export * from './row'
12 | export * from './screen'
13 | export * from './space'
14 | export * from './spinner'
15 | export * from './stack'
16 | export * from './switch'
17 | export * from './text'
18 | export * from './text-button'
19 | export * from './text-input'
20 | export * from './touchable'
21 | export * from './wrap'
22 | export * from './moti-color'
23 | export * from './fade-view'
24 |
--------------------------------------------------------------------------------
/src/components/widgets/input-field/index.ts:
--------------------------------------------------------------------------------
1 | export * from './input-field'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/input-field/input-field.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Control, Controller } from 'react-hook-form'
3 | import { ViewStyle } from 'react-native'
4 |
5 | import { TextProps } from '@shopify/restyle'
6 |
7 | import { borderRadiusSizes } from '@/common/constants'
8 | import { ColorName, spacing, Theme, useTheme } from '@/common/themes'
9 | import { Row, Space, Text, TextInput } from '@/components/widgets'
10 |
11 | type InputFieldProps = {
12 | control?: Control
13 | error?: string | undefined
14 | name: string
15 | styleContainer?: ViewStyle
16 | colorText?: ColorName
17 | }
18 |
19 | const INPUT: ViewStyle = {
20 | width: '100%',
21 | borderWidth: 1,
22 | borderRadius: borderRadiusSizes.small,
23 | height: 56,
24 | paddingHorizontal: spacing.small,
25 | }
26 |
27 | export const InputField: React.FC<
28 | InputFieldProps & React.ComponentProps & TextProps
29 | > = function ({ control, error, name, styleContainer = {}, colorText = 'text.default', ...props }) {
30 | const { colors } = useTheme()
31 | return (
32 | <>
33 | (
39 |
40 |
47 |
48 | )}
49 | name={name}
50 | />
51 |
52 | {!!error && {error}}
53 | >
54 | )
55 | }
56 |
--------------------------------------------------------------------------------
/src/components/widgets/moti-color/index.ts:
--------------------------------------------------------------------------------
1 | export * from './moti-color'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/moti-color/moti-color.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { StyleProp, ViewStyle } from 'react-native'
3 |
4 | import { MotiView } from 'moti'
5 |
6 | import { transitions } from '@/common/constants'
7 | import { ColorName, useTheme } from '@/common/themes'
8 | import { ViewProps } from '@/common/types'
9 |
10 | type Props = {
11 | // components's props
12 | backgroundColor: ColorName
13 | children?: React.ReactNode
14 | style?: StyleProp
15 | }
16 |
17 | /**
18 | * Describe your component here
19 | */
20 |
21 | export const MotiColor: React.FC = function ({
22 | children,
23 | backgroundColor,
24 | style,
25 | }: Props) {
26 | const theme = useTheme()
27 | return (
28 |
37 | {children}
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/components/widgets/positioned/index.ts:
--------------------------------------------------------------------------------
1 | export * from './positioned'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/positioned/positioned.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | export const Positioned: React.FC = function ({ children, ...props }) {
7 | return (
8 |
9 | {children}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/widgets/row/index.ts:
--------------------------------------------------------------------------------
1 | export * from './row'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/row/row.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | export const Row: React.FC = function ({ children, ...props }) {
7 | return (
8 |
9 | {children}
10 |
11 | )
12 | }
13 |
--------------------------------------------------------------------------------
/src/components/widgets/screen/index.ts:
--------------------------------------------------------------------------------
1 | export * from './screen'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/screen/screen.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { SafeAreaView } from 'react-native-safe-area-context'
3 |
4 | import { MotiColor } from '../moti-color'
5 | import { styles } from '@/common/constants'
6 | import { useTheme } from '@/common/themes'
7 |
8 | type LayoutProps = {
9 | children: React.ReactNode
10 | safe?: boolean
11 | }
12 |
13 | export const Screen: React.FC = function ({ children, safe = false }) {
14 | const { colors } = useTheme()
15 | if (!safe)
16 | return (
17 |
18 | {children}
19 |
20 | )
21 | return (
22 |
23 |
24 | {children}
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/components/widgets/space/index.ts:
--------------------------------------------------------------------------------
1 | export * from './space'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/space/space.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { FlexAlignType, StyleProp, ViewStyle } from 'react-native'
3 |
4 | import { Box } from '../box/box'
5 | import { ViewProps } from '@/common/types'
6 |
7 | export interface SpaceProps {
8 | style?: StyleProp
9 | height?: number | string
10 | width?: number | string
11 | align?: FlexAlignType
12 | }
13 |
14 | export const Space: React.FC = function ({
15 | style = {},
16 | height = 0,
17 | width = '100%',
18 | align = 'flex-start',
19 | ...props
20 | }) {
21 | const styles: any = [style, { height, width }]
22 |
23 | return
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/widgets/spinner/index.ts:
--------------------------------------------------------------------------------
1 | export * from './spinner'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/spinner/spinner.tsx:
--------------------------------------------------------------------------------
1 | /* eslint-disable global-require */
2 | import React from 'react'
3 |
4 | import Lottie from 'lottie-react-native'
5 |
6 | import { Center } from '../center'
7 |
8 | export const Spinner: React.FC = function () {
9 | return (
10 |
11 |
20 |
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/src/components/widgets/stack/index.ts:
--------------------------------------------------------------------------------
1 | export * from './stack'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/stack/stack.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | export const Stack: React.FC = function ({ children, ...props }) {
7 | return (
8 |
9 | {React.Children.map(children, (child, index) => {
10 | const length = Array.isArray(children) ? children.length : 0
11 | return React.cloneElement(child as React.ReactElement, {
12 | position: 'absolute',
13 | zIndex: length - index,
14 | })
15 | })}
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/src/components/widgets/switch/index.ts:
--------------------------------------------------------------------------------
1 | export * from './switch'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/switch/switch.tsx:
--------------------------------------------------------------------------------
1 | import React, { useMemo } from 'react'
2 | import { Pressable, View } from 'react-native'
3 | import { Easing } from 'react-native-reanimated'
4 |
5 | import { MotiTransitionProp, MotiView } from 'moti'
6 |
7 | import { MotiColor } from '../moti-color'
8 | import { useTheme } from '@/common/themes'
9 |
10 | type SwitchProps = {
11 | isActive?: boolean
12 | onPress?: () => void
13 | size: number
14 | }
15 |
16 | const transition: MotiTransitionProp = {
17 | type: 'timing',
18 | duration: 300,
19 | easing: Easing.inOut(Easing.ease),
20 | }
21 |
22 | export const Switch: React.FC = function ({ isActive, onPress, size }) {
23 | const trackWitch = useMemo(() => {
24 | return size
25 | }, [size])
26 |
27 | const trackHeight = useMemo(() => {
28 | return size * 0.5
29 | }, [size])
30 |
31 | const { colors } = useTheme()
32 |
33 | return (
34 |
35 | ({
38 | alignItems: 'center',
39 | justifyContent: 'center',
40 | width: size,
41 | }),
42 | [size],
43 | )}>
44 | ({
48 | width: trackWitch,
49 | height: trackHeight,
50 | borderRadius: trackHeight / 2,
51 | borderWidth: 1,
52 | borderColor: colors['background.switch'],
53 | }),
54 | [colors, trackHeight, trackWitch],
55 | )}
56 | />
57 | ({
61 | translateX: isActive ? trackWitch * 0.25 : -trackWitch * 0.25,
62 | }),
63 | [isActive, trackWitch],
64 | )}
65 | style={useMemo(
66 | () => ({
67 | position: 'absolute',
68 | width: trackHeight * 0.8,
69 | height: trackHeight * 0.8,
70 | borderRadius: (trackHeight * 0.8) / 2,
71 | backgroundColor: colors['background.switchTrack'],
72 | }),
73 | [colors, trackHeight],
74 | )}
75 | />
76 |
77 |
78 | )
79 | }
80 |
--------------------------------------------------------------------------------
/src/components/widgets/text-button/index.ts:
--------------------------------------------------------------------------------
1 | export * from './text-button'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/text-button/text-button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Text } from '../text/text'
4 | import { Touchable } from '../touchable'
5 | import { ColorName, TypoName } from '@/common/themes'
6 |
7 | type Props = {
8 | label: string
9 | variant?: TypoName
10 | color?: ColorName
11 | onPress: () => void
12 | }
13 |
14 | export const TextButton: React.FC = function ({
15 | label,
16 | variant = 'normal',
17 | color = 'text.default',
18 | onPress,
19 | }) {
20 | return (
21 |
22 |
23 | {label}
24 |
25 |
26 | )
27 | }
28 |
--------------------------------------------------------------------------------
/src/components/widgets/text-input/index.ts:
--------------------------------------------------------------------------------
1 | export * from './text-input'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/text-input/text-input.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { TextInput as RNTextInput } from 'react-native'
3 |
4 | import { createRestyleComponent, createVariant, VariantProps } from '@shopify/restyle'
5 |
6 | import { Theme } from '@/common/themes'
7 |
8 | const variant = createVariant({
9 | themeKey: 'textInputVariants',
10 | })
11 |
12 | export const TextInput = createRestyleComponent<
13 | VariantProps & React.ComponentProps,
14 | Theme
15 | >([variant], RNTextInput)
16 |
--------------------------------------------------------------------------------
/src/components/widgets/text/index.ts:
--------------------------------------------------------------------------------
1 | export * from './text'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/text/text.tsx:
--------------------------------------------------------------------------------
1 | import { createText } from '@shopify/restyle'
2 |
3 | import { Theme } from '@/common/themes'
4 |
5 | export const Text = createText()
6 |
--------------------------------------------------------------------------------
/src/components/widgets/touchable/index.ts:
--------------------------------------------------------------------------------
1 | export * from './touchable'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/touchable/touchable.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Insets, TouchableOpacity } from 'react-native'
3 |
4 | type Props = {
5 | children?: React.ReactNode
6 | onPress?: () => void
7 | hitSlop?: Insets
8 | }
9 |
10 | export const Touchable: React.FC = function ({ onPress, children, hitSlop }) {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/components/widgets/wrap/index.ts:
--------------------------------------------------------------------------------
1 | export * from './wrap'
2 |
--------------------------------------------------------------------------------
/src/components/widgets/wrap/wrap.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { Box } from '../box'
4 | import { ViewProps } from '@/common/types'
5 |
6 | type Props = {
7 | children: React.ReactNode
8 | }
9 |
10 | export const Wrap: React.FC = function ({ children, ...props }) {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
17 |
--------------------------------------------------------------------------------
/src/localization/en/auth.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | username: 'Username',
3 | password: 'Password',
4 | login: 'Login',
5 | register: 'Register',
6 | enter_email: 'Enter your email',
7 | enter_password: 'Enter your password',
8 | enter_confirm_password: 'Enter your confirm password',
9 | }
10 |
--------------------------------------------------------------------------------
/src/localization/en/common.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | welcome: 'Welcome',
3 | change_language: 'Change Language',
4 | }
5 |
--------------------------------------------------------------------------------
/src/localization/en/index.ts:
--------------------------------------------------------------------------------
1 | import auth from './auth'
2 | import common from './common'
3 | import navigate from './navigate'
4 |
5 | export default {
6 | common,
7 | auth,
8 | navigate,
9 | }
10 |
--------------------------------------------------------------------------------
/src/localization/en/navigate.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | login: 'Login',
3 | register: 'Register',
4 | }
5 |
--------------------------------------------------------------------------------
/src/localization/i18n.ts:
--------------------------------------------------------------------------------
1 | import { initReactI18next } from 'react-i18next'
2 |
3 | import i18n from 'i18next'
4 |
5 | import en from './en'
6 | import vi from './vi'
7 |
8 | const resources = {
9 | en,
10 | vi,
11 | }
12 |
13 | i18n.use(initReactI18next).init({
14 | compatibilityJSON: 'v3',
15 | resources,
16 | lng: 'en',
17 | interpolation: {
18 | escapeValue: false,
19 | },
20 | })
21 |
22 | export default i18n
23 |
--------------------------------------------------------------------------------
/src/localization/language.ts:
--------------------------------------------------------------------------------
1 | export const language: { [key: string]: string } = {
2 | english: 'English',
3 | vietnam: 'Việt Nam',
4 | }
5 |
--------------------------------------------------------------------------------
/src/localization/vi/auth.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | username: 'Tên người dùng',
3 | password: 'Mật khẩu',
4 | login: 'Đăng nhập',
5 | register: 'Đăng ký',
6 | enter_email: 'Nhập email của bạn',
7 | enter_password: 'Nhập mật khẩu của bạn',
8 | enter_confirm_password: 'Nhập lại mật khẩu của bạn',
9 | }
10 |
--------------------------------------------------------------------------------
/src/localization/vi/common.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | welcome: 'Chào mừng',
3 | change_language: 'Thay đổi ngôn ngữ',
4 | }
5 |
--------------------------------------------------------------------------------
/src/localization/vi/index.ts:
--------------------------------------------------------------------------------
1 | import auth from './auth'
2 | import common from './common'
3 | import navigate from './navigate'
4 |
5 | export default {
6 | common,
7 | auth,
8 | navigate,
9 | }
10 |
--------------------------------------------------------------------------------
/src/localization/vi/navigate.ts:
--------------------------------------------------------------------------------
1 | export default {
2 | login: 'Đăng nhập',
3 | register: 'Đăng ký',
4 | }
5 |
--------------------------------------------------------------------------------
/src/modules/auth/api/auth.ts:
--------------------------------------------------------------------------------
1 | import { LoginPayload, RegisterPayload } from '../types'
2 | import { UserResponse } from '../types/api'
3 | import { UserModel } from '../types/models'
4 | import { ResponseApi } from '@/common/types/api'
5 | import { axios } from '@/common/utils/axios'
6 | import { wrapApiCall } from '@/common/utils/helpers'
7 |
8 | export const getUserApi = async (): Promise> => {
9 | return wrapApiCall(() => axios.get('/auth/me'))
10 | }
11 |
12 | export const registerWithEmailAndPasswordApi = (
13 | data: RegisterPayload,
14 | ): Promise> => {
15 | return wrapApiCall(() => axios.post('/auth/register', data))
16 | }
17 |
18 | export const loginWithEmailAndPasswordApi = async (
19 | data: LoginPayload,
20 | ): Promise> => {
21 | return wrapApiCall(() => axios.post('/auth/login', data))
22 | }
23 |
--------------------------------------------------------------------------------
/src/modules/auth/api/index.ts:
--------------------------------------------------------------------------------
1 | export * from './auth'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/assets/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/src/modules/auth/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout'
2 | export * from './login-form'
3 | export * from './register-form'
4 |
--------------------------------------------------------------------------------
/src/modules/auth/components/layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/components/layout/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { useThemeStore } from '@/common/stores'
4 | import { ChildrenProps } from '@/common/types'
5 | import { Center, Row, Screen, Space, Switch, Text } from '@/components/widgets'
6 |
7 | type AuthLayoutProps = {
8 | title?: string
9 | isShowToggleDarkMode?: boolean
10 | safe?: boolean
11 | }
12 |
13 | export const AuthLayout: React.FC = function ({
14 | children,
15 | title,
16 | isShowToggleDarkMode,
17 | safe = false,
18 | }) {
19 | const { isDarkMode, toggleMode } = useThemeStore()
20 | return (
21 |
22 | {isShowToggleDarkMode && (
23 |
24 |
25 |
26 | )}
27 |
28 | {title}
29 |
30 | {children}
31 |
32 |
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/modules/auth/components/login-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from './login-form'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/components/login-form/login-form.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 |
3 | import { useNavigation } from '@react-navigation/native'
4 |
5 | import { useAuth } from '../../hooks'
6 | import { useLoginForm } from './useLoginForm'
7 | import { useTranslation } from '@/common/hooks'
8 | import { useThemeStore } from '@/common/stores'
9 | import { useTheme } from '@/common/themes'
10 | import { Button, Col, Space } from '@/components/widgets'
11 | import { InputField } from '@/components/widgets/input-field'
12 | import { AppNavigationProp } from '@/routes'
13 |
14 | export const LoginForm: React.FC = function () {
15 | const navigation = useNavigation()
16 | const { login, goToRegister } = useAuth()
17 | const { t } = useTranslation()
18 | const isDarkMode = useThemeStore(state => state.isDarkMode)
19 | const { colors } = useTheme()
20 |
21 | const { control, handleSubmit, errors } = useLoginForm()
22 |
23 | useEffect(() => {
24 | navigation.setOptions({
25 | headerTitle: t('navigate:login'),
26 | })
27 | }, [navigation, t])
28 |
29 | return (
30 |
31 |
40 |
41 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
61 |
62 |
63 | )
64 | }
65 |
--------------------------------------------------------------------------------
/src/modules/auth/components/login-form/useLoginForm.ts:
--------------------------------------------------------------------------------
1 | import { useForm } from 'react-hook-form'
2 |
3 | import { yupResolver } from '@hookform/resolvers/yup'
4 | import * as yup from 'yup'
5 |
6 | import { LoginPayload } from '../../types'
7 |
8 | const schema = yup.object({
9 | email: yup.string().required('Required'),
10 | password: yup.string().required('Required'),
11 | })
12 |
13 | export const useLoginForm = () => {
14 | const {
15 | control,
16 | handleSubmit,
17 | formState: { errors },
18 | } = useForm({
19 | defaultValues: {
20 | email: '',
21 | password: '',
22 | },
23 | resolver: yupResolver(schema),
24 | })
25 |
26 | return {
27 | control,
28 | handleSubmit,
29 | errors,
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/modules/auth/components/register-form/index.ts:
--------------------------------------------------------------------------------
1 | export * from './register-form'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/components/register-form/register-form.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback, useEffect } from 'react'
2 |
3 | import { useNavigation } from '@react-navigation/native'
4 |
5 | import { useAuth } from '../../hooks'
6 | import { useRegisterForm } from './useRegisterForm'
7 | import { useTranslation } from '@/common/hooks'
8 | import { useThemeStore } from '@/common/stores'
9 | import { useTheme } from '@/common/themes'
10 | import { Button, Col, Space } from '@/components/widgets'
11 | import { InputField } from '@/components/widgets/input-field'
12 | import { AppNavigationProp } from '@/routes'
13 |
14 | export const RegisterForm = function () {
15 | const navigation = useNavigation()
16 | const { register, goToLogin } = useAuth()
17 | const { t } = useTranslation()
18 | const isDarkMode = useThemeStore(state => state.isDarkMode)
19 | const { colors } = useTheme()
20 |
21 | const { control, handleSubmit, errors } = useRegisterForm()
22 |
23 | useEffect(() => {
24 | navigation.setOptions({
25 | headerTitle: t('navigate:register'),
26 | })
27 | }, [navigation, t])
28 |
29 | const onSubmit = useCallback(
30 | (/* data: FormData */) => {
31 | register()
32 | },
33 | [register],
34 | )
35 |
36 | return (
37 |
38 |
47 |
48 |
57 |
58 |
67 |
68 |
69 |
72 |
73 |
74 |
75 |
78 |
79 |
80 | )
81 | }
82 |
--------------------------------------------------------------------------------
/src/modules/auth/components/register-form/useRegisterForm.ts:
--------------------------------------------------------------------------------
1 | import { useForm } from 'react-hook-form'
2 |
3 | import { yupResolver } from '@hookform/resolvers/yup'
4 | import * as yup from 'yup'
5 |
6 | type FormData = {
7 | email: string
8 | password: string
9 | confirmPassword: string
10 | }
11 |
12 | const schema = yup.object({
13 | email: yup.string().email().required('Required'),
14 | password: yup.string().required('Required'),
15 | confirmPassword: yup
16 | .string()
17 | .required('Required')
18 | .oneOf([yup.ref('password'), null], 'Passwords must match'),
19 | })
20 |
21 | export const useRegisterForm = () => {
22 | const {
23 | control,
24 | handleSubmit,
25 | formState: { errors },
26 | } = useForm({
27 | defaultValues: {
28 | email: '',
29 | password: '',
30 | confirmPassword: '',
31 | },
32 | resolver: yupResolver(schema),
33 | })
34 | return { control, handleSubmit, errors }
35 | }
36 |
--------------------------------------------------------------------------------
/src/modules/auth/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useAuth'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/hooks/useAuth.ts:
--------------------------------------------------------------------------------
1 | import { useCallback } from 'react'
2 | import { Keyboard } from 'react-native'
3 |
4 | import { useNavigation } from '@react-navigation/native'
5 |
6 | import { useAuthStore } from '../stores'
7 | import { LoginPayload } from '../types'
8 | import { AppNavigationProp } from '@/routes'
9 | import { RouterName } from '@/routes/router-name'
10 |
11 | export const useAuth = () => {
12 | const navigation = useNavigation()
13 | const { loginAction, registerAction } = useAuthStore()
14 |
15 | const goToLogin = useCallback(() => {
16 | navigation.navigate(RouterName.login)
17 | }, [navigation])
18 |
19 | const goToRegister = useCallback(() => {
20 | navigation.navigate(RouterName.register)
21 | }, [navigation])
22 |
23 | const login = useCallback(
24 | (data: LoginPayload) => {
25 | Keyboard.dismiss()
26 | loginAction(data)
27 | },
28 | [loginAction],
29 | )
30 |
31 | const register = useCallback(async () => {
32 | await registerAction()
33 | navigation.navigate(RouterName.login)
34 | }, [registerAction, navigation])
35 |
36 | return { goToLogin, goToRegister, login, register }
37 | }
38 |
--------------------------------------------------------------------------------
/src/modules/auth/index.ts:
--------------------------------------------------------------------------------
1 | export * from './routes'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {
4 | createStackNavigator,
5 | StackNavigationOptions,
6 | TransitionPresets,
7 | } from '@react-navigation/stack'
8 |
9 | import { Landing, Login, Register } from '../screens'
10 | import { RouterName } from '@/routes/router-name'
11 |
12 | export type AuthStackParamList = {
13 | register: undefined
14 | login: undefined
15 | landing: undefined
16 | }
17 |
18 | const options: StackNavigationOptions = {
19 | headerShown: true,
20 | ...TransitionPresets.SlideFromRightIOS,
21 | }
22 |
23 | const Stack = createStackNavigator()
24 |
25 | export const AuthRoutes: React.FC = function () {
26 | return (
27 |
28 |
29 |
30 |
37 |
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/src/modules/auth/screens/index.ts:
--------------------------------------------------------------------------------
1 | export * from './landing'
2 | export * from './login'
3 | export * from './register'
4 |
--------------------------------------------------------------------------------
/src/modules/auth/screens/landing.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useMemo, useState } from 'react'
2 |
3 | import { useNavigation } from '@react-navigation/native'
4 |
5 | import { AuthLayout } from '../components/layout/layout'
6 | import { useAuth } from '../hooks'
7 | import { APP_NAME } from '@/common/config'
8 | import { useTranslation } from '@/common/hooks'
9 | import { Button, Space, Text } from '@/components/widgets'
10 | import { language } from '@/localization/language'
11 | import { AppNavigationProp } from '@/routes'
12 |
13 | const languages = [
14 | // Language List
15 | { code: 'en', label: language.english },
16 | { code: 'vi', label: language.vietnam },
17 | ]
18 |
19 | export const Landing: React.FC = function () {
20 | const navigation = useNavigation()
21 | const { goToLogin, goToRegister } = useAuth()
22 |
23 | useEffect(() => {
24 | navigation.setOptions({
25 | headerTitle: '',
26 | })
27 | }, [navigation])
28 |
29 | const { t, i18n } = useTranslation()
30 | const [lang, setLang] = useState(i18n.language)
31 |
32 | const languagesSelector = useMemo(
33 | () => (
34 | <>
35 | {languages.map(currentLang => {
36 | const selectedLanguage = currentLang.code === lang
37 | return (
38 | {
43 | setLang(currentLang.code)
44 | i18n.changeLanguage(currentLang.code) // it will change the language through out the app.
45 | }}>
46 | {currentLang.label}
47 |
48 | )
49 | })}
50 | >
51 | ),
52 | [i18n, lang],
53 | )
54 |
55 | return (
56 |
57 |
60 |
61 |
64 |
65 | {languagesSelector}
66 |
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/src/modules/auth/screens/login.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { AuthLayout, LoginForm } from '../components'
4 | import { useTranslation } from '@/common/hooks'
5 |
6 | export const Login: React.FC = function () {
7 | const { t } = useTranslation()
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/modules/auth/screens/register.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { AuthLayout, RegisterForm } from '../components'
4 | import { useTranslation } from '@/common/hooks'
5 |
6 | export const Register = function () {
7 | const { t } = useTranslation()
8 | return (
9 |
10 |
11 |
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/src/modules/auth/stores/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useAuthStore'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/stores/useAuthStore.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | import { loginWithEmailAndPasswordApi } from '../api'
4 | import { LoginPayload } from '../types'
5 | import { UserModel } from '../types/models'
6 | import { hideLoading, showLoading } from '@/common/utils/dialog'
7 | import { delay } from '@/common/utils/helpers'
8 | import storage from '@/common/utils/storage'
9 |
10 | type State = {
11 | data: UserModel | undefined
12 | isLoggedIn: boolean
13 | }
14 |
15 | type Action = {
16 | registerAction: () => Promise
17 | checkLoggedInAction: () => void
18 | loginAction: (data: LoginPayload) => Promise
19 | logOutAction: () => Promise
20 | }
21 |
22 | export const useAuthStore = create(set => ({
23 | isLoggedIn: false,
24 | data: undefined,
25 | loginAction: async data => {
26 | showLoading()
27 | const resp = await loginWithEmailAndPasswordApi(data)
28 | resp.ok = true
29 | if (resp.ok) {
30 | storage.saveAccessToken('token')
31 | set(() => ({
32 | isLoggedIn: true,
33 | data: resp.data?.user,
34 | }))
35 | }
36 | hideLoading()
37 | },
38 | registerAction: async () => {
39 | await delay(3000)
40 | },
41 | checkLoggedInAction: () => {
42 | set({
43 | isLoggedIn: !!storage.getAccessToken(),
44 | })
45 | },
46 | logOutAction: async () => {
47 | showLoading()
48 | await delay(1000)
49 | storage.saveAccessToken('')
50 | set({
51 | isLoggedIn: false,
52 | data: undefined,
53 | })
54 | hideLoading()
55 | },
56 | }))
57 |
--------------------------------------------------------------------------------
/src/modules/auth/types/api.ts:
--------------------------------------------------------------------------------
1 | import { UserModel } from './models'
2 |
3 | export type UserResponse = {
4 | jwt: string
5 | user: UserModel
6 | }
7 |
--------------------------------------------------------------------------------
/src/modules/auth/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './payload'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/types/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './user'
2 |
--------------------------------------------------------------------------------
/src/modules/auth/types/models/user.ts:
--------------------------------------------------------------------------------
1 | export type UserModel = {
2 | id: string
3 | email: string
4 | firstName: string
5 | lastName: string
6 | bio: string
7 | role: 'ADMIN' | 'USER'
8 | }
9 |
--------------------------------------------------------------------------------
/src/modules/auth/types/payload.ts:
--------------------------------------------------------------------------------
1 | export type RegisterPayload = {
2 | email: string
3 | password: string
4 | firstName: string
5 | lastName: string
6 | }
7 |
8 | export type LoginPayload = {
9 | email: string
10 | password: string
11 | }
12 |
--------------------------------------------------------------------------------
/src/modules/auth/utils/index.tsx:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/src/modules/error/components/error-boundary/error-boundary.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactElement, useCallback, useMemo } from 'react'
2 | import ErrorBoundaryLib from 'react-native-error-boundary'
3 |
4 | import { Crash } from '../../screens'
5 | import { logger } from '@/common/utils/logger'
6 |
7 | type Props = {
8 | children: ReactElement
9 | catchErrors: 'always' | 'dev' | 'prod' | 'never'
10 | }
11 |
12 | export const ErrorBoundary: React.FC = function ({ children, catchErrors }) {
13 | const isEnabled = useMemo(() => {
14 | return (
15 | catchErrors === 'always' ||
16 | (catchErrors === 'dev' && __DEV__) ||
17 | (catchErrors === 'prod' && !__DEV__)
18 | )
19 | }, [catchErrors])
20 |
21 | const errorHandler = useCallback((error: Error, stackTrace: string) => {
22 | // send error to service log
23 | logger.log(error)
24 | logger.log(stackTrace)
25 | }, [])
26 |
27 | return isEnabled ? (
28 |
29 | {children}
30 |
31 | ) : (
32 | children
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/src/modules/error/components/error-boundary/index.ts:
--------------------------------------------------------------------------------
1 | export * from './error-boundary'
2 |
--------------------------------------------------------------------------------
/src/modules/error/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './error-boundary'
2 |
--------------------------------------------------------------------------------
/src/modules/error/screens/crash.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react'
2 | import RNRestart from 'react-native-restart'
3 |
4 | import { Button, Center, Screen, Space, Text } from '@/components/widgets'
5 |
6 | type IProps = {
7 | error: Error
8 | resetError: () => void
9 | }
10 |
11 | export const Crash: React.FC = function ({ error, resetError }) {
12 | const handleResetApp = useCallback(() => {
13 | resetError()
14 | RNRestart.Restart()
15 | }, [resetError])
16 |
17 | return (
18 |
19 |
20 | {__DEV__ ? error.toString() : 'Oops!'}
21 |
22 |
25 |
26 |
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/src/modules/error/screens/index.ts:
--------------------------------------------------------------------------------
1 | export * from './crash'
2 |
--------------------------------------------------------------------------------
/src/modules/home/components/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout'
2 |
--------------------------------------------------------------------------------
/src/modules/home/components/layout/index.ts:
--------------------------------------------------------------------------------
1 | export * from './layout'
2 |
--------------------------------------------------------------------------------
/src/modules/home/components/layout/layout.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { ChildrenProps } from '@/common/types'
4 | import { Center, Screen, Space, Text } from '@/components/widgets'
5 |
6 | type HomeLayoutProps = {
7 | title?: string
8 | }
9 |
10 | export const HomeLayout: React.FC = function ({
11 | children,
12 | title,
13 | }) {
14 | return (
15 |
16 |
17 | {title}
18 |
19 | {children}
20 |
21 |
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/src/modules/home/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {
4 | createStackNavigator,
5 | StackNavigationOptions,
6 | TransitionPresets,
7 | } from '@react-navigation/stack'
8 |
9 | import { Home } from '../screens'
10 | import { RouterName } from '@/routes/router-name'
11 |
12 | export type HomeStackParamList = {
13 | home: undefined
14 | }
15 |
16 | const options: StackNavigationOptions = {
17 | headerShown: true,
18 | ...TransitionPresets.SlideFromRightIOS,
19 | }
20 |
21 | const Stack = createStackNavigator()
22 |
23 | export const HomeRoutes: React.FC = function () {
24 | return (
25 |
26 |
33 |
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/src/modules/home/screens/home.tsx:
--------------------------------------------------------------------------------
1 | import React, { useCallback } from 'react'
2 |
3 | import { HomeLayout } from '../components/layout'
4 | import { showCustomDialog } from '@/common/utils/dialog'
5 | import { EventRegister, EVENTS } from '@/common/utils/event-register'
6 | import { ConfirmModal } from '@/components/modals'
7 | import { Button, Space } from '@/components/widgets'
8 | import { useAuthStore } from '@/modules/auth/stores'
9 |
10 | export const Home: React.FC = function () {
11 | const logout = useAuthStore(state => state.logOutAction)
12 |
13 | const showModal = useCallback(() => {
14 | showCustomDialog()
15 | }, [])
16 |
17 | const emitEvent = useCallback(() => {
18 | EventRegister.emit(EVENTS.EVENT_LOGOUT)
19 | }, [])
20 |
21 | return (
22 |
23 |
26 |
27 |
30 |
31 |
34 |
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/src/modules/home/screens/index.ts:
--------------------------------------------------------------------------------
1 | export * from './home'
2 |
--------------------------------------------------------------------------------
/src/providers/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useMemo } from 'react'
2 | import { Alert } from 'react-native'
3 | import { RootSiblingParent } from 'react-native-root-siblings'
4 | import { SafeAreaProvider } from 'react-native-safe-area-context'
5 |
6 | import { ThemeProvider } from '@shopify/restyle'
7 |
8 | import { useBarStyle } from '@/common/hooks'
9 | import { useThemeStore } from '@/common/stores'
10 | import { darkTheme, lightTheme } from '@/common/themes'
11 | import { EventRegister, EVENTS } from '@/common/utils/event-register'
12 | import { useAuthStore } from '@/modules/auth/stores'
13 | import { ErrorBoundary } from '@/modules/error/components'
14 | import { AppRoutes } from '@/routes'
15 |
16 | export const AppProviders: React.FC = function () {
17 | useBarStyle()
18 | const checkLoggedIn = useAuthStore(state => state.checkLoggedInAction)
19 | const isDarkMode = useThemeStore(state => state.isDarkMode)
20 | const theme = useMemo(() => (isDarkMode ? darkTheme : lightTheme), [isDarkMode])
21 |
22 | useEffect(() => {
23 | checkLoggedIn()
24 | EventRegister.on(EVENTS.EVENT_LOGOUT, () => {
25 | Alert.alert("I'm an event!")
26 | })
27 | }, [checkLoggedIn])
28 |
29 | return (
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | )
40 | }
41 |
--------------------------------------------------------------------------------
/src/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import RNBootSplash from 'react-native-bootsplash'
3 |
4 | import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native'
5 | import { StackNavigationProp } from '@react-navigation/stack'
6 |
7 | import { useThemeStore } from '@/common/stores'
8 | import { navigationRef } from '@/common/utils/navigation-utilities'
9 | import { AuthRoutes, AuthStackParamList } from '@/modules/auth/routes'
10 | import { useAuthStore } from '@/modules/auth/stores'
11 | import { HomeRoutes, HomeStackParamList } from '@/modules/home/routes'
12 |
13 | export type AppParamList = AuthStackParamList & HomeStackParamList
14 |
15 | export type AppNavigationProp = StackNavigationProp
16 |
17 | export const AppRoutes: React.FC = function () {
18 | const isLoggedIn = useAuthStore(state => state.isLoggedIn)
19 | const isDarkMode = useThemeStore(state => state.isDarkMode)
20 |
21 | return (
22 | RNBootSplash.hide({ fade: true })}>
26 | {!isLoggedIn ? : }
27 |
28 | )
29 | }
30 |
--------------------------------------------------------------------------------
/src/routes/router-name.ts:
--------------------------------------------------------------------------------
1 | import { AppParamList } from '.'
2 |
3 | type Route = { [key in keyof AppParamList]: keyof AppParamList | any }
4 | export const RouterName: Route = {
5 | home: 'home',
6 | landing: 'landing',
7 | register: 'register',
8 | login: 'login',
9 | }
10 |
--------------------------------------------------------------------------------
/src/starter.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import { AppProviders } from './providers'
4 |
5 | export const Starter: React.FC = function () {
6 | return
7 | }
8 |
--------------------------------------------------------------------------------
/templates/form/NAME.tsx.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | patch:
3 | append: "export * from './<%= props.kebabCaseName %>'\n"
4 | skip: <%= props.skipIndexFile %>
5 | ---
6 | import React, { useCallback } from 'react'
7 | import { useForm } from 'react-hook-form'
8 |
9 | import { yupResolver } from '@hookform/resolvers/yup'
10 | import * as yup from 'yup'
11 |
12 | import { Button, Col, Space } from '@/components/widgets'
13 | import { InputField } from '@/components/widgets/input-field'
14 |
15 | const schema = yup.object({
16 | email: yup.string().required('Required'),
17 | password: yup.string().required('Required'),
18 | })
19 |
20 | type DataForm = {
21 | email: string
22 | password: string
23 | }
24 |
25 | type Props = {
26 | // props
27 | }
28 |
29 | /**
30 | * Describe your component here
31 | */
32 |
33 | export const <%= props.pascalCaseName %>: React.FC = function (props: Props) {
34 | const {
35 | control,
36 | handleSubmit,
37 | formState: { errors },
38 | } = useForm({
39 | defaultValues: {
40 | email: '',
41 | password: '',
42 | },
43 | resolver: yupResolver(schema),
44 | })
45 |
46 | const onSubmit = useCallback((data: DataForm) => {
47 | // handle
48 | }, [])
49 |
50 | return (
51 |
52 |
60 |
61 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | )
76 | }
77 |
--------------------------------------------------------------------------------
/templates/form/index.ts.ejs:
--------------------------------------------------------------------------------
1 | export * from './<%= props.kebabCaseName %>'
2 |
--------------------------------------------------------------------------------
/templates/modal/NAME.tsx.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | patch:
3 | append: "export * from './<%= props.kebabCaseName %>'\n"
4 | skip: <%= props.skipIndexFile %>
5 | ---
6 | import React, { memo, useCallback } from 'react'
7 |
8 | import { Button, Col, Row, Space, Text } from '@/components/widgets'
9 | import { WIDTH } from '@/shared/constants'
10 | import { hideCustomDialog } from '@/shared/libs/dialog'
11 |
12 | type Props = {
13 | onClose?: () => void
14 | onConfirm?: () => void
15 | title: string
16 | content: string
17 | labelConfirm?: string
18 | labelCancel?: string
19 | }
20 |
21 | /**
22 | * Describe your component here
23 | */
24 |
25 | export const <%= props.pascalCaseName %>: React.FC = memo(function ({
26 | onClose,
27 | onConfirm,
28 | title,
29 | content,
30 | labelCancel = 'Cancel',
31 | labelConfirm = 'OK',
32 | }) {
33 | const handleClose = useCallback(() => {
34 | onClose?.()
35 | hideCustomDialog()
36 | }, [onClose])
37 |
38 | const handleConfirm = useCallback(() => {
39 | onConfirm?.()
40 | hideCustomDialog()
41 | }, [onConfirm])
42 |
43 | return (
44 |
45 |
46 |
47 | {title}
48 |
49 |
50 |
51 |
52 | {content}
53 |
54 |
55 |
56 |
66 |
67 |
70 |
71 |
72 | )
73 | })
74 |
75 |
--------------------------------------------------------------------------------
/templates/modal/index.ts.ejs:
--------------------------------------------------------------------------------
1 | export * from './<%= props.kebabCaseName %>'
2 |
--------------------------------------------------------------------------------
/templates/module/api/demo.ts:
--------------------------------------------------------------------------------
1 | import { ResponseApi } from '@/common/types/api'
2 | import { axios } from '@/common/utils/axios'
3 | import { wrapApiCall } from '@/common/utils/helpers'
4 |
5 | export const getDemoApi = async (): Promise> => {
6 | return wrapApiCall(() => axios.get('/demo'))
7 | }
8 |
--------------------------------------------------------------------------------
/templates/module/api/index.ts:
--------------------------------------------------------------------------------
1 | export * from './demo'
2 |
--------------------------------------------------------------------------------
/templates/module/assets/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/templates/module/components/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/templates/module/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/templates/module/index.ts:
--------------------------------------------------------------------------------
1 | export * from './routes'
2 |
--------------------------------------------------------------------------------
/templates/module/routes/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | import {
4 | createStackNavigator,
5 | StackNavigationOptions,
6 | TransitionPresets,
7 | } from '@react-navigation/stack'
8 |
9 | export type StackParamList = {
10 | demo: undefined
11 | }
12 |
13 | const options: StackNavigationOptions = {
14 | headerShown: true,
15 | ...TransitionPresets.SlideFromRightIOS,
16 | }
17 |
18 | const Stack = createStackNavigator()
19 |
20 | export const AuthRoutes: React.FC = function () {
21 | return (
22 |
23 | {/* */}
30 |
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/templates/module/screens/index.ts:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/templates/module/stores/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useDemoStore'
2 |
--------------------------------------------------------------------------------
/templates/module/stores/useDemoStore.ts:
--------------------------------------------------------------------------------
1 | import { create } from 'zustand'
2 |
3 | type State = {
4 | data: string | undefined
5 | }
6 |
7 | type Action = {
8 | changeData: (data: string) => Promise
9 | }
10 |
11 | export const useDemoStore = create(set => ({
12 | data: undefined,
13 | changeData: async data => {
14 | set({ data })
15 | },
16 | }))
17 |
--------------------------------------------------------------------------------
/templates/module/types/api.ts:
--------------------------------------------------------------------------------
1 | import { DemoModel } from './models'
2 |
3 | export type UserResponse = {
4 | jwt: string
5 | user: DemoModel
6 | }
7 |
--------------------------------------------------------------------------------
/templates/module/types/index.ts:
--------------------------------------------------------------------------------
1 | export * from './payload'
2 | export * from './api'
3 |
--------------------------------------------------------------------------------
/templates/module/types/models/demo.ts:
--------------------------------------------------------------------------------
1 | export type DemoModel = {
2 | id: string
3 | email: string
4 | firstName: string
5 | lastName: string
6 | }
7 |
--------------------------------------------------------------------------------
/templates/module/types/models/index.ts:
--------------------------------------------------------------------------------
1 | export * from './demo'
2 |
--------------------------------------------------------------------------------
/templates/module/types/payload.ts:
--------------------------------------------------------------------------------
1 | export type DemoPayload = {
2 | email: string
3 | password: string
4 | }
5 |
--------------------------------------------------------------------------------
/templates/module/utils/index.tsx:
--------------------------------------------------------------------------------
1 | export {}
2 |
--------------------------------------------------------------------------------
/templates/screen/NAME.tsx.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | patch:
3 | append: "export * from './<%= props.kebabCaseName %>'\n"
4 | skip: <%= props.skipIndexFile %>
5 | ---
6 | import React from 'react'
7 |
8 | import { Center, Screen, Text } from '@/components/widgets'
9 |
10 | export const <%= props.pascalCaseName %>: React.FC = function () {
11 | return (
12 |
13 |
14 | <%= props.pascalCaseName %>
15 |
16 |
17 | )
18 | }
19 |
--------------------------------------------------------------------------------
/templates/store/useNAME.tsx.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | patch:
3 | append: "export * from './use<%= props.pascalCaseName %>'\n"
4 | skip: <%= props.skipIndexFile %>
5 | ---
6 | import create from 'zustand'
7 |
8 | type State = {
9 | //
10 | }
11 |
12 | type Action = {
13 | //
14 | }
15 |
16 | export const use<%= props.pascalCaseName %> = create((set, get) => ({
17 | //
18 | }))
19 |
--------------------------------------------------------------------------------
/templates/widget/NAME.tsx.ejs:
--------------------------------------------------------------------------------
1 | ---
2 | patch:
3 | append: "export * from './<%= props.kebabCaseName %>'\n"
4 | skip: <%= props.skipIndexFile %>
5 | ---
6 | import React from 'react'
7 |
8 | import { ViewProps } from '@/shared/types'
9 |
10 | type Props = {
11 | // components's props
12 | }
13 |
14 | /**
15 | * Describe your component here
16 | */
17 |
18 | export const <%= props.pascalCaseName %>: React.FC = function (props: Props) {
19 | return null
20 | }
21 |
--------------------------------------------------------------------------------
/templates/widget/index.ts.ejs:
--------------------------------------------------------------------------------
1 | export * from './<%= props.kebabCaseName %>'
2 |
--------------------------------------------------------------------------------
/tsconfig.paths.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "paths": {
5 | "@/*": ["./src/*"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/types/declarations.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.svg' {
2 | import React from 'react'
3 | import { SvgProps } from 'react-native-svg'
4 |
5 | const content: React.FC
6 | export default content
7 | }
8 |
--------------------------------------------------------------------------------