├── template ├── .watchmanconfig ├── src │ ├── screens │ │ ├── HomeComponent │ │ │ ├── index.ts │ │ │ └── HomeScreen.tsx │ │ ├── AuthComponent │ │ │ ├── index.ts │ │ │ ├── LoginScreen.tsx │ │ │ └── SignUpScreen.tsx │ │ ├── index.ts │ │ └── SplashScreen.tsx │ ├── store │ │ ├── types │ │ │ ├── loading.ts │ │ │ ├── index.ts │ │ │ ├── store.ts │ │ │ ├── action.ts │ │ │ ├── app.ts │ │ │ └── user.ts │ │ ├── selectors │ │ │ ├── index.ts │ │ │ ├── user.ts │ │ │ ├── app.ts │ │ │ └── loading.ts │ │ ├── actionTypes.ts │ │ ├── saga │ │ │ ├── index.ts │ │ │ ├── app.ts │ │ │ └── user.ts │ │ ├── initialState.ts │ │ ├── constants │ │ │ └── app.ts │ │ ├── reducers │ │ │ ├── user.ts │ │ │ ├── loading.ts │ │ │ ├── index.ts │ │ │ └── app.ts │ │ └── store.ts │ ├── assets │ │ └── images │ │ │ ├── sts.png │ │ │ ├── close.png │ │ │ └── info.png │ ├── themes │ │ ├── fonts.ts │ │ ├── index.ts │ │ ├── images.ts │ │ ├── colors.ts │ │ └── metrics.ts │ ├── services │ │ ├── api │ │ │ └── api.ts │ │ └── networking │ │ │ ├── index.ts │ │ │ └── axios.ts │ ├── components │ │ ├── index.ts │ │ ├── Row.tsx │ │ ├── ScreenContainer.tsx │ │ ├── IndicatorDialog.tsx │ │ ├── InfoMenu.tsx │ │ └── Toast.tsx │ ├── utilities │ │ ├── types.ts │ │ ├── storage.ts │ │ ├── utils.ts │ │ └── Emitter.ts │ ├── navigation │ │ ├── RouteKey.ts │ │ ├── types.ts │ │ ├── ScreenService.tsx │ │ ├── AppNavigator.tsx │ │ ├── StackNavigation.tsx │ │ ├── CustomTabBar.tsx │ │ └── NavigationService.tsx │ ├── constants │ │ ├── index.ts │ │ ├── interface │ │ │ └── services │ │ │ │ └── axios.ts │ │ └── configs.tsx │ ├── locale │ │ ├── en.ts │ │ └── I18nConfig.ts │ └── MainLayout.tsx ├── jest.config.js ├── .bundle │ └── config ├── generators │ ├── module │ │ ├── Module.index.js.hbs │ │ └── Module.view.js.hbs │ ├── redux │ │ ├── constants.js.hbs │ │ ├── selectors.js.hbs │ │ ├── interface.js.hbs │ │ ├── saga.js.hbs │ │ └── reducer.js.hbs │ └── component │ │ └── index.js.hbs ├── app.json ├── .husky │ └── pre-commit ├── android │ ├── app │ │ ├── debug.keystore │ │ ├── src │ │ │ ├── main │ │ │ │ ├── assets │ │ │ │ │ └── fonts │ │ │ │ │ │ ├── Roboto-Bold.ttf │ │ │ │ │ │ └── Roboto-Regular.ttf │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── ic_launcher_round.png │ │ │ │ │ ├── values │ │ │ │ │ │ ├── strings.xml │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── drawable │ │ │ │ │ │ └── rn_edit_text_material.xml │ │ │ │ ├── java │ │ │ │ │ └── com │ │ │ │ │ │ └── saigontechnology │ │ │ │ │ │ └── rnbaseprojecttypescript │ │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ │ └── MainApplication.kt │ │ │ │ └── AndroidManifest.xml │ │ │ └── debug │ │ │ │ └── AndroidManifest.xml │ │ └── proguard-rules.pro │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── link-assets-manifest.json │ ├── settings.gradle │ ├── build.gradle │ ├── gradle.properties │ └── gradlew.bat ├── assets │ └── fonts │ │ ├── Roboto-Bold.ttf │ │ └── Roboto-Regular.ttf ├── ios │ ├── RNBaseProjectTypeScript │ │ ├── Images.xcassets │ │ │ ├── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── 100.png │ │ │ │ ├── 102.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ ├── 128.png │ │ │ │ ├── 144.png │ │ │ │ ├── 152.png │ │ │ │ ├── 16.png │ │ │ │ ├── 167.png │ │ │ │ ├── 172.png │ │ │ │ ├── 180.png │ │ │ │ ├── 196.png │ │ │ │ ├── 20.png │ │ │ │ ├── 216.png │ │ │ │ ├── 256.png │ │ │ │ ├── 29.png │ │ │ │ ├── 32.png │ │ │ │ ├── 40.png │ │ │ │ ├── 48.png │ │ │ │ ├── 50.png │ │ │ │ ├── 512.png │ │ │ │ ├── 55.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 64.png │ │ │ │ ├── 66.png │ │ │ │ ├── 72.png │ │ │ │ ├── 76.png │ │ │ │ ├── 80.png │ │ │ │ ├── 87.png │ │ │ │ ├── 88.png │ │ │ │ ├── 92.png │ │ │ │ └── 1024.png │ │ │ ├── AppIconDev.appiconset │ │ │ │ ├── 100.png │ │ │ │ ├── 102.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ ├── 128.png │ │ │ │ ├── 144.png │ │ │ │ ├── 152.png │ │ │ │ ├── 16.png │ │ │ │ ├── 167.png │ │ │ │ ├── 172.png │ │ │ │ ├── 180.png │ │ │ │ ├── 196.png │ │ │ │ ├── 20.png │ │ │ │ ├── 216.png │ │ │ │ ├── 256.png │ │ │ │ ├── 29.png │ │ │ │ ├── 32.png │ │ │ │ ├── 40.png │ │ │ │ ├── 48.png │ │ │ │ ├── 50.png │ │ │ │ ├── 512.png │ │ │ │ ├── 55.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 64.png │ │ │ │ ├── 66.png │ │ │ │ ├── 72.png │ │ │ │ ├── 76.png │ │ │ │ ├── 80.png │ │ │ │ ├── 87.png │ │ │ │ ├── 88.png │ │ │ │ ├── 92.png │ │ │ │ └── 1024.png │ │ │ └── AppIconStg.appiconset │ │ │ │ ├── 100.png │ │ │ │ ├── 102.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ ├── 128.png │ │ │ │ ├── 144.png │ │ │ │ ├── 152.png │ │ │ │ ├── 16.png │ │ │ │ ├── 167.png │ │ │ │ ├── 172.png │ │ │ │ ├── 180.png │ │ │ │ ├── 196.png │ │ │ │ ├── 20.png │ │ │ │ ├── 216.png │ │ │ │ ├── 256.png │ │ │ │ ├── 29.png │ │ │ │ ├── 32.png │ │ │ │ ├── 40.png │ │ │ │ ├── 48.png │ │ │ │ ├── 50.png │ │ │ │ ├── 512.png │ │ │ │ ├── 55.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 64.png │ │ │ │ ├── 66.png │ │ │ │ ├── 72.png │ │ │ │ ├── 76.png │ │ │ │ ├── 80.png │ │ │ │ ├── 87.png │ │ │ │ ├── 88.png │ │ │ │ ├── 92.png │ │ │ │ └── 1024.png │ │ ├── AppDelegate.h │ │ ├── main.m │ │ ├── AppDelegate.mm │ │ ├── PrivacyInfo.xcprivacy │ │ ├── Info.plist │ │ └── LaunchScreen.storyboard │ ├── RNBaseProjectTypeScript.xcworkspace │ │ ├── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ │ └── contents.xcworkspacedata │ ├── link-assets-manifest.json │ ├── tmp.xcconfig │ ├── .xcode.env │ ├── RNBaseProjectTypeScriptTests │ │ ├── Info.plist │ │ └── RNBaseProjectTypeScriptTests.m │ ├── PrivacyInfo.xcprivacy │ ├── Podfile │ └── RNBaseProjectTypeScript.xcodeproj │ │ └── xcshareddata │ │ └── xcschemes │ │ ├── RNBaseProjectTypeScriptStg.xcscheme │ │ └── RNBaseProjectTypeScriptDev.xcscheme ├── react-native.config.js ├── babel.config.js ├── fastlane │ ├── Pluginfile │ ├── Appfile │ └── README.md ├── .prettierrc.js ├── index.js ├── __tests__ │ ├── App.test.js │ └── App.test.tsx ├── scripts │ ├── post-install.sh │ ├── build-app.sh │ ├── code-push.sh │ ├── run-app.sh │ └── genimg.js ├── metro.config.js ├── tsconfig.json ├── .env.production ├── .env.staging ├── .env ├── .env.development ├── Gemfile ├── .eslintrc.js ├── _gitignore ├── App.tsx ├── azure-pipelines.yml ├── Gemfile.lock ├── package.json ├── README.md ├── bitbucket-pipelines.yml └── plopfile.js ├── .DS_Store ├── docs ├── images │ └── Logo.png ├── networking.md ├── config-redux-persist.md └── fastlane.md ├── .husky └── pre-commit ├── jest.config.js ├── template.config.js ├── .github ├── CODEOWNERS ├── workflows │ ├── auto-assign.yml │ ├── label-conflict.yml │ ├── eslint.yml │ └── pull-request.yml └── dependabot.yml ├── tsconfig.json ├── .gitignore ├── LICENSE ├── package.json └── .vscode └── settings.json /template/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /template/src/screens/HomeComponent/index.ts: -------------------------------------------------------------------------------- 1 | export * from './HomeScreen' 2 | -------------------------------------------------------------------------------- /template/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | } 4 | -------------------------------------------------------------------------------- /template/.bundle/config: -------------------------------------------------------------------------------- 1 | BUNDLE_PATH: "vendor/bundle" 2 | BUNDLE_FORCE_RUBY_PLATFORM: 1 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/.DS_Store -------------------------------------------------------------------------------- /template/generators/module/Module.index.js.hbs: -------------------------------------------------------------------------------- 1 | export * from './{{properCase name}}Screen' 2 | -------------------------------------------------------------------------------- /template/src/store/types/loading.ts: -------------------------------------------------------------------------------- 1 | export interface ILoading { 2 | [key: string]: boolean 3 | } 4 | -------------------------------------------------------------------------------- /template/src/store/selectors/index.ts: -------------------------------------------------------------------------------- 1 | // Selector 2 | export * from './app' 3 | export * from './user' 4 | -------------------------------------------------------------------------------- /template/src/screens/AuthComponent/index.ts: -------------------------------------------------------------------------------- 1 | export * from './LoginScreen' 2 | export * from './SignUpScreen' 3 | -------------------------------------------------------------------------------- /template/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNBaseProjectTypeScript", 3 | "displayName": "RNBaseProjectTypeScript" 4 | } 5 | -------------------------------------------------------------------------------- /docs/images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/docs/images/Logo.png -------------------------------------------------------------------------------- /template/.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn pretty && yarn lint:fix 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | cd template && yarn pretty && yarn lint:fix 5 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'react-native', 3 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'] 4 | }; -------------------------------------------------------------------------------- /template/src/assets/images/sts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/src/assets/images/sts.png -------------------------------------------------------------------------------- /template/src/themes/fonts.ts: -------------------------------------------------------------------------------- 1 | const fonts = { 2 | bold: 'Roboto-Bold', 3 | regular: 'Roboto-Regular', 4 | } as const 5 | 6 | export {fonts} 7 | -------------------------------------------------------------------------------- /template/src/themes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './colors' 2 | export * from './metrics' 3 | export * from './fonts' 4 | export * from './images' 5 | -------------------------------------------------------------------------------- /template/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/debug.keystore -------------------------------------------------------------------------------- /template/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "author": "xcode", 4 | "version": 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /template/src/assets/images/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/src/assets/images/close.png -------------------------------------------------------------------------------- /template/src/assets/images/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/src/assets/images/info.png -------------------------------------------------------------------------------- /template/src/services/api/api.ts: -------------------------------------------------------------------------------- 1 | export const AUTH_API = { 2 | // ADD ENDPOINT REFRESH TOKEN HERE 3 | refreshToken: 'api/refreshToken', 4 | } 5 | -------------------------------------------------------------------------------- /template/react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | project: { 3 | ios: {}, 4 | android: {}, 5 | }, 6 | assets: ['./assets/fonts'], 7 | } 8 | -------------------------------------------------------------------------------- /template/src/screens/index.ts: -------------------------------------------------------------------------------- 1 | // Screen Export 2 | export * from './AuthComponent' 3 | export * from './HomeComponent' 4 | export * from './SplashScreen' 5 | -------------------------------------------------------------------------------- /template/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : RCTAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /template/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /template/src/store/types/index.ts: -------------------------------------------------------------------------------- 1 | // Export Type 2 | export * from './action' 3 | export * from './app' 4 | export * from './loading' 5 | export * from './store' 6 | export * from './user' 7 | -------------------------------------------------------------------------------- /template.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | placeholderName: "RNBaseProjectTypeScript", 4 | 5 | titlePlaceholder: "RNBaseProjectTypeScript", 6 | 7 | templateDir: "./template", 8 | }; 9 | -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/assets/fonts/Roboto-Bold.ttf -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/assets/fonts/Roboto-Regular.ttf -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:@react-native/babel-preset'], 3 | plugins: [ 4 | 'react-native-reanimated/plugin', // this has to be list last 5 | ], 6 | } 7 | -------------------------------------------------------------------------------- /template/fastlane/Pluginfile: -------------------------------------------------------------------------------- 1 | # Autogenerated by fastlane 2 | # 3 | # Ensure this file is checked in to source control! 4 | 5 | gem 'fastlane-plugin-appcenter' 6 | gem 'fastlane-plugin-google_chat' 7 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/src/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './DebugMenu' 2 | export * from './Draggable' 3 | export * from './IndicatorDialog' 4 | export * from './Row' 5 | export * from './ScreenContainer' 6 | export * from './Toast' 7 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/100.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/102.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/114.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/120.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/144.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/152.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/167.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/172.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/180.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/196.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/20.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/216.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/40.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/48.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/50.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/55.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/57.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/58.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/60.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/66.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/72.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/76.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/80.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/87.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/88.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/92.png -------------------------------------------------------------------------------- /template/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | arrowParens: 'avoid', 3 | bracketSameLine: true, 4 | bracketSpacing: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | semi: false, 8 | printWidth: 110, 9 | } 10 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/100.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/102.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/114.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/120.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/128.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/144.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/152.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/16.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/167.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/172.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/180.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/196.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/20.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/216.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/256.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/29.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/32.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/40.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/48.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/50.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/512.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/55.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/57.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/58.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/60.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/64.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/66.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/72.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/76.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/80.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/87.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/88.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/92.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/100.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/102.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/114.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/120.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/128.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/144.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/152.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/16.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/167.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/172.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/172.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/180.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/196.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/196.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/20.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/216.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/216.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/256.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/29.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/32.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/40.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/48.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/50.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/512.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/55.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/57.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/58.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/60.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/64.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/66.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/72.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/76.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/80.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/87.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/88.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/88.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/92.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/92.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | @string/APP_NAME 3 | @string/CODEPUSH_KEY_ANDROID 4 | 5 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconDev.appiconset/1024.png -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saigontechnology/rn-base-project-typescript/HEAD/template/ios/RNBaseProjectTypeScript/Images.xcassets/AppIconStg.appiconset/1024.png -------------------------------------------------------------------------------- /template/src/store/selectors/user.ts: -------------------------------------------------------------------------------- 1 | import {IInitialState, IUser, IUserInfo} from '../types' 2 | 3 | export const getUserData = (state: IInitialState): IUser => state.user 4 | export const getUserInfo = (state: IInitialState): IUserInfo => getUserData(state).userInfo 5 | -------------------------------------------------------------------------------- /template/src/utilities/types.ts: -------------------------------------------------------------------------------- 1 | interface IDataRefs { 2 | eventName: string 3 | callback: (param: any) => void 4 | } 5 | 6 | export type IRefs = {[key: string]: IDataRefs} 7 | 8 | export type EmitterListener = { 9 | count: number 10 | refs: IRefs 11 | } 12 | -------------------------------------------------------------------------------- /template/src/themes/images.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @flow 3 | */ 4 | 5 | const Images = { 6 | close: require('./../assets/images/close.png'), 7 | info: require('./../assets/images/info.png'), 8 | sts: require('./../assets/images/sts.png'), 9 | } 10 | 11 | export {Images} 12 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/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 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in the repo. 2 | * @honghoangsts @baonguyenhsts @anhle10051996 @hoangSTS @huydosgtech @loc-nguyenthien @loido @nghoangchung @ThinhKimVo @TranQuangPhi @tuledu @vunguyendhSTS @ngochuyduong @hangnguyensaigontech 3 | -------------------------------------------------------------------------------- /template/src/store/actionTypes.ts: -------------------------------------------------------------------------------- 1 | import {IActionTypes} from './types' 2 | 3 | export const actionTypes = (actionName: string): IActionTypes => ({ 4 | ORIGIN: actionName, 5 | HANDLER: `${actionName}_REQUEST`, 6 | SUCCESS: `${actionName}_SUCCESS`, 7 | FAILURE: `${actionName}_FAILURE`, 8 | }) 9 | -------------------------------------------------------------------------------- /template/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | import 'intl-pluralrules' 5 | import 'react-native-gesture-handler' 6 | import {AppRegistry} from 'react-native' 7 | import App from './App' 8 | import {name as appName} from './app.json' 9 | 10 | AppRegistry.registerComponent(appName, () => App) 11 | -------------------------------------------------------------------------------- /template/src/store/saga/index.ts: -------------------------------------------------------------------------------- 1 | import {all} from 'redux-saga/effects' 2 | // Saga Imports 3 | import appSaga from './app' 4 | import userSaga from './user' 5 | 6 | export default function* rootSaga() { 7 | yield all([ 8 | // Sagas 9 | ...appSaga, 10 | ...userSaga, 11 | ]) 12 | } 13 | -------------------------------------------------------------------------------- /template/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | validateDistributionUrl=true -------------------------------------------------------------------------------- /template/generators/redux/constants.js.hbs: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '../actionTypes' 2 | import {IActionTypes} from '../types' 3 | 4 | const {{constantCase name}}_ACTIONS: IActionTypes = actionTypes('{{constantCase name}}_ACTIONS') 5 | 6 | export const {{constantCase name}}_CONSTANTS_ACTIONS = { 7 | {{constantCase name}}_ACTIONS, 8 | } 9 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/__tests__/App.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native' 6 | import React from 'react' 7 | import App from '../App' 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer' 11 | 12 | it('renders correctly', () => { 13 | renderer.create() 14 | }) 15 | -------------------------------------------------------------------------------- /template/generators/redux/selectors.js.hbs: -------------------------------------------------------------------------------- 1 | import {I{{properCase name}}, IInitialState} from '../types' 2 | 3 | const get{{properCase name}}Data = (state: IInitialState): I{{properCase name}} => state.{{camelCase name}} 4 | 5 | export const get{{properCase name}}StateData = (state: IInitialState): I{{properCase name}} => get{{properCase name}}Data(state).data -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /template/scripts/post-install.sh: -------------------------------------------------------------------------------- 1 | #1/use/bin/env bash 2 | 3 | # Do not install iOS Pods on CI 4 | npx pod-install 5 | 6 | echo "Removing temporary files..." 7 | rm -rf $TMPDIR/react-* 8 | rm -rf $TMPDIR/metro-* 9 | rm -rf $TMPDIR/haste-map-* 10 | rm -rf $TMPDIR/haste-task-* 11 | 12 | echo "Clearing watchman cache..." 13 | watchman watch-del-all 14 | -------------------------------------------------------------------------------- /template/src/store/initialState.ts: -------------------------------------------------------------------------------- 1 | import RouteKey from '../navigation/RouteKey' 2 | import {IInitialState} from './types' 3 | 4 | const INITIAL_STATE: IInitialState = { 5 | app: {showGlobalIndicator: false, appState: RouteKey.SplashScreen}, 6 | user: { 7 | userInfo: {}, 8 | }, 9 | loading: {}, 10 | } 11 | 12 | export default INITIAL_STATE 13 | -------------------------------------------------------------------------------- /template/ios/link-assets-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "migIndex": 1, 3 | "data": [ 4 | { 5 | "path": "assets/fonts/Roboto-Bold.ttf", 6 | "sha1": "0a1793926e2ee724cf2ff3fc7adc745348659f82" 7 | }, 8 | { 9 | "path": "assets/fonts/Roboto-Regular.ttf", 10 | "sha1": "096c9245b6a192d1403a82848e104a65f578a8ec" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /template/src/store/types/store.ts: -------------------------------------------------------------------------------- 1 | // Import Type 2 | import {IApp} from './app' 3 | import {ILoading} from './loading' 4 | import {IUser} from './user' 5 | 6 | export interface IInitialState { 7 | // State 8 | app: IApp 9 | user: IUser 10 | loading: ILoading 11 | } 12 | 13 | export interface IError { 14 | code: number 15 | message: string 16 | } 17 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /template/android/link-assets-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "migIndex": 1, 3 | "data": [ 4 | { 5 | "path": "assets/fonts/Roboto-Bold.ttf", 6 | "sha1": "0a1793926e2ee724cf2ff3fc7adc745348659f82" 7 | }, 8 | { 9 | "path": "assets/fonts/Roboto-Regular.ttf", 10 | "sha1": "096c9245b6a192d1403a82848e104a65f578a8ec" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /template/src/store/constants/app.ts: -------------------------------------------------------------------------------- 1 | import {actionTypes} from '../actionTypes' 2 | import {IActionTypes} from '../types' 3 | 4 | const GET_SETTING_APP_ACTIONS: IActionTypes = actionTypes('GET_SETTING_APP_ACTIONS') 5 | 6 | const LOGIN_ACTIONS: IActionTypes = actionTypes('LOGIN_ACTIONS') 7 | 8 | export const APP_CONSTANTS_ACTIONS = { 9 | GET_SETTING_APP_ACTIONS, 10 | LOGIN_ACTIONS, 11 | } 12 | -------------------------------------------------------------------------------- /template/src/navigation/RouteKey.ts: -------------------------------------------------------------------------------- 1 | export enum RouteKey { 2 | /** Screen */ 3 | SplashScreen = 'SplashScreen', 4 | LoginScreen = 'LoginScreen', 5 | SignUpScreen = 'SignUpScreen', 6 | HomeScreen = 'HomeScreen', 7 | 8 | /** Stack */ 9 | AuthStack = 'AuthStack', 10 | HomeStack = 'HomeStack', 11 | 12 | /** Tab */ 13 | MainTab = 'MainTab', 14 | } 15 | 16 | export default RouteKey 17 | -------------------------------------------------------------------------------- /template/generators/redux/interface.js.hbs: -------------------------------------------------------------------------------- 1 | import {IActionDispatch} from './action' 2 | 3 | export interface I{{properCase name}} { 4 | [key: string]: any 5 | } 6 | 7 | export interface I{{properCase name}}Actions { 8 | {{camelCase name}}Handle: IActionDispatch 9 | {{camelCase name}}Success: IActionDispatch 10 | {{camelCase name}}Failure: IActionDispatch 11 | getAction: IActionDispatch 12 | } 13 | -------------------------------------------------------------------------------- /template/metro.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config') 3 | 4 | /** 5 | * Metro configuration 6 | * https://reactnative.dev/docs/metro 7 | * 8 | * @type {import('metro-config').MetroConfig} 9 | */ 10 | const config = {} 11 | 12 | module.exports = mergeConfig(getDefaultConfig(__dirname), config) 13 | -------------------------------------------------------------------------------- /template/tsconfig.json: -------------------------------------------------------------------------------- 1 | // prettier-ignore 2 | { 3 | "extends": "@react-native/typescript-config/tsconfig.json", /* Recommended React Native TSConfig base */ 4 | "compilerOptions": { 5 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 6 | 7 | /* Completeness */ 8 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/auto-assign.yml: -------------------------------------------------------------------------------- 1 | name: PR assignment 2 | 3 | on: 4 | pull_request: 5 | types: [opened, edited, synchronize, reopened] 6 | 7 | jobs: 8 | auto-assign: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Auto-assign Issue 12 | uses: pozil/auto-assign-issue@v1.11.0 13 | with: 14 | assignees: ${{ github.actor }} 15 | numOfAssignee: 1 16 | allowSelfAssign: true 17 | -------------------------------------------------------------------------------- /template/.env.production: -------------------------------------------------------------------------------- 1 | API_URL= 2 | APP_ENV=production 3 | CODEPUSH_KEY_IOS= 4 | CODEPUSH_KEY_ANDROID= 5 | APP_ID=com.saigontechnology.rnbaseprojecttypescript 6 | APP_NAME=Base 7 | 8 | #Config for codepush and build 9 | APPCENTER_TOKEN_ANDROID= 10 | APPCENTER_TOKEN_IOS= 11 | APPCENTER_APP_NAME_ANDROID= 12 | APPCENTER_APP_NAME_IOS= 13 | APPCENTER_APP_DISPLAY_NAME= 14 | APPCENTER_RELEASE_NOTE= 15 | APPCENTER_DISTRIBUTE_DESTINATIONS=Public 16 | APPLE_TEAM_ID= 17 | -------------------------------------------------------------------------------- /template/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native' 6 | import React from 'react' 7 | import App from '../App' 8 | 9 | // Note: import explicitly to use the types shipped with jest. 10 | import {it} from '@jest/globals' 11 | 12 | // Note: test renderer must be required after react-native. 13 | import renderer from 'react-test-renderer' 14 | 15 | it('renders correctly', () => { 16 | renderer.create() 17 | }) 18 | -------------------------------------------------------------------------------- /template/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'RNBaseProjectTypeScript' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app', ':react-native-code-push' 4 | includeBuild('../node_modules/@react-native/gradle-plugin') 5 | project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') 6 | -------------------------------------------------------------------------------- /template/.env.staging: -------------------------------------------------------------------------------- 1 | API_URL= 2 | APP_ENV=staging 3 | CODEPUSH_KEY_IOS= 4 | CODEPUSH_KEY_ANDROID= 5 | APP_ID=com.saigontechnology.rnbaseprojecttypescript.staging 6 | APP_NAME=BaseStaging 7 | 8 | #Config for codepush and build 9 | APPCENTER_TOKEN_ANDROID= 10 | APPCENTER_TOKEN_IOS= 11 | APPCENTER_APP_NAME_ANDROID= 12 | APPCENTER_APP_NAME_IOS= 13 | APPCENTER_APP_DISPLAY_NAME= 14 | APPCENTER_RELEASE_NOTE= 15 | APPCENTER_DISTRIBUTE_DESTINATIONS=Public 16 | APPLE_TEAM_ID= 17 | -------------------------------------------------------------------------------- /template/ios/tmp.xcconfig: -------------------------------------------------------------------------------- 1 | API_URL= 2 | APP_ENV=dev 3 | CODEPUSH_KEY_IOS= 4 | CODEPUSH_KEY_ANDROID= 5 | APP_ID=com.saigontechnology.rnbaseprojecttypescript.development 6 | APP_NAME=BaseDev 7 | PROJECT_NAME=RNBaseProjectTypeScript 8 | APPCENTER_TOKEN_ANDROID= 9 | APPCENTER_TOKEN_IOS= 10 | APPCENTER_APP_NAME_ANDROID= 11 | APPCENTER_APP_NAME_IOS= 12 | APPCENTER_APP_DISPLAY_NAME= 13 | APPCENTER_RELEASE_NOTE= 14 | APPCENTER_DISTRIBUTE_DESTINATIONS=Public 15 | APPLE_TEAM_ID= 16 | -------------------------------------------------------------------------------- /template/src/components/Row.tsx: -------------------------------------------------------------------------------- 1 | import React, {PropsWithChildren} from 'react' 2 | import {View, StyleSheet, StyleProp, ViewStyle} from 'react-native' 3 | 4 | interface IRowProps extends PropsWithChildren { 5 | style?: StyleProp 6 | } 7 | 8 | export const Row = ({style, children}: IRowProps) => {children} 9 | 10 | const styles = StyleSheet.create({ 11 | row: { 12 | flexDirection: 'row', 13 | }, 14 | }) 15 | -------------------------------------------------------------------------------- /template/src/screens/HomeComponent/HomeScreen.tsx: -------------------------------------------------------------------------------- 1 | import {NativeStackScreenProps} from '@react-navigation/native-stack' 2 | import React from 'react' 3 | import {ScreenContainer} from '../../components' 4 | import RouteKey from '../../navigation/RouteKey' 5 | import {AppStackParamList} from '../../navigation/types' 6 | 7 | type Props = NativeStackScreenProps 8 | 9 | export const HomeScreen: React.FC = () => 10 | -------------------------------------------------------------------------------- /template/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /template/src/screens/AuthComponent/LoginScreen.tsx: -------------------------------------------------------------------------------- 1 | import {NativeStackScreenProps} from '@react-navigation/native-stack' 2 | import React from 'react' 3 | import {ScreenContainer} from '../../components' 4 | import RouteKey from '../../navigation/RouteKey' 5 | import {AppStackParamList} from '../../navigation/types' 6 | 7 | type Props = NativeStackScreenProps 8 | 9 | export const LoginScreen: React.FC = () => 10 | -------------------------------------------------------------------------------- /template/src/screens/AuthComponent/SignUpScreen.tsx: -------------------------------------------------------------------------------- 1 | import {NativeStackScreenProps} from '@react-navigation/native-stack' 2 | import React from 'react' 3 | import {ScreenContainer} from '../../components' 4 | import RouteKey from '../../navigation/RouteKey' 5 | import {AppStackParamList} from '../../navigation/types' 6 | 7 | type Props = NativeStackScreenProps 8 | 9 | export const SignUpScreen: React.FC = () => 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/react-native/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "es6", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "noResolve": false, 8 | "noImplicitAny": false, 9 | "removeComments": true, 10 | "sourceMap": true, 11 | "allowJs": true, 12 | "jsx": "react" 13 | }, 14 | "exclude": [ 15 | "./node_modules/**/*" 16 | ] 17 | } -------------------------------------------------------------------------------- /template/src/constants/index.ts: -------------------------------------------------------------------------------- 1 | import {IResponseCode, IToken, ITokenType} from './interface/services/axios' 2 | 3 | export const TOKEN: IToken = { 4 | token: 'TOKEN_KEY', 5 | refreshToken: 'REFRESH_TOKEN_KEY', 6 | } 7 | 8 | export const RESPONSE_CODE: IResponseCode = { 9 | success: [200, 201], 10 | unauthorized: [401, 403], 11 | } 12 | 13 | export const TOKEN_TYPE: ITokenType = { 14 | Bearer: 'Bearer', 15 | Basic: 'Basic', 16 | } 17 | 18 | export const AXIOS_TIMEOUT = 6000 19 | -------------------------------------------------------------------------------- /template/.env: -------------------------------------------------------------------------------- 1 | API_URL= 2 | APP_ENV=development 3 | CODEPUSH_KEY_IOS= 4 | CODEPUSH_KEY_ANDROID= 5 | APP_ID=com.saigontechnology.rnbaseprojecttypescript.development 6 | APP_NAME=BaseDev 7 | PROJECT_NAME=RNBaseProjectTypeScript 8 | 9 | #Config for codepush and build 10 | APPCENTER_TOKEN_ANDROID= 11 | APPCENTER_TOKEN_IOS= 12 | APPCENTER_APP_NAME_ANDROID= 13 | APPCENTER_APP_NAME_IOS= 14 | APPCENTER_APP_DISPLAY_NAME= 15 | APPCENTER_RELEASE_NOTE= 16 | APPCENTER_DISTRIBUTE_DESTINATIONS=Public 17 | APPLE_TEAM_ID= 18 | -------------------------------------------------------------------------------- /template/src/store/selectors/app.ts: -------------------------------------------------------------------------------- 1 | import {IApp, IInitialState} from '../types' 2 | 3 | const getAppData = (state: IInitialState): IApp => state.app 4 | 5 | export const getLoadingIndicator = (state: IInitialState) => getAppData(state).showGlobalIndicator 6 | export const getAppStackState = (state: IInitialState): string => getAppData(state).appState 7 | export const getCodePushKey = (state: IInitialState) => getAppData(state).codePushKey 8 | export const getApiUrl = (state: IInitialState) => getAppData(state).apiUrl || '' 9 | -------------------------------------------------------------------------------- /template/src/store/types/action.ts: -------------------------------------------------------------------------------- 1 | import {PayloadAction} from '@reduxjs/toolkit' 2 | 3 | export interface IAction { 4 | type: string 5 | payload: PayloadAction 6 | onSuccess: () => void 7 | onFailure: () => void 8 | } 9 | 10 | export interface IActionDispatch { 11 | type: string 12 | payload: any 13 | onSuccess: () => void 14 | onFailure: () => void 15 | } 16 | 17 | export interface IActionTypes { 18 | ORIGIN: string 19 | HANDLER: string 20 | SUCCESS: string 21 | FAILURE: string 22 | } 23 | -------------------------------------------------------------------------------- /template/.env.development: -------------------------------------------------------------------------------- 1 | API_URL= 2 | APP_ENV=development 3 | CODEPUSH_KEY_IOS= 4 | CODEPUSH_KEY_ANDROID= 5 | APP_ID=com.saigontechnology.rnbaseprojecttypescript.development 6 | APP_NAME=BaseDevelopment 7 | PROJECT_NAME=RNBaseProjectTypeScript 8 | 9 | #Config for codepush and build 10 | APPCENTER_TOKEN_ANDROID= 11 | APPCENTER_TOKEN_IOS= 12 | APPCENTER_APP_NAME_ANDROID= 13 | APPCENTER_APP_NAME_IOS= 14 | APPCENTER_APP_DISPLAY_NAME= 15 | APPCENTER_RELEASE_NOTE= 16 | APPCENTER_DISTRIBUTE_DESTINATIONS=Public 17 | APPLE_TEAM_ID= 18 | -------------------------------------------------------------------------------- /template/src/store/types/app.ts: -------------------------------------------------------------------------------- 1 | import {IActionDispatch} from './action' 2 | 3 | export interface IApp { 4 | showGlobalIndicator?: boolean 5 | appState: string 6 | showSearchBar?: boolean 7 | codePushKey?: string 8 | apiUrl?: string 9 | } 10 | 11 | export interface IAppActions { 12 | getSettings: IActionDispatch 13 | setAppStack: IActionDispatch 14 | getSettingsSuccess: IActionDispatch 15 | setShowGlobalIndicator: IActionDispatch 16 | setCodePushKey: IActionDispatch 17 | setApiUrl: IActionDispatch 18 | } 19 | -------------------------------------------------------------------------------- /template/src/store/types/user.ts: -------------------------------------------------------------------------------- 1 | import {ActionCreatorWithoutPayload} from '@reduxjs/toolkit' 2 | import {IActionDispatch} from './action' 3 | 4 | export interface IUserInfo {} 5 | 6 | export interface ITokenData {} 7 | 8 | export interface IUser { 9 | userInfo: IUserInfo 10 | isEndUser?: boolean 11 | tokenData?: ITokenData 12 | contentFlagged?: string 13 | } 14 | 15 | export interface IUserActions { 16 | userLogin: IActionDispatch 17 | userSignUp: IActionDispatch 18 | logout: ActionCreatorWithoutPayload 19 | } 20 | -------------------------------------------------------------------------------- /template/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 | -------------------------------------------------------------------------------- /template/src/components/ScreenContainer.tsx: -------------------------------------------------------------------------------- 1 | import React, {PropsWithChildren} from 'react' 2 | import {StyleSheet, View, ViewStyle, StyleProp} from 'react-native' 3 | 4 | interface IRowProps extends PropsWithChildren { 5 | style?: StyleProp 6 | } 7 | 8 | export const ScreenContainer = ({children, style, ...rest}: IRowProps) => ( 9 | 10 | {children} 11 | 12 | ) 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex: 1, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /template/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.6.10" 5 | 6 | # Cocoapods 1.15 introduced a bug which break the build. We will remove the upper 7 | # bound in the template on Cocoapods with next React Native release. 8 | gem 'cocoapods', '>= 1.13', '< 1.15' 9 | gem 'activesupport', '>= 6.1.7.5', '< 7.1.0' 10 | gem 'fastlane' 11 | 12 | plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') 13 | eval_gemfile(plugins_path) if File.exist?(plugins_path) 14 | -------------------------------------------------------------------------------- /template/src/navigation/types.ts: -------------------------------------------------------------------------------- 1 | import {ParamListBase} from '@react-navigation/native' 2 | import RouteKey from './RouteKey' 3 | 4 | /** Type */ 5 | type HomeScreenParams = { 6 | userId: '' 7 | } 8 | type LoginScreenParams = object 9 | type SignUpScreenParams = object 10 | 11 | export interface AppStackParamList extends ParamListBase { 12 | /** Params */ 13 | [RouteKey.HomeScreen]: HomeScreenParams 14 | [RouteKey.LoginScreen]: LoginScreenParams 15 | [RouteKey.SignUpScreen]: SignUpScreenParams 16 | } 17 | 18 | export type IItemTabBar = { 19 | route: string 20 | title: string 21 | } 22 | -------------------------------------------------------------------------------- /template/generators/component/index.js.hbs: -------------------------------------------------------------------------------- 1 | import React, {ReactElement} from 'react' 2 | import {StyleSheet, View, ViewStyle, StyleProp} from 'react-native' 3 | 4 | interface IProps { 5 | children: ReactElement | null 6 | style?: StyleProp 7 | } 8 | 9 | const {{properCase name}} = ({children, style, ...rest}: IProps) => { 10 | return ( 11 | 12 | {children} 13 | 14 | ) 15 | } 16 | 17 | const styles = StyleSheet.create({ 18 | container: { 19 | flex: 1, 20 | }, 21 | }) 22 | 23 | export default {{properCase name}} 24 | -------------------------------------------------------------------------------- /template/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 | 12 | -keep class com.swmansion.reanimated.** { *; } 13 | -keep class com.facebook.react.turbomodule.** { *; } 14 | -------------------------------------------------------------------------------- /template/generators/module/Module.view.js.hbs: -------------------------------------------------------------------------------- 1 | import {NativeStackScreenProps} from '@react-navigation/native-stack' 2 | import React from 'react' 3 | import {ScreenContainer} from '../../components' 4 | import RouteKey from '../../navigation/RouteKey' 5 | import {AppStackParamList} from '../../navigation/types' 6 | 7 | type Props = NativeStackScreenProps 8 | 9 | const {{properCase name}}Screen: React.FC = props => { 10 | const {navigation, route} = props 11 | return } /> 12 | } 13 | 14 | export default {{properCase name}}Screen 15 | -------------------------------------------------------------------------------- /template/src/store/selectors/loading.ts: -------------------------------------------------------------------------------- 1 | import {IInitialState} from '../types' 2 | 3 | export const getLoadingSelector = (state: IInitialState, actionTypes: [string]): boolean => { 4 | if (Array.isArray(actionTypes)) { 5 | // some of element of list actionTypes is dispatched in redux will return state loading 6 | return actionTypes.some(r => { 7 | const matches = /(.*)_(REQUEST|SUCCESS|FAILURE)/.exec(r) 8 | if (!matches) { 9 | return false 10 | } 11 | const [, requestName, requestState] = matches 12 | return state.loading[`${requestName}`] || false 13 | }) 14 | } 15 | return false 16 | } 17 | -------------------------------------------------------------------------------- /template/src/themes/colors.ts: -------------------------------------------------------------------------------- 1 | const colors = { 2 | primary: '#65c8c6', 3 | black: '#1F1F1F', 4 | white: '#ffffff', 5 | gray: '#454545', 6 | red: '#ff0009', 7 | error: '#FF5247', 8 | success: '#23C16B', 9 | warning: '#FFB323', 10 | info: '#48A7F8', 11 | } as const 12 | 13 | const getColorOpacity = (color: string, opacity: number): string => { 14 | if (opacity >= 0 && opacity <= 1 && color.includes('#')) { 15 | const hexValue = Math.round(opacity * 255).toString(16) 16 | return `${color.slice(0, 7)}${hexValue.padStart(2, '0').toUpperCase()}` 17 | } 18 | return color 19 | } 20 | 21 | export {colors, getColorOpacity} 22 | -------------------------------------------------------------------------------- /template/src/locale/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | appName: 'App Name', 3 | appVersion: 'App Version', 4 | buildNumber: 'Build #', 5 | bundleId: 'Bundle Identifier', 6 | deviceId: 'Device', 7 | appEnv: 'App Env', 8 | info: 'Info', 9 | actions: 'Actions', 10 | pleaseRestartAppForChanges: 'Please restart app for the changes to take effects', 11 | close: 'Close', 12 | on: 'On', 13 | off: 'Off', 14 | store: 'Store', 15 | testingEnvironment: 'Testing Environment', 16 | current: 'Current', 17 | update: 'Update', 18 | toggleLanguages: 'Toggle Languages', 19 | currentLanguage: 'Current Language', 20 | codePush: 'CodePush Environment', 21 | } as const 22 | -------------------------------------------------------------------------------- /template/src/store/saga/app.ts: -------------------------------------------------------------------------------- 1 | import {AnyAction} from 'redux' 2 | import {call, put, takeLatest} from 'redux-saga/effects' 3 | import RouteKey from '../../navigation/RouteKey' 4 | import {appActions} from '../reducers' 5 | import {getData} from '../../utilities/storage' 6 | import {TOKEN} from '../../constants' 7 | 8 | function* getAppSettingSaga(): IterableIterator { 9 | try { 10 | const token = yield call(getData, TOKEN.token) 11 | if (!token) { 12 | throw new Error('Token does not existed!') 13 | } 14 | } catch (e) { 15 | yield put(appActions.setAppStack(RouteKey.AuthStack)) 16 | } 17 | } 18 | 19 | export default [takeLatest(appActions.getSettings.type, getAppSettingSaga)] 20 | -------------------------------------------------------------------------------- /.github/workflows/label-conflict.yml: -------------------------------------------------------------------------------- 1 | name: "Maintenance" 2 | on: 3 | # So that PRs touching the same files as the push are updated 4 | push: 5 | # So that the `dirtyLabel` is removed if conflicts are resolve 6 | # We recommend `pull_request_target` so that github secrets are available. 7 | # In `pull_request` we wouldn't be able to change labels of fork PRs 8 | pull_request_target: 9 | types: [synchronize] 10 | 11 | jobs: 12 | main: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: check if PRs are conflict 16 | uses: eps1lon/actions-label-merge-conflict@releases/2.x 17 | with: 18 | dirtyLabel: "PR: has conflict" 19 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 20 | -------------------------------------------------------------------------------- /template/src/navigation/ScreenService.tsx: -------------------------------------------------------------------------------- 1 | import {NativeStackNavigationOptions} from '@react-navigation/native-stack' 2 | import RouteKey from './RouteKey' 3 | import {HomeNavigator} from './StackNavigation' 4 | 5 | export const optionsMatch = (screen: string): NativeStackNavigationOptions => { 6 | switch (screen) { 7 | case RouteKey.HomeScreen: 8 | case RouteKey.HomeStack: 9 | return { 10 | headerLeft: undefined, 11 | } 12 | default: 13 | return {} 14 | } 15 | } 16 | 17 | export const componentMatch = (stackName: string): Element | string => { 18 | switch (stackName) { 19 | case RouteKey.HomeStack: 20 | return HomeNavigator 21 | default: 22 | return '' 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /template/generators/redux/saga.js.hbs: -------------------------------------------------------------------------------- 1 | import {AnyAction} from 'redux' 2 | import {takeLatest, call, put} from 'redux-saga/effects' 3 | import {appActions, {{camelCase name}}Actions} from '../reducers' 4 | import {showToast} from '../../components' 5 | 6 | function* {{camelCase name}}Saga(action: any): IterableIterator { 7 | try { 8 | yield put(appActions.setShowGlobalIndicator(true)) 9 | // TODO: 10 | } catch (e) { 11 | if (e instanceof Error) { 12 | showToast({type: 'ERROR', message: e.message}) 13 | } 14 | } finally { 15 | yield put(appActions.setShowGlobalIndicator(false)) 16 | } 17 | } 18 | 19 | export default [takeLatest({{camelCase name}}Actions.getAction.type, {{camelCase name}}Saga)] 20 | -------------------------------------------------------------------------------- /template/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "34.0.0" 6 | minSdkVersion = 23 7 | compileSdkVersion = 34 8 | targetSdkVersion = 34 9 | ndkVersion = "26.1.10909125" 10 | kotlinVersion = "1.9.22" 11 | } 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | dependencies { 17 | classpath("com.android.tools.build:gradle") 18 | classpath("com.facebook.react:react-native-gradle-plugin") 19 | classpath("org.jetbrains.kotlin:kotlin-gradle-plugin") 20 | } 21 | } 22 | 23 | apply plugin: "com.facebook.react.rootproject" 24 | -------------------------------------------------------------------------------- /template/src/store/reducers/user.ts: -------------------------------------------------------------------------------- 1 | import {createSlice} from '@reduxjs/toolkit' 2 | import {IUser} from '../types' 3 | 4 | export const userInitialState: IUser = { 5 | userInfo: {}, 6 | isEndUser: false, 7 | tokenData: {}, 8 | contentFlagged: '', 9 | } 10 | 11 | export const userSlice = createSlice({ 12 | name: 'auth', 13 | initialState: userInitialState, 14 | reducers: { 15 | userLogin: () => { 16 | // TODO: add action when user login 17 | }, 18 | userSignUp: () => { 19 | // TODO: add action when user sign up 20 | }, 21 | logout: () => { 22 | // TODO: add action when user logout 23 | }, 24 | }, 25 | }) 26 | 27 | export const userActions = { 28 | ...userSlice.actions, 29 | } 30 | 31 | export default userSlice.reducer 32 | -------------------------------------------------------------------------------- /template/scripts/build-app.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | # Script will be showed below: 4 | 5 | # Reset 6 | Color_Off='\033[0m' # Text Reset 7 | 8 | # Regular Colors 9 | Red='\033[0;31m' # Red 10 | Green='\033[0;32m' # Green 11 | 12 | TYPE=$1 13 | 14 | function buildApp() { 15 | fastlane ios build type:build --env $1 16 | fastlane android build type:build --env $1 17 | } 18 | 19 | function main() { 20 | 21 | if [ -z $TYPE ]; then 22 | read -p 'Enter your environment (development,staging,production): ' env 23 | TYPE=$env 24 | fi 25 | 26 | # increaseCodepushVersion 27 | buildApp $TYPE 28 | 29 | if [ $? -eq 0 ]; then 30 | printf "\n$Green Build App Successful\n" 31 | else 32 | printf "\n$Red Build App Failed\n" 33 | fi 34 | } 35 | 36 | main 37 | -------------------------------------------------------------------------------- /template/scripts/code-push.sh: -------------------------------------------------------------------------------- 1 | #!bin/bash 2 | 3 | # Script will be showed below: 4 | 5 | # Reset 6 | Color_Off='\033[0m' # Text Reset 7 | 8 | # Regular Colors 9 | Red='\033[0;31m' # Red 10 | Green='\033[0;32m' # Green 11 | 12 | TYPE=$1 13 | 14 | 15 | function codePush() { 16 | fastlane ios build type:codepush --env $1 17 | fastlane android build type:codepush --env $1 18 | } 19 | 20 | function main() { 21 | 22 | if [ -z $TYPE ]; then 23 | read -p 'Enter your environment (development, staging, production): ' env 24 | TYPE=$env 25 | fi 26 | 27 | # increaseCodepushVersion 28 | codePush $TYPE 29 | 30 | if [ $? -eq 0 ]; then 31 | printf "\n$Green Code Push Successful\n" 32 | else 33 | printf "\n$Red Code Push Failed\n" 34 | fi 35 | } 36 | 37 | main 38 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScriptTests/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 | -------------------------------------------------------------------------------- /template/fastlane/Appfile: -------------------------------------------------------------------------------- 1 | # app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app 2 | # apple_id("[[APPLE_ID]]") # Your Apple Developer Portal username 3 | 4 | 5 | # For more information about the Appfile, see: 6 | # https://docs.fastlane.tools/advanced/#appfile 7 | app_identifier("your_bundle_id") # The bundle identifier of your app 8 | apple_id("#{ENV['APPLE_ID']}") # Your Apple email address 9 | 10 | itc_team_id("your_app_store_connect_id") # App Store Connect Team ID 11 | team_id(ENV["APPLE_TEAM_ID"]) # Developer Portal Team ID 12 | 13 | # For more information about the Appfile, see: 14 | # https://docs.fastlane.tools/advanced/#appfile 15 | 16 | json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one 17 | package_name("com.saigontechnology.rnbaseprojecttypescript") # e.g. com.krausefx.app 18 | 19 | -------------------------------------------------------------------------------- /template/src/components/IndicatorDialog.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | View, 4 | StyleSheet, 5 | Dimensions, 6 | ActivityIndicator, 7 | ViewStyle, 8 | StyleProp, 9 | ActivityIndicatorProps, 10 | } from 'react-native' 11 | 12 | interface IIndicatorDialogProps { 13 | containerStyle?: StyleProp 14 | activityIndicatorStyle?: ActivityIndicatorProps 15 | } 16 | 17 | const {width, height} = Dimensions.get('screen') 18 | export function IndicatorDialog(props: IIndicatorDialogProps) { 19 | return ( 20 | 21 | 22 | 23 | ) 24 | } 25 | 26 | const styles = StyleSheet.create({ 27 | container: { 28 | flex: 1, 29 | justifyContent: 'center', 30 | alignItems: 'center', 31 | position: 'absolute', 32 | width, 33 | height, 34 | backgroundColor: 'rgba(0, 0, 0, 0.2)', 35 | }, 36 | }) 37 | -------------------------------------------------------------------------------- /template/src/store/reducers/loading.ts: -------------------------------------------------------------------------------- 1 | import {PayloadAction, Reducer} from '@reduxjs/toolkit' 2 | import {ILoading} from '../types' 3 | 4 | const loadingReducer = (state: ILoading = {}, action: PayloadAction): ILoading => { 5 | const {type} = action 6 | // remove module name when create from createSlice 7 | // cause by create type by createSlice it auto add prefix module 8 | // this cause action param pass in will not match with type dispatch to store 9 | const removeModule = type.slice(type.indexOf('/') + 1) 10 | const matches = /(.*)_(REQUEST|SUCCESS|FAILURE)/.exec(removeModule) 11 | // not a *_REQUEST / *_SUCCESS / *_FAILURE actions, so we ignore them 12 | if (!matches) { 13 | return state 14 | } 15 | 16 | const [, requestName, requestState] = matches 17 | return { 18 | ...state, 19 | [requestName]: requestState === 'REQUEST', 20 | } 21 | } 22 | 23 | export default loadingReducer as Reducer 24 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import 3 | 4 | #import 5 | 6 | @implementation AppDelegate 7 | 8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 9 | { 10 | self.moduleName = @"RNBaseProjectTypeScript"; 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 | return [self bundleURL]; 21 | } 22 | 23 | - (NSURL *)bundleURL 24 | { 25 | #if DEBUG 26 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; 27 | #else 28 | return [CodePush bundleURL]; 29 | #endif 30 | } 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /template/src/store/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import {combineReducers} from '@reduxjs/toolkit' 2 | 3 | // Reducer Imports 4 | import app, {appInitialState} from './app' 5 | import loading from './loading' 6 | import user, {userInitialState} from './user' 7 | import {persistReducer} from 'redux-persist' 8 | import INITIAL_STATE from '../initialState' 9 | import AsyncStorage from '@react-native-async-storage/async-storage' 10 | 11 | // Reducer Export 12 | export * from './app' 13 | export * from './user' 14 | 15 | export const persistConfig = { 16 | key: 'root', 17 | storage: AsyncStorage, 18 | blacklist: Object.keys(INITIAL_STATE), 19 | } 20 | 21 | const userPersistConfig = { 22 | key: 'user', 23 | storage: AsyncStorage, 24 | } 25 | 26 | export const InitialState = { 27 | user: userInitialState, 28 | app: appInitialState, 29 | } 30 | 31 | export default combineReducers({ 32 | // Reducers 33 | user: persistReducer(userPersistConfig, user), 34 | app, 35 | loading, 36 | }) 37 | -------------------------------------------------------------------------------- /template/src/navigation/AppNavigator.tsx: -------------------------------------------------------------------------------- 1 | import {NavigationContainer} from '@react-navigation/native' 2 | import React from 'react' 3 | import {useSelector} from 'react-redux' 4 | import SplashScreen from '../screens/SplashScreen' 5 | import {getAppStackState} from '../store/selectors' 6 | import {navigationRef} from './NavigationService' 7 | import RouteKey from './RouteKey' 8 | import {AuthNavigator, HomeNavigator} from './StackNavigation' 9 | 10 | function AppNavigation(): React.ReactElement { 11 | const appState: string = useSelector(getAppStackState) 12 | 13 | function renderStack(): React.ReactNode { 14 | switch (appState) { 15 | case RouteKey.AuthStack: 16 | return 17 | case RouteKey.HomeStack: 18 | return 19 | default: 20 | return 21 | } 22 | } 23 | 24 | return {renderStack()} 25 | } 26 | 27 | export default AppNavigation 28 | -------------------------------------------------------------------------------- /template/src/constants/interface/services/axios.ts: -------------------------------------------------------------------------------- 1 | import {AxiosRequestHeaders, HeadersDefaults, RawAxiosRequestHeaders} from 'axios' 2 | 3 | export interface IToken { 4 | token: string 5 | refreshToken: string 6 | } 7 | 8 | export interface IResponseCode { 9 | success: number[] 10 | unauthorized: number[] 11 | } 12 | 13 | export interface ITokenType { 14 | Bearer: string 15 | Basic: string 16 | } 17 | 18 | export interface IAxiosHeaders extends HeadersDefaults { 19 | Authorization: string 20 | baseURL: string 21 | } 22 | 23 | export interface IAxiosMethod { 24 | get: string 25 | post: string 26 | put: string 27 | delete: string 28 | patch: string 29 | } 30 | 31 | export interface IAxiosError { 32 | error: string | undefined 33 | } 34 | 35 | export interface IParams { 36 | url: string 37 | method?: string 38 | body?: TRequest | string 39 | params?: TParams 40 | config?: AxiosRequestHeaders | RawAxiosRequestHeaders 41 | } 42 | -------------------------------------------------------------------------------- /template/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | '@react-native', 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react-hooks/recommended', 8 | 'plugin:@typescript-eslint/eslint-recommended', 9 | 'plugin:@typescript-eslint/recommended', 10 | ], 11 | parser: '@typescript-eslint/parser', 12 | plugins: ['@typescript-eslint', 'import', 'unused-imports'], 13 | overrides: [ 14 | { 15 | files: ['*.ts', '*.tsx', '*.js'], 16 | rules: { 17 | semi: 'off', 18 | 'comma-dangle': 'off', 19 | 'no-shadow': 'off', 20 | 'no-undef': 'off', 21 | 'import/no-cycle': 'warn', 22 | 'import/first': 'error', 23 | 'import/no-duplicates': 'error', 24 | 'unused-imports/no-unused-imports': 'error', 25 | 'arrow-body-style': ['error', 'as-needed'], 26 | '@typescript-eslint/no-shadow': ['error'], 27 | '@typescript-eslint/no-empty-interface': 'warn', 28 | }, 29 | }, 30 | ], 31 | } 32 | -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/saigontechnology/rnbaseprojecttypescript/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.saigontechnology.rnbaseprojecttypescript 2 | 3 | import com.facebook.react.ReactActivity 4 | import com.facebook.react.ReactActivityDelegate 5 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled 6 | import com.facebook.react.defaults.DefaultReactActivityDelegate 7 | 8 | class MainActivity : ReactActivity() { 9 | 10 | /** 11 | * Returns the name of the main component registered from JavaScript. This is used to schedule 12 | * rendering of the component. 13 | */ 14 | 15 | override fun getMainComponentName(): String = "RNBaseProjectTypeScript" 16 | 17 | /** 18 | * Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate] 19 | * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] 20 | */ 21 | override fun createReactActivityDelegate(): ReactActivityDelegate = 22 | DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) 23 | } 24 | -------------------------------------------------------------------------------- /template/src/utilities/storage.ts: -------------------------------------------------------------------------------- 1 | import AsyncStorage from '@react-native-async-storage/async-storage' 2 | 3 | export const clearAllData = async (): Promise => { 4 | try { 5 | await AsyncStorage.clear() 6 | } catch (e) { 7 | // Do something if there is error 8 | } 9 | } 10 | 11 | export const setData = async (key: string, data: any): Promise => { 12 | const stringValue = typeof data === 'string' ? data : JSON.stringify(data) 13 | try { 14 | await AsyncStorage.setItem(key, stringValue) 15 | } catch (e) { 16 | // Do something if there is error 17 | } 18 | } 19 | 20 | export const getData = async (key: string): Promise => { 21 | const jsonValue = await AsyncStorage.getItem(key) 22 | try { 23 | return jsonValue !== null ? JSON.parse(jsonValue) : null 24 | } catch (e) { 25 | return jsonValue 26 | } 27 | } 28 | 29 | export const removeData = async (key: string): Promise => { 30 | try { 31 | await AsyncStorage.removeItem(key) 32 | } catch (e) { 33 | // Do something if there is error 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /template/src/navigation/StackNavigation.tsx: -------------------------------------------------------------------------------- 1 | import {createNativeStackNavigator} from '@react-navigation/native-stack' 2 | import React from 'react' 3 | import {HomeScreen, LoginScreen, SignUpScreen} from '../screens' 4 | import RouteKey from './RouteKey' 5 | import {optionsMatch} from './ScreenService' 6 | import {AppStackParamList} from './types' 7 | 8 | const Stack = createNativeStackNavigator() 9 | 10 | export const HomeNavigator = () => ( 11 | 12 | 17 | 18 | ) 19 | 20 | export const AuthNavigator = () => ( 21 | 22 | 27 | 32 | 33 | ) 34 | -------------------------------------------------------------------------------- /template/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /.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 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | *.hprof 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | !debug.keystore 44 | 45 | # fastlane 46 | # 47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 48 | # screenshots whenever they are needed. 49 | # For more information about the recommended setup visit: 50 | # https://docs.fastlane.tools/best-practices/source-control/ 51 | 52 | */fastlane/report.xml 53 | */fastlane/Preview.html 54 | */fastlane/screenshots 55 | 56 | # Bundle artifact 57 | *.jsbundle 58 | 59 | # CocoaPods 60 | Pods/ 61 | /ProjectBase/ios/Pods/ 62 | -------------------------------------------------------------------------------- /template/generators/redux/reducer.js.hbs: -------------------------------------------------------------------------------- 1 | import {PayloadAction, createSlice} from '@reduxjs/toolkit' 2 | import { {{constantCase name}}_CONSTANTS_ACTIONS } from '../constants/{{camelCase name}}' 3 | import {I{{properCase name}} } from '../types/{{camelCase name}}' 4 | 5 | export const initialState: I{{properCase name}} = {} 6 | 7 | export const {{camelCase name}}Slice = createSlice({ 8 | name: '{{camelCase name}}', 9 | initialState, 10 | reducers: { 11 | [{{constantCase name}}_CONSTANTS_ACTIONS.{{constantCase name}}_ACTIONS.HANDLER]: (state, action: PayloadAction): void => {}, 12 | [{{constantCase name}}_CONSTANTS_ACTIONS.{{constantCase name}}_ACTIONS.SUCCESS]: (state, action: PayloadAction): void => {}, 13 | [{{constantCase name}}_CONSTANTS_ACTIONS.{{constantCase name}}_ACTIONS.FAILURE]: (state, action: PayloadAction): void => {}, 14 | getAction: (state, action: PayloadAction): void => {} 15 | }, 16 | extraReducers: builder => {}, 17 | }) 18 | 19 | export const {{camelCase name}}Actions = {{camelCase name}}Slice.actions 20 | 21 | export default {{camelCase name}}Slice.reducer 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Saigon Technology 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. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rn-base-project-ts", 3 | "version": "0.1.8", 4 | "main": "index.js", 5 | "repository": "https://github.com/saigontechnology/rn-base-project-typescript.git", 6 | "author": "React Native Team ", 7 | "license": "MIT", 8 | "keywords": [ 9 | "project base", 10 | "react native", 11 | "react native project base", 12 | "react native project base typescript", 13 | "react native structure", 14 | "react native structure typescript", 15 | "react native base", 16 | "react native base typescript", 17 | "react native template", 18 | "react native template typescript", 19 | "saigon technology solutions" 20 | ], 21 | "bugs": "https://github.com/saigontechnology/rn-base-project-typescript/issues", 22 | "scripts": { 23 | "release": "yarn publish", 24 | "prepare": "husky install" 25 | }, 26 | "devDependencies": { 27 | "@tsconfig/react-native": "^2.0.2", 28 | "@types/jest": "^29.2.3", 29 | "@types/react": "^18.0.25", 30 | "@types/react-native": "^0.70.7", 31 | "@types/react-test-renderer": "^18.0.0", 32 | "husky": "^8.0.0", 33 | "typescript": "^4.9.3" 34 | }, 35 | "packageManager": "yarn@1.22.22" 36 | } 37 | -------------------------------------------------------------------------------- /template/src/store/store.ts: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, configureStore} from '@reduxjs/toolkit' 2 | import {useSelector as useReduxSelector} from 'react-redux' 3 | import type {TypedUseSelectorHook} from 'react-redux' 4 | import {persistReducer, persistStore} from 'redux-persist' 5 | import reducers, {persistConfig} from './reducers' 6 | import createSagaMiddleware from 'redux-saga' 7 | import rootSaga from './saga' 8 | 9 | const sagaMiddleware = createSagaMiddleware() 10 | 11 | const middlewareEnhancer = applyMiddleware(sagaMiddleware) 12 | 13 | const persistedReducer = persistReducer(persistConfig, reducers) 14 | 15 | const store = configureStore({ 16 | reducer: persistedReducer, 17 | middleware: getDefaultMiddleware => getDefaultMiddleware({serializableCheck: false, thunk: false}), 18 | enhancers: getDefaultEnhancers => getDefaultEnhancers().concat(middlewareEnhancer), 19 | }) 20 | 21 | sagaMiddleware.run(rootSaga) 22 | 23 | const persistor = persistStore(store) 24 | 25 | export type AppDispatch = typeof store.dispatch 26 | export type AppState = typeof store.getState 27 | export type RootState = ReturnType 28 | 29 | export const useSelector: TypedUseSelectorHook = useReduxSelector 30 | export {store, persistor} 31 | -------------------------------------------------------------------------------- /template/_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 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # Maintain dependencies 9 | - package-ecosystem: "npm" # See documentation for possible values 10 | directory: "/template/" # Location of package manifests 11 | schedule: 12 | interval: "weekly" 13 | allow: 14 | # Allow both direct and indirect updates for all packages 15 | - dependency-type: "direct" 16 | # Allow up to 10 open pull requests for pip dependencies (default: 5) 17 | open-pull-requests-limit: 10 18 | pull-request-branch-name: 19 | # Separate sections of the branch name with a hyphen 20 | # for example, `dependabot-npm_and_yarn-next_js-acorn-6.4.1` 21 | separator: "-" 22 | # Auto rebasing for pull requests 23 | rebase-strategy: "auto" 24 | # Raise pull requests for version updates 25 | # to pip against the `develop` branch 26 | target-branch: "develop" 27 | versioning-strategy: increase 28 | -------------------------------------------------------------------------------- /template/src/locale/I18nConfig.ts: -------------------------------------------------------------------------------- 1 | import i18n from 'i18next' 2 | import {getI18n, initReactI18next} from 'react-i18next' 3 | 4 | import en from './en' 5 | 6 | export const configureLocalization = (locale: string, fallback = 'en') => 7 | i18n.use(initReactI18next).init({ 8 | returnNull: false, 9 | lng: locale, 10 | fallbackLng: fallback, 11 | 12 | resources: { 13 | en: { 14 | translation: en, 15 | }, 16 | }, 17 | 18 | debug: false, 19 | 20 | cache: { 21 | enabled: true, 22 | }, 23 | 24 | interpolation: { 25 | escapeValue: false, // not needed for react as it does escape per default to prevent xss! 26 | }, 27 | }) 28 | 29 | type TranslateRecord = typeof en 30 | 31 | type TranslateKey = keyof TranslateRecord 32 | 33 | type TranslateStr, Key extends keyof O> = Key extends string 34 | ? O[Key] extends string 35 | ? Key 36 | : `${Key}${TranslateStr extends string 37 | ? `.${TranslateStr}` 38 | : never}` 39 | : Key 40 | 41 | export const getString = (key: TranslateStr, params: object = {}) => 42 | getI18n() ? getI18n().t(key, params) : '' 43 | 44 | export default i18n 45 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.alwaysShowStatus": true, 3 | "eslint.packageManager": "yarn", 4 | "eslint.run": "onSave", 5 | "editor.suggestSelection": "first", 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true 8 | }, 9 | "editor.formatOnSave": true, 10 | "javascript.validate.enable": false, 11 | "[javascript]": { 12 | "editor.defaultFormatter": "esbenp.prettier-vscode" 13 | }, 14 | "[json]": { 15 | "editor.defaultFormatter": "esbenp.prettier-vscode" 16 | }, 17 | "javascript.updateImportsOnFileMove.enabled": "always", 18 | "files.exclude": { 19 | "**/.git": true, 20 | "**/.DS_Store": true, 21 | "**/node_modules": true, 22 | "node_modules": true, 23 | "**/.classpath": true, 24 | "**/.project": true, 25 | "**/.settings": true, 26 | "**/.factorypath": true 27 | }, 28 | "diffEditor.ignoreTrimWhitespace": false, 29 | "eslint.workingDirectories": [ 30 | { 31 | "directory": ".", 32 | "changeProcessCWD": true 33 | } 34 | ], 35 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 36 | "[typescriptreact]": { 37 | "editor.defaultFormatter": "esbenp.prettier-vscode" 38 | }, 39 | "[typescript]": { 40 | "editor.defaultFormatter": "esbenp.prettier-vscode" 41 | }, 42 | } 43 | -------------------------------------------------------------------------------- /template/src/store/reducers/app.ts: -------------------------------------------------------------------------------- 1 | import {PayloadAction, createSlice} from '@reduxjs/toolkit' 2 | import RouteKey from '../../navigation/RouteKey' 3 | import {IApp} from '../types/app' 4 | import Config, {CODEPUSH_KEYS} from '../../constants/configs' 5 | 6 | export const appInitialState: IApp = { 7 | showGlobalIndicator: false, 8 | appState: RouteKey.SplashScreen, 9 | showSearchBar: false, 10 | codePushKey: CODEPUSH_KEYS[0]?.dev, 11 | apiUrl: Config.API_URL, 12 | } 13 | 14 | const appSlice = createSlice({ 15 | name: 'app', 16 | initialState: appInitialState, 17 | reducers: { 18 | getSettings: () => { 19 | // TODO: add action when user get settings 20 | }, 21 | setAppStack: (state, action: PayloadAction): void => { 22 | state.appState = action.payload 23 | }, 24 | setShowGlobalIndicator: (state, action: PayloadAction): void => { 25 | state.showGlobalIndicator = action.payload 26 | }, 27 | setCodePushKey: (state, action: PayloadAction) => { 28 | state.codePushKey = action.payload 29 | }, 30 | setApiUrl: (state, action: PayloadAction) => { 31 | state.apiUrl = action.payload 32 | }, 33 | }, 34 | }) 35 | 36 | export const appActions = appSlice.actions 37 | 38 | export default appSlice.reducer 39 | -------------------------------------------------------------------------------- /template/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {LogBox, Text} from 'react-native' 3 | import {SafeAreaProvider} from 'react-native-safe-area-context' 4 | import {Provider} from 'react-redux' 5 | import {configureLocalization} from './src/locale/I18nConfig' 6 | import MainLayout from './src/MainLayout' 7 | import {injectStore} from './src/services/networking/axios' 8 | import {store, persistor} from './src/store/store' 9 | import {PersistGate} from 'redux-persist/integration/react' 10 | 11 | interface TextWithDefaultProps extends Text { 12 | defaultProps?: { 13 | allowFontScaling?: boolean 14 | underlineColorAndroid?: 'transparent' 15 | } 16 | } 17 | /* eslint-disable @typescript-eslint/no-extra-semi */ 18 | ;(Text as unknown as TextWithDefaultProps).defaultProps = { 19 | ...(Text as unknown as TextWithDefaultProps).defaultProps, 20 | allowFontScaling: false, 21 | underlineColorAndroid: 'transparent', 22 | } 23 | LogBox.ignoreAllLogs(true) 24 | 25 | injectStore(store) 26 | configureLocalization('en') 27 | 28 | function App(): React.JSX.Element { 29 | return ( 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ) 38 | } 39 | 40 | export default App 41 | -------------------------------------------------------------------------------- /template/scripts/run-app.sh: -------------------------------------------------------------------------------- 1 | prompt_android="Please select mode for android: " 2 | OPTIONS_ANDROID=( 3 | "developmentDebug" 4 | "developmentRelease" 5 | "stagingDebug" 6 | "stagingRelease" 7 | "productionDebug" 8 | "productionRelease" 9 | "quit" 10 | ) 11 | 12 | if [ "$1" == "android" ] 13 | then 14 | PS3="$prompt_android" 15 | select opt in "${OPTIONS_ANDROID[@]}"; do 16 | if [ "$opt" == "quit" ] 17 | then 18 | break 19 | fi 20 | if [ "$opt" == "" ] 21 | then 22 | echo "Invalid" 23 | else 24 | echo "Mode: $opt" 25 | npx react-native run-android --mode $opt 26 | break 27 | fi 28 | done 29 | fi 30 | 31 | prompt_ios="Please select variant for ios: " 32 | OPTIONS_IOS=( 33 | "RNBaseProjectTypeScriptDev" 34 | "RNBaseProjectTypeScriptStg" 35 | "RNBaseProjectTypeScript" 36 | "quit" 37 | ) 38 | 39 | if [ "$1" == "ios" ] 40 | then 41 | PS3="$prompt_ios" 42 | select opt in "${OPTIONS_IOS[@]}"; do 43 | if [ "$opt" == "quit" ] 44 | then 45 | break 46 | fi 47 | if [ "$opt" == "" ] 48 | then 49 | echo "Invalid" 50 | else 51 | echo "Scheme: $opt" 52 | npx react-native run-ios --scheme "$opt" 53 | break 54 | fi 55 | done 56 | fi 57 | -------------------------------------------------------------------------------- /template/ios/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategorySystemBootTime 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | 35F9.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategoryUserDefaults 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | CA92.1 29 | 30 | 31 | 32 | NSPrivacyAccessedAPIType 33 | NSPrivacyAccessedAPICategoryDiskSpace 34 | NSPrivacyAccessedAPITypeReasons 35 | 36 | 85F4.1 37 | 38 | 39 | 40 | NSPrivacyCollectedDataTypes 41 | 42 | NSPrivacyTracking 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryFileTimestamp 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | C617.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategorySystemBootTime 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | 35F9.1 21 | 22 | 23 | 24 | NSPrivacyAccessedAPIType 25 | NSPrivacyAccessedAPICategoryUserDefaults 26 | NSPrivacyAccessedAPITypeReasons 27 | 28 | CA92.1 29 | 30 | 31 | 32 | NSPrivacyAccessedAPIType 33 | NSPrivacyAccessedAPICategoryDiskSpace 34 | NSPrivacyAccessedAPITypeReasons 35 | 36 | 85F4.1 37 | 38 | 39 | 40 | NSPrivacyCollectedDataTypes 41 | 42 | NSPrivacyTracking 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /template/src/navigation/CustomTabBar.tsx: -------------------------------------------------------------------------------- 1 | import {BottomTabBarProps} from '@react-navigation/bottom-tabs' 2 | import React from 'react' 3 | import {StyleSheet, Text, TouchableOpacity, View} from 'react-native' 4 | import {SafeAreaView} from 'react-native-safe-area-context' 5 | import {IItemTabBar} from './types' 6 | 7 | const CustomTabBar: React.FC = props => { 8 | const {navigation} = props 9 | function renderItem(item: IItemTabBar) { 10 | const {route, title} = item 11 | return ( 12 | { 17 | if (route) { 18 | navigation.navigate(route) 19 | } 20 | }}> 21 | {title} 22 | 23 | ) 24 | } 25 | 26 | return ( 27 | 28 | {[].map(renderItem)} 29 | 30 | ) 31 | } 32 | 33 | export default CustomTabBar 34 | 35 | const styles = StyleSheet.create({ 36 | container: { 37 | maxHeight: 80, 38 | }, 39 | wrapper: { 40 | flexDirection: 'row', 41 | width: '100%', 42 | }, 43 | itemContainer: { 44 | flex: 1, 45 | alignItems: 'center', 46 | justifyContent: 'center', 47 | height: '100%', 48 | }, 49 | title: {}, 50 | }) 51 | -------------------------------------------------------------------------------- /template/src/store/saga/user.ts: -------------------------------------------------------------------------------- 1 | import {AnyAction} from 'redux' 2 | import {delay, put, takeLatest} from 'redux-saga/effects' 3 | import {showToast} from '../../components' 4 | import RouteKey from '../../navigation/RouteKey' 5 | import {appActions, userActions} from '../reducers' 6 | 7 | function* userLoginSaga(): IterableIterator { 8 | try { 9 | yield put(appActions.setShowGlobalIndicator(true)) 10 | // TODO: login login 11 | yield delay(1000) 12 | yield put(appActions.setAppStack(RouteKey.HomeStack)) 13 | } catch (e) { 14 | if (e instanceof Error) { 15 | showToast({type: 'ERROR', message: e.message}) 16 | } 17 | yield put(appActions.setAppStack(RouteKey.AuthStack)) 18 | } finally { 19 | yield put(appActions.setShowGlobalIndicator(false)) 20 | } 21 | } 22 | 23 | function* userSignUpSaga(): IterableIterator { 24 | try { 25 | yield put(appActions.setShowGlobalIndicator(true)) 26 | } catch (e) { 27 | if (e instanceof Error) { 28 | showToast({type: 'ERROR', message: e.message}) 29 | } 30 | } finally { 31 | yield put(appActions.setShowGlobalIndicator(false)) 32 | } 33 | } 34 | 35 | function* userLogout() { 36 | // TODO: add logout function 37 | } 38 | 39 | export default [ 40 | takeLatest(userActions.userLogin.type, userLoginSaga), 41 | takeLatest(userActions.userSignUp.type, userSignUpSaga), 42 | takeLatest(userActions.logout.type, userLogout), 43 | ] 44 | -------------------------------------------------------------------------------- /template/src/utilities/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-useless-escape */ 2 | import {Appearance, Dimensions, Platform, StatusBar} from 'react-native' 3 | import {isIOS} from '../themes' 4 | 5 | export function getStatusBarHeight(skipAndroid = false): number { 6 | if (isIOS) { 7 | return isIphoneX() ? 65 : 30 8 | } 9 | if (skipAndroid) { 10 | return 0 11 | } 12 | 13 | return StatusBar.currentHeight || 0 14 | } 15 | 16 | export function isIphoneX(): boolean { 17 | const {width, height} = Dimensions.get('window') 18 | return ( 19 | Platform.OS === 'ios' && 20 | !Platform.isPad && 21 | (height === 780 || 22 | width === 780 || 23 | height === 812 || 24 | width === 812 || 25 | height === 844 || 26 | width === 844 || 27 | height === 852 || 28 | width === 852 || 29 | height === 896 || 30 | width === 896 || 31 | height === 926 || 32 | width === 926) 33 | ) 34 | } 35 | 36 | export function validateEmail(email: string): boolean { 37 | const re = 38 | /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 39 | return re.test(email) 40 | } 41 | 42 | export function isDarkMode(): boolean { 43 | return Appearance.getColorScheme() === 'dark' 44 | } 45 | 46 | export function isObject(val: T): boolean { 47 | return typeof val === 'object' && val?.constructor !== FormData && val !== null 48 | } 49 | -------------------------------------------------------------------------------- /.github/workflows/eslint.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # ESLint is a tool for identifying and reporting on patterns 6 | # found in ECMAScript/JavaScript code. 7 | # More details at https://github.com/eslint/eslint 8 | # and https://eslint.org 9 | 10 | name: ESLint 11 | 12 | on: 13 | push: 14 | branches: ["develop", master] 15 | pull_request: 16 | # The branches below must be a subset of the branches above 17 | branches: ["develop"] 18 | schedule: 19 | - cron: "24 23 * * 4" 20 | 21 | jobs: 22 | eslint: 23 | name: Run eslint scanning 24 | runs-on: ubuntu-latest 25 | permissions: 26 | contents: read 27 | security-events: write 28 | actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status 29 | steps: 30 | - name: Checkout code 31 | uses: actions/checkout@v3 32 | 33 | - name: Install ESLint 34 | run: | 35 | npm install eslint@8.10.0 36 | npm install @microsoft/eslint-formatter-sarif@2.1.7 37 | 38 | - name: Run ESLint 39 | run: npx eslint . 40 | --config .eslintrc.js 41 | --ext .js,.jsx,.ts,.tsx 42 | --format @microsoft/eslint-formatter-sarif 43 | --output-file eslint-results.sarif 44 | continue-on-error: true 45 | -------------------------------------------------------------------------------- /template/fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ---- 3 | 4 | # Installation 5 | 6 | Make sure you have the latest version of the Xcode command line tools installed: 7 | 8 | ```sh 9 | xcode-select --install 10 | ``` 11 | 12 | For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) 13 | 14 | # Available Actions 15 | 16 | ## iOS 17 | 18 | ### ios upload 19 | 20 | ```sh 21 | [bundle exec] fastlane ios upload 22 | ``` 23 | 24 | Build and upload to TestFlight 25 | 26 | ### ios codepush 27 | 28 | ```sh 29 | [bundle exec] fastlane ios codepush 30 | ``` 31 | 32 | ====CODE PUSH==== 33 | 34 | ### ios build 35 | 36 | ```sh 37 | [bundle exec] fastlane ios build 38 | ``` 39 | 40 | Build or codepush 41 | 42 | ---- 43 | 44 | 45 | ## Android 46 | 47 | ### android upload 48 | 49 | ```sh 50 | [bundle exec] fastlane android upload 51 | ``` 52 | 53 | Build file apk and upload to appcenter 54 | 55 | ### android codepush 56 | 57 | ```sh 58 | [bundle exec] fastlane android codepush 59 | ``` 60 | 61 | ====CODE PUSH==== 62 | 63 | ### android build 64 | 65 | ```sh 66 | [bundle exec] fastlane android build 67 | ``` 68 | 69 | Build or codepush 70 | 71 | ---- 72 | 73 | This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. 74 | 75 | More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). 76 | 77 | The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 78 | -------------------------------------------------------------------------------- /template/src/constants/configs.tsx: -------------------------------------------------------------------------------- 1 | import {Platform} from 'react-native' 2 | import RNConfig from 'react-native-config' 3 | import packageJSON from '../../package.json' 4 | import {getBundleId} from 'react-native-device-info' 5 | 6 | const AppEnv = { 7 | DEV: 'development', 8 | STAGING: 'staging', 9 | PRODUCTION: 'production', 10 | } 11 | 12 | const configs = { 13 | appBundleID: getBundleId(), 14 | appVersion: packageJSON.version, 15 | APP_ENV: RNConfig.APP_ENV || 'development', 16 | DEBUG_ENABLED: RNConfig.APP_ENV !== AppEnv.PRODUCTION, 17 | API_URL: RNConfig.API_URL, 18 | buildEvn: RNConfig.APP_ENV, 19 | codePushKey: Platform.select({ 20 | ios: RNConfig.CODEPUSH_KEY_IOS, 21 | android: RNConfig.CODEPUSH_KEY_ANDROID, 22 | }), 23 | } 24 | 25 | export const BOTTOM_SHEET_TYPE = { 26 | env: '0', 27 | codePush: '1', 28 | } 29 | 30 | export const EXTRA_QA_ENVS = 31 | configs.APP_ENV === AppEnv.DEV ? ['https://qa1.com/api/', 'https://qa2.com/api/'] : [] 32 | 33 | export const CODEPUSH_KEYS = 34 | configs.APP_ENV === AppEnv.DEV 35 | ? [ 36 | { 37 | dev: 'Dev', 38 | key: configs.codePushKey, 39 | }, 40 | 41 | { 42 | dev: 'Thinh', 43 | key: Platform.select({ 44 | android: '', 45 | ios: '', 46 | }), 47 | }, 48 | ] 49 | : [ 50 | { 51 | dev: '', 52 | key: '', 53 | }, 54 | 55 | { 56 | dev: '', 57 | key: Platform.select({ 58 | android: '', 59 | ios: '', 60 | }), 61 | }, 62 | ] 63 | 64 | export default configs 65 | -------------------------------------------------------------------------------- /template/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 | linkage = ENV['USE_FRAMEWORKS'] 12 | if linkage != nil 13 | Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green 14 | use_frameworks! :linkage => linkage.to_sym 15 | end 16 | 17 | target 'RNBaseProjectTypeScript' do 18 | config = use_native_modules! 19 | 20 | use_react_native!( 21 | :path => config[:reactNativePath], 22 | # An absolute path to your application root. 23 | :app_path => "#{Pod::Config.instance.installation_root}/.." 24 | ) 25 | 26 | target 'RNBaseProjectTypeScriptTests' do 27 | inherit! :complete 28 | # Pods for testing 29 | end 30 | 31 | post_install do |installer| 32 | # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 33 | react_native_post_install( 34 | installer, 35 | config[:reactNativePath], 36 | :mac_catalyst_enabled => false, 37 | # :ccache_enabled => true 38 | ) 39 | installer.pods_project.targets.each do |target| 40 | target.build_configurations.each do |config| 41 | # NOTE: Change IPHONEOS_DEPLOYMENT_TARGET to 12.4 42 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.4' 43 | # NOTE: Add `arm64` to EXCLUDED_ARCHS in Pods 44 | config.build_settings["EXCLUDED_ARCHS[sdk=iphonesimulator*]"] = "arm64" 45 | end 46 | end 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/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 | APPL 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | CodePushDeploymentKey 24 | $(CODEPUSH_KEY_IOS) 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | 30 | NSAllowsArbitraryLoads 31 | 32 | NSAllowsLocalNetworking 33 | 34 | 35 | NSLocationWhenInUseUsageDescription 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIRequiredDeviceCapabilities 40 | 41 | arm64 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UIViewControllerBasedStatusBarAppearance 50 | 51 | UIAppFonts 52 | 53 | Roboto-Bold.ttf 54 | Roboto-Regular.ttf 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /template/src/utilities/Emitter.ts: -------------------------------------------------------------------------------- 1 | import {EmitterListener} from './types' 2 | 3 | class EventRegister { 4 | static _Listeners: EmitterListener = { 5 | count: 0, 6 | // eslint-disable-next-line @typescript-eslint/no-empty-function 7 | refs: {key: {eventName: '', callback: () => {}}}, 8 | } 9 | 10 | static addEventListener(eventName: string, callback: (param?: any) => void): string { 11 | EventRegister._Listeners.count += 1 12 | const eventId = `l${EventRegister._Listeners.count}` 13 | EventRegister._Listeners.refs[eventId] = { 14 | eventName: eventName, 15 | callback, 16 | } 17 | return eventId 18 | } 19 | 20 | static removeEventListener(id: string): boolean { 21 | return delete EventRegister._Listeners.refs[id] 22 | } 23 | 24 | static removeAllListeners(): boolean { 25 | let removeError = false 26 | Object.keys(EventRegister._Listeners.refs).forEach(_id => { 27 | const removed = delete EventRegister._Listeners.refs[_id] 28 | removeError = !removeError ? !removed : removeError 29 | }) 30 | return !removeError 31 | } 32 | 33 | static emitEvent(eventName: string, param?: any): void { 34 | Object.keys(EventRegister._Listeners.refs).forEach(_id => { 35 | if (EventRegister._Listeners.refs[_id] && eventName === EventRegister._Listeners.refs[_id].eventName) { 36 | EventRegister._Listeners.refs[_id].callback(param) 37 | } 38 | }) 39 | } 40 | 41 | /* 42 | * Shorten 43 | */ 44 | static on(eventName: string, callback: (param?: any) => void): string { 45 | return EventRegister.addEventListener(eventName, callback) 46 | } 47 | 48 | static rm(eventName: string): boolean { 49 | return EventRegister.removeEventListener(eventName) 50 | } 51 | 52 | static rmAll(): boolean { 53 | return EventRegister.removeAllListeners() 54 | } 55 | 56 | static emit(eventName: string, param?: any): void { 57 | EventRegister.emitEvent(eventName, param) 58 | } 59 | } 60 | 61 | export default EventRegister 62 | -------------------------------------------------------------------------------- /template/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 | # Use this property to specify which architecture you want to build. 28 | # You can also override it from the CLI using 29 | # ./gradlew -PreactNativeArchitectures=x86_64 30 | reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 31 | 32 | # Use this property to enable support to the new architecture. 33 | # This will allow you to use TurboModules and the Fabric render in 34 | # your application. You should enable this flag either if you want 35 | # to write custom TurboModules/Fabric components OR use libraries that 36 | # are providing them. 37 | newArchEnabled=false 38 | 39 | # Use this property to enable or disable the Hermes JS engine. 40 | # If set to false, you will be using JSC instead. 41 | hermesEnabled=true 42 | 43 | VERSION_NAME=1.0.0 44 | -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/saigontechnology/rnbaseprojecttypescript/MainApplication.kt: -------------------------------------------------------------------------------- 1 | package com.saigontechnology.rnbaseprojecttypescript; 2 | 3 | import android.app.Application 4 | import com.facebook.react.PackageList 5 | import com.facebook.react.ReactApplication 6 | import com.facebook.react.ReactHost 7 | import com.facebook.react.ReactNativeHost 8 | import com.facebook.react.ReactPackage 9 | import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load 10 | import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost 11 | import com.facebook.react.defaults.DefaultReactNativeHost 12 | import com.facebook.soloader.SoLoader 13 | import com.microsoft.codepush.react.CodePush 14 | 15 | class MainApplication : Application(), ReactApplication { 16 | 17 | override val reactNativeHost: ReactNativeHost = 18 | object : DefaultReactNativeHost(this) { 19 | override fun getPackages(): List = 20 | PackageList(this).packages.apply { 21 | // Packages that cannot be autolinked yet can be added manually here, for example: 22 | // add(MyReactNativePackage()) 23 | } 24 | 25 | override fun getJSMainModuleName(): String = "index" 26 | 27 | override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG 28 | 29 | override fun getJSBundleFile(): String { 30 | return CodePush.getJSBundleFile() 31 | } 32 | 33 | override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED 34 | override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED 35 | } 36 | 37 | override val reactHost: ReactHost 38 | get() = getDefaultReactHost(applicationContext, reactNativeHost) 39 | 40 | override fun onCreate() { 41 | super.onCreate() 42 | SoLoader.init(this, false) 43 | if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { 44 | // If you opted-in for the New Architecture, we load the native entry point for this app. 45 | load() 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/networking.md: -------------------------------------------------------------------------------- 1 | ## Networking 2 | Requests can be made by passing the relevant config to axios. 3 | 4 | Add API_URL to the environment variable in the `.env` file. 5 | ```text 6 | API_URL = MY_API_URL 7 | ``` 8 | 9 | These are the basic configuration options for making requests. 10 | ```text 11 | const instance = axios.create({ 12 | baseURL: Config.API_URL, 13 | timeout: AXIOS_TIMEOUT, 14 | withCredentials: false, 15 | responseType: 'json', 16 | headers: { 17 | Accept: 'application/json', 18 | 'Content-Type': 'application/json; charset=utf-8', 19 | }, 20 | data: {}, 21 | }) 22 | ``` 23 | 24 | Important: If axios is used with multiple domains, the `AUTH_TOKEN` will be sent to all of them. 25 | ```text 26 | setToken(token, TokenType.Bearer) 27 | ``` 28 | 29 | ## RefreshToken 30 | Add refreshToken api to services/api/api 31 | ```text 32 | export const AUTH_API = { 33 | // ADD ENDPOINT REFRESH TOKEN HERE 34 | refreshToken: 'api/refreshToken', 35 | } 36 | ``` 37 | 38 | ## Axios API 39 | ##### getRequest<`T`>({url: api_url, config}) 40 | ```text 41 | getRequest({url: API_URL, config}) 42 | ``` 43 | ##### postRequest<`T`>({url: api_url, body, config}) 44 | ```text 45 | postRequest({url: API_URL, body, config}) 46 | ``` 47 | ##### putRequest<`T`>({url: api_url, body, config}) 48 | ```text 49 | putRequest({url: API_URL, body, config}) 50 | ``` 51 | ##### deleteRequest({url: api_url, config}) 52 | ```text 53 | deleteRequest({url: API_URL, config}) 54 | ``` 55 | 56 | ## Interceptors 57 | You can intercept requests or responses before they are handled by `then` or `catch`. 58 | ```text 59 | instance.interceptors.request.use( 60 | config => { 61 | // Do something before request is sent 62 | return config 63 | }, 64 | error => Promise.reject(error), 65 | ) 66 | instance.interceptors.response.use( 67 | response => { 68 | // Do something with response data 69 | return response 70 | }, 71 | error => Promise.reject(error), 72 | ) 73 | ``` 74 | 75 | ## Version 76 | We downgrade axios to version 0.27.2 to resolve this issue: https://github.com/facebook/react-native/issues/34868 -------------------------------------------------------------------------------- /template/src/MainLayout.tsx: -------------------------------------------------------------------------------- 1 | import React, {useCallback, useEffect, useRef} from 'react' 2 | import {AppState, Linking, StatusBar, StyleSheet} from 'react-native' 3 | import {useSelector} from 'react-redux' 4 | import AppNavigation from './navigation/AppNavigator' 5 | import RouteKey from './navigation/RouteKey' 6 | import {getAppStackState, getLoadingIndicator} from './store/selectors' 7 | import configs from './constants/configs' 8 | import {DebugMenu, IndicatorDialog, Toast} from './components' 9 | import {GestureHandlerRootView} from 'react-native-gesture-handler' 10 | 11 | function MainLayout() { 12 | const appState = useSelector(getAppStackState) 13 | const appStateRef = useRef(AppState.currentState) 14 | const showGlobalIndicator = useSelector(getLoadingIndicator) 15 | 16 | const handleAppState = useCallback(() => { 17 | AppState.addEventListener('change', nextAppState => { 18 | if (appStateRef.current.match(/inactive|background/) && nextAppState === 'active') { 19 | // Update action when appState changes 20 | } 21 | appStateRef.current = nextAppState 22 | }) 23 | }, []) 24 | 25 | const handleDeepLink = useCallback(() => { 26 | Linking.getInitialURL().then(res => { 27 | // This function only work when disable debug mode. 28 | if (res) { 29 | // Do something if app is opened from an url 30 | } 31 | }) 32 | Linking.addEventListener('url', res => { 33 | if (res?.url) { 34 | // Do something if app is opened from an url 35 | } 36 | }) 37 | }, []) 38 | 39 | useEffect(() => { 40 | if (appState === RouteKey.HomeStack) { 41 | handleAppState() 42 | handleDeepLink() 43 | } 44 | }, [appState, handleAppState, handleDeepLink]) 45 | 46 | return ( 47 | 48 | 49 | 50 | {showGlobalIndicator && } 51 | {configs.DEBUG_ENABLED && } 52 | 53 | 54 | ) 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | container: { 59 | flex: 1, 60 | }, 61 | }) 62 | 63 | export default MainLayout 64 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScriptTests/RNBaseProjectTypeScriptTests.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 RNBaseProjectTypeScriptTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation RNBaseProjectTypeScriptTests 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 | -------------------------------------------------------------------------------- /docs/config-redux-persist.md: -------------------------------------------------------------------------------- 1 | # Quickstart 2 | ``` 3 | yarn add redux-persist 4 | // or 5 | npm install redux-persist 6 | ``` 7 | 8 | ## 1.Basic Usage 9 | ## 2.Nested Persists 10 | ## 3.Hot Module Replacement 11 | 12 | ## Basic Usage 13 | ``` 14 | Basic usage involves adding persistReducer and persistStore to your setup. IMPORTANT Every app needs to decide how many levels of state they want to "merge". The default is 1 level. Please read through the state reconciler docs for more information. 15 | 16 | import { PersistGate } from 'redux-persist/integration/react' 17 | 18 | // ... normal setup, create store and persistor, import components etc. 19 | 20 | const App = () => { 21 | return ( 22 | 23 | 24 | 25 | 26 | 27 | ); 28 | }; 29 | ``` 30 | 31 | # API 32 | 33 | ## persistReducer(config, reducer) 34 | ``` 35 | * arguments 36 | * config object 37 | required config: key, storage 38 | notable other config: whitelist, blacklist, version, stateReconciler, debug 39 | * reducer function 40 | any reducer will work, typically this would be the top level reducer returned by combineReducers 41 | * returns an enhanced reducer 42 | ``` 43 | 44 | ## persistStore(store, [config, callback]) 45 | ``` 46 | * arguments 47 | * store redux store The store to be persisted. 48 | * config object (typically null) 49 | * If you want to avoid that the persistence starts immediately after calling persistStore, set the option manualPersist. Example: { manualPersist: true } Persistence can then be started at any point with peristor.persist(). You usually want to do this if your storage is not ready when the persistStore call is made. 50 | * callback function will be called after rehydration is finished. 51 | * returns persistor object 52 | ``` 53 | ## persistor object 54 | ``` 55 | * the persistor object is returned by persistStore with the following methods: 56 | * .purge() 57 | * purges state from disk and returns a promise 58 | * .flush() 59 | * immediately writes all pending state to disk and returns a promise 60 | * .pause() 61 | * pauses persistence 62 | * .persist() 63 | * resumes persistence 64 | ``` 65 | 66 | ### More information check at [redux-persist](https://github.com/rt2zz/redux-persist) -------------------------------------------------------------------------------- /template/azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Starter pipeline 2 | # Start with a minimal pipeline that you can customize to build and deploy your code. 3 | # Add steps that build, run tests, deploy, and more: 4 | # https://aka.ms/yaml 5 | 6 | trigger: 7 | - develop 8 | - staging 9 | - master 10 | 11 | pool: 12 | name: 'Mobile Pipeline' 13 | 14 | stages: 15 | - stage: Codepush 16 | condition: eq(variables['Build.SourceBranchName'], 'develop') 17 | jobs: 18 | - job: Codepush 19 | steps: 20 | - script: yarn install 21 | displayName: Install Dependencies 22 | - script: yarn codepush:qa 23 | displayName: Codepush 24 | - stage: Build_Development 25 | condition: and(eq(variables['Build.SourceBranchName'], 'develop'), contains(variables['Build.SourceVersionMessage'], '[BUILD]')) 26 | jobs: 27 | - job: Build 28 | steps: 29 | - script: yarn install 30 | displayName: Install Dependencies 31 | - script: fastlane android build type:build buildNumber:variables['Build.BuildNumber'] --env development 32 | displayName: Build Android 33 | - script: fastlane ios build type:build ci:true buildNumber:variables['Build.BuildNumber'] --env development 34 | displayName: Build iOS 35 | - stage: Build_Staging 36 | condition: eq(variables['Build.SourceBranchName'], 'staging') 37 | jobs: 38 | - job: Build 39 | steps: 40 | - script: yarn install 41 | displayName: Install Dependencies 42 | - script: fastlane android build type:build buildNumber:variables['Build.BuildNumber'] --env staging 43 | displayName: Build Android 44 | - script: fastlane ios build type:build ci:true buildNumber:variables['Build.BuildNumber'] --env staging 45 | displayName: Build iOS 46 | - stage: Build_Production 47 | condition: eq(variables['Build.SourceBranchName'], 'master') 48 | jobs: 49 | - job: Build 50 | steps: 51 | - script: yarn install 52 | displayName: Install Dependencies 53 | - script: fastlane android build type:build buildNumber:variables['Build.BuildNumber'] --env production 54 | displayName: Build Android 55 | - script: fastlane ios build type:build ci:true buildNumber:variables['Build.BuildNumber'] --env production 56 | displayName: Build iOS 57 | 58 | -------------------------------------------------------------------------------- /template/src/components/InfoMenu.tsx: -------------------------------------------------------------------------------- 1 | import React, {FC, ReactNode} from 'react' 2 | import {View, Text, Switch, TouchableOpacity, StyleSheet, StyleProp, ViewStyle, TextStyle} from 'react-native' 3 | import {colors, metrics} from '../themes' 4 | 5 | interface Props { 6 | title: string 7 | description: string 8 | descriptionStyle?: StyleProp 9 | style?: StyleProp 10 | titleStyle?: StyleProp 11 | action?: React.ReactElement 12 | horizontal?: boolean 13 | children?: ReactNode 14 | } 15 | 16 | interface LinkProps extends Props { 17 | onPress: () => void 18 | linkTitle: string 19 | linkTitleStyle?: StyleProp 20 | } 21 | 22 | interface ToggleProps extends Props { 23 | onValueChange: (value: boolean) => void 24 | value: boolean 25 | disabled?: boolean 26 | } 27 | 28 | const InfoMenu: FC = ({ 29 | title, 30 | description, 31 | descriptionStyle, 32 | style, 33 | titleStyle, 34 | action, 35 | horizontal, 36 | children, 37 | }) => ( 38 | 39 | 40 | {title} 41 | {action} 42 | 43 | {description} 44 | {children} 45 | 46 | ) 47 | 48 | const InfoMenuRow: FC = ({...rest}) => 49 | 50 | const InfoMenuLink: FC = ({linkTitle, onPress, linkTitleStyle, ...rest}) => ( 51 | 55 | {linkTitle} 56 | 57 | } 58 | /> 59 | ) 60 | const InfoMenuToggle: FC = ({value, onValueChange, disabled = false, ...rest}) => ( 61 | } /> 62 | ) 63 | 64 | export {InfoMenu, InfoMenuRow, InfoMenuLink, InfoMenuToggle} 65 | 66 | const styles = StyleSheet.create({ 67 | container: { 68 | borderColor: colors.gray, 69 | paddingBottom: metrics.xs, 70 | justifyContent: 'space-between', 71 | }, 72 | titleWrapper: { 73 | justifyContent: 'space-between', 74 | flexDirection: 'row', 75 | marginBottom: metrics.xxs, 76 | }, 77 | row: { 78 | flexDirection: 'row', 79 | }, 80 | infoMenuText: {color: colors.gray}, 81 | }) 82 | -------------------------------------------------------------------------------- /template/src/themes/metrics.ts: -------------------------------------------------------------------------------- 1 | import {Dimensions, Platform} from 'react-native' 2 | 3 | const DESIGN_WIDTH = 375 4 | const DESIGN_HEIGHT = 812 5 | const {width, height} = Dimensions.get('window') 6 | 7 | function responsiveWidth(value: T) { 8 | return ((width * value) / DESIGN_WIDTH) as T 9 | } 10 | 11 | function responsiveHeight(value: T) { 12 | return ((height * value) / DESIGN_HEIGHT) as T 13 | } 14 | 15 | function responsiveFont(value: T) { 16 | return ((width * value) / DESIGN_WIDTH) as T 17 | } 18 | 19 | function deviceWidth(): number { 20 | return width 21 | } 22 | 23 | function deviceHeight(): number { 24 | return height 25 | } 26 | 27 | const isIOS = Platform.OS === 'ios' 28 | 29 | const shadow = { 30 | shadowColor: '#000', 31 | shadowRadius: 5, 32 | elevation: 5, 33 | shadowOpacity: 0.2, 34 | shadowOffset: {width: 0, height: 3}, 35 | } as const 36 | 37 | const hitSlop = { 38 | top: 10, 39 | bottom: 10, 40 | right: 10, 41 | left: 10, 42 | } as const 43 | 44 | const metrics = { 45 | // Text Size 46 | title: responsiveFont(20), 47 | span: responsiveFont(14), 48 | 49 | // spacing 50 | tiny: responsiveHeight(4), 51 | xxs: responsiveHeight(8), 52 | xs: responsiveHeight(12), 53 | small: responsiveHeight(16), 54 | sMedium: responsiveHeight(18), 55 | medium: responsiveHeight(20), 56 | large: responsiveHeight(24), 57 | xl: responsiveHeight(28), 58 | xxl: responsiveHeight(32), 59 | huge: responsiveHeight(48), 60 | massive: responsiveHeight(64), 61 | 62 | borderRadius: responsiveHeight(5), 63 | borderRadiusLarge: responsiveHeight(10), 64 | borderRadiusHuge: responsiveHeight(20), 65 | // margin 66 | marginTop: responsiveHeight(12), 67 | marginHorizontal: responsiveWidth(24), 68 | marginVertical: responsiveWidth(16), 69 | paddingHorizontal: responsiveWidth(20), 70 | 71 | voucherBorderRadius: responsiveHeight(15), 72 | logoWidth: responsiveWidth(300), 73 | logoHeight: responsiveHeight(70), 74 | icon: responsiveHeight(30), 75 | toast: responsiveHeight(44), 76 | } as const 77 | 78 | const FontSizes = { 79 | small: responsiveFont(12), 80 | span: responsiveFont(14), 81 | body: responsiveFont(16), 82 | large: responsiveFont(18), 83 | title: responsiveFont(20), 84 | } as const 85 | 86 | export { 87 | metrics, 88 | FontSizes, 89 | isIOS, 90 | shadow, 91 | hitSlop, 92 | responsiveFont, 93 | responsiveHeight, 94 | responsiveWidth, 95 | deviceWidth, 96 | deviceHeight, 97 | } 98 | -------------------------------------------------------------------------------- /template/scripts/genimg.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /** 3 | * @flow 4 | */ 5 | const path = require('path') 6 | const fs = require('fs') 7 | const argv = require('yargs-parser')(process.argv.slice(2)) 8 | 9 | // $FlowFixMe 10 | String.prototype.format = function () { 11 | let a = this 12 | for (const k in arguments) { 13 | // $FlowFixMe 14 | a = a.replace(`{${k}}`.toRegex('g'), arguments[k]) 15 | } 16 | return a 17 | } 18 | 19 | // $FlowFixMe 20 | String.prototype.toRegex = function (option = 'i') { 21 | let regexStr = this.replace(/[\.\*\+\?\^\$\{\}\(\)\|\[\]\\]/g, '\\$&') 22 | regexStr = regexStr.replace(/\s/g, '\\s?') 23 | return new RegExp(regexStr, option) 24 | } 25 | 26 | const getFileName = file => { 27 | const fileNameMatch = file.match(/^(.+)\.[^\.]+$/) 28 | return ( 29 | fileNameMatch && 30 | fileNameMatch[1].toLowerCase().replace(/[^a-zA-Z0-9]+(.)/g, (m, chr) => chr.toUpperCase()) 31 | ) 32 | } 33 | 34 | const getFileSubName = file => { 35 | return file 36 | } 37 | 38 | const folder = argv.folder || argv.d || argv._[0] 39 | const match = folder.match(/^(.+\/([^\/]+))\/?$/) 40 | // $FlowFixMe 41 | let output = match && '{0}/{1}.tsx'.format(match[1], match[2]) 42 | output = argv.output || argv.o || output 43 | const outputMatch = output.match(/^(?:(.*)\/)?([^\/]+)$/) 44 | const outputPath = outputMatch[1] || '.' 45 | const author = argv.author || argv.a || 'Robot' 46 | const template = `/** 47 | * @flow 48 | */ 49 | 50 | const Images = { 51 | {0}} 52 | export {Images}\n` 53 | 54 | const processFolder = (folderPath, prefix = '') => { 55 | const files = fs.readdirSync(folderPath) 56 | const strCodes = [] 57 | let subfolderCodes = '' 58 | files.forEach(file => { 59 | const filePath = path.join(folderPath, file) 60 | if (fs.statSync(filePath).isDirectory()) { 61 | const subfolderPath = path.join(folderPath, file) 62 | const subfolderImageKeys = processFolder(subfolderPath, `${prefix}/${file}`) 63 | if (subfolderImageKeys.length > 0) { 64 | const folderName = getFileSubName(file) 65 | subfolderCodes += ` ${folderName}: {\n${subfolderImageKeys.join('\n')}\n},\n` 66 | } 67 | } else { 68 | const fileName = getFileName(file) 69 | if (fileName) { 70 | const fileRequirePath = path.relative(outputPath, filePath) 71 | strCodes.push(` ${fileName}: require('./${fileRequirePath}'),`) 72 | } 73 | } 74 | }) 75 | return strCodes.concat(subfolderCodes) 76 | } 77 | 78 | const imageKeys = processFolder(folder) 79 | const code = imageKeys.join('\n') 80 | fs.writeFileSync(output, template.format(code, author)) 81 | -------------------------------------------------------------------------------- /template/src/screens/SplashScreen.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from 'react' 2 | import {StyleSheet, Text, View} from 'react-native' 3 | import CodePush from 'react-native-code-push' 4 | import * as Progress from 'react-native-progress' 5 | import {ScreenContainer} from '../components' 6 | import configs from '../constants/configs' 7 | import {appActions} from '../store/reducers/app' 8 | import {useDispatch} from 'react-redux' 9 | import {colors, deviceWidth, metrics, responsiveHeight} from '../themes' 10 | 11 | const codePushOptions = { 12 | installMode: CodePush.InstallMode.IMMEDIATE, 13 | deploymentKey: configs.codePushKey, 14 | } 15 | 16 | const STATUS = { 17 | check: 'Checking for update', 18 | download: 'Downloading...', 19 | done: 'Done', 20 | } 21 | 22 | const SplashScreen = () => { 23 | const dispatch = useDispatch() 24 | const [updatePercent, setUpdatePercent] = useState(0) 25 | const [statusText, setStatusText] = useState('') 26 | 27 | useEffect(() => { 28 | CodePush.sync( 29 | codePushOptions, 30 | status => { 31 | switch (status) { 32 | case CodePush.SyncStatus.UP_TO_DATE: 33 | case CodePush.SyncStatus.UNKNOWN_ERROR: 34 | dispatch(appActions.getSettings()) 35 | break 36 | case CodePush.SyncStatus.DOWNLOADING_PACKAGE: 37 | case CodePush.SyncStatus.INSTALLING_UPDATE: 38 | case CodePush.SyncStatus.SYNC_IN_PROGRESS: 39 | setStatusText(STATUS.download) 40 | break 41 | default: 42 | setStatusText(STATUS.check) 43 | } 44 | }, 45 | ({receivedBytes, totalBytes}) => { 46 | const percent = receivedBytes / totalBytes 47 | if (totalBytes > 0) { 48 | setUpdatePercent(percent) 49 | } 50 | if (percent === 1) { 51 | setStatusText(STATUS.done) 52 | } 53 | }, 54 | ).catch(() => { 55 | dispatch(appActions.getSettings()) 56 | }) 57 | }, [dispatch]) 58 | 59 | return ( 60 | 61 | {updatePercent > 0 ? ( 62 | 63 | 64 | {statusText} 65 | 66 | ) : null} 67 | 68 | ) 69 | } 70 | 71 | const styles = StyleSheet.create({ 72 | container: { 73 | alignItems: 'center', 74 | justifyContent: 'center', 75 | }, 76 | progressBar: { 77 | position: 'absolute', 78 | bottom: responsiveHeight(40), 79 | alignSelf: 'center', 80 | alignItems: 'center', 81 | }, 82 | progressText: { 83 | color: colors.primary, 84 | paddingTop: metrics.xxs, 85 | }, 86 | }) 87 | 88 | export default SplashScreen 89 | -------------------------------------------------------------------------------- /template/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (7.0.8.1) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | addressable (2.8.6) 14 | public_suffix (>= 2.0.2, < 6.0) 15 | algoliasearch (1.27.5) 16 | httpclient (~> 2.8, >= 2.8.3) 17 | json (>= 1.5.1) 18 | atomos (0.1.3) 19 | base64 (0.2.0) 20 | claide (1.1.0) 21 | cocoapods (1.14.3) 22 | addressable (~> 2.8) 23 | claide (>= 1.0.2, < 2.0) 24 | cocoapods-core (= 1.14.3) 25 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 26 | cocoapods-downloader (>= 2.1, < 3.0) 27 | cocoapods-plugins (>= 1.0.0, < 2.0) 28 | cocoapods-search (>= 1.0.0, < 2.0) 29 | cocoapods-trunk (>= 1.6.0, < 2.0) 30 | cocoapods-try (>= 1.1.0, < 2.0) 31 | colored2 (~> 3.1) 32 | escape (~> 0.0.4) 33 | fourflusher (>= 2.3.0, < 3.0) 34 | gh_inspector (~> 1.0) 35 | molinillo (~> 0.8.0) 36 | nap (~> 1.0) 37 | ruby-macho (>= 2.3.0, < 3.0) 38 | xcodeproj (>= 1.23.0, < 2.0) 39 | cocoapods-core (1.14.3) 40 | activesupport (>= 5.0, < 8) 41 | addressable (~> 2.8) 42 | algoliasearch (~> 1.0) 43 | concurrent-ruby (~> 1.1) 44 | fuzzy_match (~> 2.0.4) 45 | nap (~> 1.0) 46 | netrc (~> 0.11) 47 | public_suffix (~> 4.0) 48 | typhoeus (~> 1.0) 49 | cocoapods-deintegrate (1.0.5) 50 | cocoapods-downloader (2.1) 51 | cocoapods-plugins (1.0.0) 52 | nap 53 | cocoapods-search (1.0.1) 54 | cocoapods-trunk (1.6.0) 55 | nap (>= 0.8, < 2.0) 56 | netrc (~> 0.11) 57 | cocoapods-try (1.2.0) 58 | colored2 (3.1.2) 59 | concurrent-ruby (1.2.3) 60 | escape (0.0.4) 61 | ethon (0.16.0) 62 | ffi (>= 1.15.0) 63 | ffi (1.16.3) 64 | fourflusher (2.3.1) 65 | fuzzy_match (2.0.4) 66 | gh_inspector (1.1.3) 67 | httpclient (2.8.3) 68 | i18n (1.14.4) 69 | concurrent-ruby (~> 1.0) 70 | json (2.7.2) 71 | minitest (5.22.3) 72 | molinillo (0.8.0) 73 | nanaimo (0.3.0) 74 | nap (1.1.0) 75 | netrc (0.11.0) 76 | nkf (0.2.0) 77 | public_suffix (4.0.7) 78 | rexml (3.2.6) 79 | ruby-macho (2.5.1) 80 | typhoeus (1.4.1) 81 | ethon (>= 0.9.0) 82 | tzinfo (2.0.6) 83 | concurrent-ruby (~> 1.0) 84 | xcodeproj (1.24.0) 85 | CFPropertyList (>= 2.3.3, < 4.0) 86 | atomos (~> 0.1.3) 87 | claide (>= 1.0.2, < 2.0) 88 | colored2 (~> 3.1) 89 | nanaimo (~> 0.3.0) 90 | rexml (~> 3.2.4) 91 | 92 | PLATFORMS 93 | ruby 94 | 95 | DEPENDENCIES 96 | activesupport (>= 6.1.7.5, < 7.1.0) 97 | cocoapods (>= 1.13, < 1.15) 98 | 99 | RUBY VERSION 100 | ruby 2.7.5p203 101 | 102 | BUNDLED WITH 103 | 2.1.4 104 | -------------------------------------------------------------------------------- /template/src/navigation/NavigationService.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | createNavigationContainerRef, 3 | NavigationAction, 4 | NavigationProp, 5 | NavigationState, 6 | StackActions, 7 | } from '@react-navigation/native' 8 | import {AppStackParamList} from './types' 9 | 10 | export const navigationRef = createNavigationContainerRef() 11 | 12 | export const canGoBack = (): boolean => navigationRef.isReady() && navigationRef.canGoBack() 13 | 14 | export const getState = (): NavigationState | null => 15 | navigationRef.isReady() ? navigationRef.getState() : null 16 | 17 | export const getParent = (): NavigationProp | null => 18 | navigationRef.isReady() ? navigationRef.getParent() : null 19 | 20 | export const navigate = ( 21 | name: keyof AppStackParamList, 22 | params?: AppStackParamList[keyof AppStackParamList], 23 | ) => { 24 | if (navigationRef.isReady()) { 25 | navigationRef.navigate(name as string, params as object | undefined) 26 | } 27 | } 28 | 29 | export const pop = (count?: number) => { 30 | if (navigationRef.isReady()) { 31 | navigationRef.dispatch(StackActions.pop(count)) 32 | } 33 | } 34 | 35 | export const popToTop = () => { 36 | if (navigationRef.isReady()) { 37 | navigationRef.dispatch(StackActions.popToTop()) 38 | } 39 | } 40 | 41 | export const push = (name: keyof AppStackParamList, params: AppStackParamList[keyof AppStackParamList]) => { 42 | if (navigationRef.isReady()) { 43 | navigationRef.dispatch(StackActions.push(name as string, params as object | undefined)) 44 | } 45 | } 46 | 47 | export const checkRouteOrigin = () => navigationRef.getRootState().routeNames[0] 48 | 49 | export function navigationPop(numberToPop = 1) { 50 | if (navigationRef.isReady()) { 51 | navigationRef.dispatch(StackActions.pop(numberToPop)) 52 | } 53 | } 54 | 55 | export const replace = ( 56 | name: keyof AppStackParamList, 57 | params: AppStackParamList[keyof AppStackParamList], 58 | ) => { 59 | if (navigationRef.isReady()) { 60 | navigationRef.dispatch(StackActions.replace(name as string, params as object | undefined)) 61 | } 62 | } 63 | 64 | export const reset = (state: NavigationState) => { 65 | if (navigationRef.isReady()) { 66 | navigationRef.reset(state) 67 | } 68 | } 69 | 70 | export const resetTo = (name: string) => { 71 | if (navigationRef.isReady()) { 72 | navigationRef.reset({ 73 | index: 0, 74 | routes: [{name}], 75 | }) 76 | } 77 | } 78 | 79 | export const goBack = () => { 80 | if (canGoBack()) { 81 | navigationRef.goBack() 82 | } 83 | } 84 | 85 | export const setParams = (params: AppStackParamList[keyof AppStackParamList]) => { 86 | if (navigationRef.isReady()) { 87 | navigationRef.setParams(params as object | undefined) 88 | } 89 | } 90 | 91 | export const dispatch = (action: NavigationAction | ((state: NavigationState) => NavigationAction)) => { 92 | if (navigationRef.isReady()) { 93 | navigationRef.dispatch(action) 94 | } 95 | } 96 | 97 | export const getCurrentRouteName = () => 98 | navigationRef.isReady() ? navigationRef?.getCurrentRoute()?.name : '' 99 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RNBaseProjectTypeScript", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "bash ./scripts/run-app.sh android", 7 | "ios": "bash ./scripts/run-app.sh ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint . --ext .js,.jsx,.ts,.tsx", 11 | "lint:fix": "eslint . --fix", 12 | "pretty": "prettier --write \"./**/*.{js,jsx,json}\"", 13 | "prepare": "husky install", 14 | "postinstall": "bash ./scripts/post-install.sh", 15 | "reverse": "adb reverse tcp:8081 tcp:8081", 16 | "plop": "plop", 17 | "codepush": "bash ./scripts/code-push.sh", 18 | "build": "bash ./scripts/build-app.sh", 19 | "generateimages": "node scripts/genimg src/assets/images --output src/themes/images.ts --name Images" 20 | }, 21 | "dependencies": { 22 | "@gorhom/bottom-sheet": "^4.4.7", 23 | "@react-native-async-storage/async-storage": "^1.18.1", 24 | "@react-navigation/bottom-tabs": "^6.5.7", 25 | "@react-navigation/native": "^6.1.17", 26 | "@react-navigation/native-stack": "^6.9.26", 27 | "@react-navigation/stack": "^6.3.20", 28 | "@reduxjs/toolkit": "^2.2.7", 29 | "axios": "^1.6.8", 30 | "i18next": "^22.4.15", 31 | "intl-pluralrules": "^2.0.1", 32 | "react": "18.2.0", 33 | "react-i18next": "^12.3.1", 34 | "react-native": "0.74.5", 35 | "react-native-code-push": "^8.0.2", 36 | "react-native-config": "^1.5.0", 37 | "react-native-device-info": "^11.1.0", 38 | "react-native-gesture-handler": "^2.16.2", 39 | "react-native-progress": "^5.0.0", 40 | "react-native-reanimated": "^3.15.0", 41 | "react-native-safe-area-context": "^4.10.1", 42 | "react-native-screens": "^3.31.1", 43 | "react-native-svg": "^13.9.0", 44 | "react-redux": "^9.1.2", 45 | "redux-persist": "^6.0.0", 46 | "redux-saga": "^1.2.3" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.25.2", 50 | "@babel/preset-env": "^7.20.0", 51 | "@babel/runtime": "^7.20.0", 52 | "@react-native/babel-preset": "0.74.87", 53 | "@react-native/eslint-config": "0.76.1", 54 | "@react-native/metro-config": "0.74.87", 55 | "@react-native/typescript-config": "0.74.87", 56 | "@tsconfig/react-native": "^3.0.5", 57 | "@types/jest": "^29.5.2", 58 | "@types/react": "^18.2.6", 59 | "@types/react-native": "^0.71.8", 60 | "@types/react-test-renderer": "^18.0.0", 61 | "@typescript-eslint/eslint-plugin": "^7.18.0", 62 | "@typescript-eslint/parser": "^7.18.0", 63 | "babel-jest": "^29.6.3", 64 | "eslint": "^8.53.0", 65 | "eslint-plugin-import": "^2.29.1", 66 | "eslint-plugin-jest": "^28.5.0", 67 | "eslint-plugin-react": "^7.32.2", 68 | "eslint-plugin-react-hooks": "^4.6.0", 69 | "eslint-plugin-unused-imports": "^3.2.0", 70 | "husky": "^9.1.4", 71 | "jest": "^29.6.3", 72 | "metro-react-native-babel-preset": "0.77.0", 73 | "plop": "^3.1.2", 74 | "prettier": "^3.3.3", 75 | "react-test-renderer": "18.1.0", 76 | "typescript": "^5.4.5" 77 | }, 78 | "engines": { 79 | "node": ">=18" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /template/src/services/networking/index.ts: -------------------------------------------------------------------------------- 1 | import {AxiosError, AxiosResponse} from 'axios' 2 | import {IAxiosMethod, IAxiosError, IParams} from '../../constants/interface/services/axios' 3 | import {isObject} from '../../utilities/utils' 4 | import instance from './axios' 5 | 6 | const AxiosMethod: IAxiosMethod = { 7 | get: 'GET', 8 | post: 'POST', 9 | put: 'PUT', 10 | delete: 'DELETE', 11 | patch: 'PATCH', 12 | } 13 | 14 | async function axiosAPI( 15 | request: IParams, 16 | ): Promise> { 17 | const {url, method, body, config, params} = request 18 | let data = body 19 | 20 | if (isObject(body)) { 21 | data = JSON.stringify(body) 22 | } 23 | return instance({ 24 | url, 25 | method, 26 | data, 27 | params, 28 | headers: {...config}, 29 | }) 30 | .then((response: AxiosResponse) => ({...response.data, status: response.status})) 31 | .catch((error: AxiosError) => ({ 32 | error: error?.response?.data, 33 | status: error?.response?.status, 34 | })) 35 | } 36 | 37 | export function getRequest( 38 | request: IParams, 39 | ): Promise> { 40 | const {url, config, params} = request 41 | return axiosAPI({url, method: AxiosMethod.get, config, params}) 42 | } 43 | 44 | export function postRequest( 45 | request: IParams, 46 | ): Promise> { 47 | const {url, body, config, params} = request 48 | return axiosAPI({url, method: AxiosMethod.post, body, config, params}) 49 | } 50 | 51 | export function postFormDataRequest( 52 | request: IParams, 53 | ): Promise> | IAxiosError { 54 | const {url, body, config} = request 55 | try { 56 | if (body?.constructor !== FormData) { 57 | throw new Error('Unrecognized FormData part') 58 | } 59 | 60 | const customConfig = { 61 | ...config, 62 | 'Content-Type': 'multipart/form-data', 63 | } 64 | return axiosAPI({url, method: AxiosMethod.post, body, config: customConfig}) 65 | } catch (error: unknown) { 66 | const err = error as AxiosError 67 | return {error: err.response?.data.message || err.message} 68 | } 69 | } 70 | 71 | export function putRequest( 72 | request: IParams, 73 | ): Promise> { 74 | const {url, body, config} = request 75 | return axiosAPI({url, method: AxiosMethod.put, body, config}) 76 | } 77 | 78 | export function patchRequest( 79 | request: IParams, 80 | ): Promise> { 81 | const {url, body, config} = request 82 | return axiosAPI({url, method: AxiosMethod.patch, body, config}) 83 | } 84 | 85 | export function deleteRequest( 86 | request: IParams, 87 | ): Promise> { 88 | const {url, body, config} = request 89 | return axiosAPI({url, method: AxiosMethod.delete, body, config}) 90 | } 91 | -------------------------------------------------------------------------------- /template/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. 1>&2 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 48 | echo. 1>&2 49 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 50 | echo location of your Java installation. 1>&2 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. 1>&2 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 62 | echo. 1>&2 63 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 64 | echo location of your Java installation. 1>&2 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 | -------------------------------------------------------------------------------- /template/README.md: -------------------------------------------------------------------------------- 1 | This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli). 2 | 3 | # Getting Started 4 | 5 | >**Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. 6 | 7 | ## Step 1: Start the Metro Server 8 | 9 | First, you will need to start **Metro**, the JavaScript _bundler_ that ships _with_ React Native. 10 | 11 | To start Metro, run the following command from the _root_ of your React Native project: 12 | 13 | ```bash 14 | # using npm 15 | npm start 16 | 17 | # OR using Yarn 18 | yarn start 19 | ``` 20 | 21 | ## Step 2: Start your Application 22 | 23 | Let Metro Bundler run in its _own_ terminal. Open a _new_ terminal from the _root_ of your React Native project. Run the following command to start your _Android_ or _iOS_ app: 24 | 25 | ### For Android 26 | 27 | ```bash 28 | # using npm 29 | npm run android 30 | 31 | # OR using Yarn 32 | yarn android 33 | ``` 34 | 35 | ### For iOS 36 | 37 | ```bash 38 | # using npm 39 | npm run ios 40 | 41 | # OR using Yarn 42 | yarn ios 43 | ``` 44 | 45 | If everything is set up _correctly_, you should see your new app running in your _Android Emulator_ or _iOS Simulator_ shortly provided you have set up your emulator/simulator correctly. 46 | 47 | This is one way to run your app — you can also run it directly from within Android Studio and Xcode respectively. 48 | 49 | ## Step 3: Modifying your App 50 | 51 | Now that you have successfully run the app, let's modify it. 52 | 53 | 1. Open `App.tsx` in your text editor of choice and edit some lines. 54 | 2. For **Android**: Press the R key twice or select **"Reload"** from the **Developer Menu** (Ctrl + M (on Window and Linux) or Cmd ⌘ + M (on macOS)) to see your changes! 55 | 56 | For **iOS**: Hit Cmd ⌘ + R in your iOS Simulator to reload the app and see your changes! 57 | 58 | ## Congratulations! :tada: 59 | 60 | You've successfully run and modified your React Native App. :partying_face: 61 | 62 | ### Now what? 63 | 64 | - If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). 65 | - If you're curious to learn more about React Native, check out the [Introduction to React Native](https://reactnative.dev/docs/getting-started). 66 | 67 | # Troubleshooting 68 | 69 | If you can't get this to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. 70 | 71 | # Learn More 72 | 73 | To learn more about React Native, take a look at the following resources: 74 | 75 | - [React Native Website](https://reactnative.dev) - learn more about React Native. 76 | - [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. 77 | - [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. 78 | - [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. 79 | - [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. 80 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: pull-request 2 | on: [pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | 8 | steps: 9 | - name: Git Pull Request Details 10 | run: | 11 | echo "Pull Request Creator: ${{ github.event.pull_request.user.login }}" 12 | echo "Pull Request title: ${{ github.event.pull_request.title }}" 13 | echo "Pull Request number: ${{ github.event.pull_request.number }}" 14 | echo "Pull Request url: ${{ github.event.pull_request.html_url }}" 15 | echo "Pull Request body: ${{ github.event.pull_request.body }}" 16 | echo "Assigned labels: " ${{ join(github.event.pull_request.labels.*.name) }} 17 | echo "Assignees: " ${{ join(github.event.pull_request.assignees.*.login) }} 18 | - name: Google Chat Notification 19 | run: | 20 | curl --location --request POST '${{ secrets.GOOGLECHAT }}' \ 21 | --header 'Content-Type: application/json' \ 22 | --data-raw '{ 23 | "cards": [ 24 | { 25 | "header": { 26 | "title": "Pull Request No: #${{ github.event.pull_request.number }}" 27 | }, 28 | "sections": [ 29 | { 30 | "widgets": [ 31 | { 32 | "keyValue": { 33 | "topLabel": "Creator", 34 | "content": "${{ github.event.pull_request.user.login }}" 35 | }, 36 | }, 37 | { 38 | "keyValue": { 39 | "topLabel": "Title", 40 | "content": "${{ github.event.pull_request.title }}" 41 | } 42 | }, 43 | { 44 | "keyValue": { 45 | "topLabel": "Description", 46 | "content": "${{ github.event.pull_request.body }}" 47 | } 48 | }, 49 | { 50 | "buttons": [ 51 | { 52 | "textButton": { 53 | "text": "Open Pull Request", 54 | "onClick": { 55 | "openLink": { 56 | "url": "${{ github.event.pull_request.html_url }}" 57 | } 58 | }, 59 | "color": {"red": 1, "green": 0,"blue": 0,"alpha": 0.5} 60 | } 61 | } 62 | ] 63 | } 64 | ] 65 | } 66 | ] 67 | } 68 | ] 69 | }' 70 | -------------------------------------------------------------------------------- /template/bitbucket-pipelines.yml: -------------------------------------------------------------------------------- 1 | pipelines: 2 | branches: 3 | develop: 4 | - step: 5 | name: "Build" 6 | deployment: Development 7 | runs-on: 8 | - 'self.hosted' 9 | - 'macos' 10 | condition: 11 | changesets: 12 | includePaths: 13 | - "ios/**" 14 | - "android/**" 15 | - "package.json" 16 | - "*.env.*" 17 | script: 18 | - yarn install 19 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env development 20 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env development 21 | - step: 22 | name: "CodePush" 23 | runs-on: 24 | - 'self.hosted' 25 | - 'macos' 26 | deployment: Test 27 | script: 28 | - yarn install 29 | - yarn codepush:qa 30 | staging: 31 | - step: 32 | name: "Build" 33 | deployment: Staging 34 | runs-on: 35 | - 'self.hosted' 36 | - 'macos' 37 | script: 38 | - yarn install 39 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env staging 40 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env staging 41 | production: 42 | - step: 43 | name: "Build" 44 | deployment: Production 45 | runs-on: 46 | - 'self.hosted' 47 | - 'macos' 48 | script: 49 | - yarn install 50 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env production 51 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env production 52 | custom: 53 | buildDev: 54 | - step: 55 | name: "Build - Dev" 56 | runs-on: 57 | - 'self.hosted' 58 | - 'macos' 59 | deployment: Test 60 | script: 61 | - yarn install 62 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env development 63 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env development 64 | codepushDev: 65 | - step: 66 | name: "CodePush - Dev" 67 | runs-on: 68 | - 'self.hosted' 69 | - 'macos' 70 | deployment: Test 71 | script: 72 | - yarn install 73 | - yarn codepush:qa 74 | buildStaging: 75 | - step: 76 | name: "Build - Staging" 77 | runs-on: 78 | - 'self.hosted' 79 | - 'macos' 80 | deployment: Staging 81 | script: 82 | - yarn install 83 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env staging 84 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env staging 85 | buildProduction: 86 | - step: 87 | name: "Build - Production" 88 | runs-on: 89 | - 'self.hosted' 90 | - 'macos' 91 | deployment: Production 92 | script: 93 | - yarn install 94 | - fastlane android build type:build buildNumber:$BITBUCKET_BUILD_NUMBER --env production 95 | - fastlane ios build type:build ci:true buildNumber:$BITBUCKET_BUILD_NUMBER --env production 96 | -------------------------------------------------------------------------------- /template/src/services/networking/axios.ts: -------------------------------------------------------------------------------- 1 | import {AnyAction, Store} from '@reduxjs/toolkit' 2 | import axios, {AxiosError, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig} from 'axios' 3 | import Config from 'react-native-config' 4 | import {AXIOS_TIMEOUT, RESPONSE_CODE, TOKEN, TOKEN_TYPE} from '../../constants' 5 | import {userActions} from '../../store/reducers' 6 | import {RootState} from '../../store/store' 7 | import {getData, setData, clearAllData} from '../../utilities/storage' 8 | import {AUTH_API} from '../api/api' 9 | 10 | let store: Store 11 | 12 | const instance = axios.create({ 13 | baseURL: Config.API_URL, 14 | timeout: AXIOS_TIMEOUT, 15 | withCredentials: false, 16 | responseType: 'json', 17 | headers: { 18 | Accept: 'application/json', 19 | 'Content-Type': 'application/json; charset=utf-8', 20 | }, 21 | data: {}, 22 | }) 23 | 24 | export const injectStore = (_store: Store) => { 25 | store = _store 26 | } 27 | 28 | export function setBaseURL(baseURL: string) { 29 | instance.defaults.baseURL = baseURL 30 | } 31 | 32 | export function setToken(token: string, type: string) { 33 | switch (type) { 34 | case TOKEN_TYPE.Bearer: 35 | instance.defaults.headers.common.Authorization = `Bearer ${token}` 36 | break 37 | default: { 38 | instance.defaults.headers.common.Authorization = token 39 | } 40 | } 41 | } 42 | 43 | const logout = () => { 44 | store.dispatch(userActions.logout()) 45 | } 46 | 47 | const handleRefreshToken = async ( 48 | refreshToken: string, 49 | originalConfig: InternalAxiosRequestConfig, 50 | ): Promise => 51 | // Call RefreshToken API 52 | instance 53 | .post(AUTH_API.refreshToken, refreshToken) 54 | .then((response: AxiosResponse) => { 55 | // Save new Token and RefreshToken 56 | setToken(response?.data?.token, TOKEN_TYPE.Bearer) 57 | setData(TOKEN.token, response?.data?.token) 58 | setData(TOKEN.refreshToken, response?.data?.refreshToken) 59 | return instance(originalConfig) 60 | }) 61 | .catch(() => { 62 | // Remove all keys and back to login screen to get new token 63 | clearAllData() 64 | logout() 65 | }) 66 | 67 | instance.interceptors.request.use( 68 | (config: InternalAxiosRequestConfig) => 69 | // Do something before request is sent 70 | config, 71 | (error: AxiosError) => 72 | // Do something with request error 73 | Promise.reject(error), 74 | ) 75 | 76 | const interceptor = instance.interceptors.response.use( 77 | (response: AxiosResponse) => 78 | // Do something with response data 79 | response, 80 | async (error: AxiosError) => { 81 | const originalConfig = error?.config as InternalAxiosRequestConfig 82 | const token = await getData(TOKEN.token) 83 | const refreshToken = await getData(TOKEN.refreshToken) 84 | const isTokenExpired = token && RESPONSE_CODE.unauthorized.includes(error?.response?.status as number) 85 | 86 | if (isTokenExpired) { 87 | if (refreshToken) { 88 | // Eject the interceptor so it doesn't loop in case 89 | instance.interceptors.response.eject(interceptor) 90 | 91 | // handle refresh token when the token has expired 92 | return handleRefreshToken(refreshToken, originalConfig) 93 | } else { 94 | // Do something when expired token 95 | } 96 | } 97 | 98 | return Promise.reject(error) 99 | }, 100 | ) 101 | 102 | export default instance 103 | -------------------------------------------------------------------------------- /docs/fastlane.md: -------------------------------------------------------------------------------- 1 | # Integrate fastlane 2 | 3 | ``` 4 | fastlane init 5 | ``` 6 | 7 | update Fastfile: 8 | `platform :ios do` 9 | ... 10 | `platform :android do` 11 | ... 12 | 13 | ## IOS 14 | 15 | ### Setup 16 | 17 | Set environment 18 | Enter file ~/.bash_profile 19 | `vi ~/.bash_profile` 20 | Use the export command to add new environment variables 21 | `export APPLE_ID=your_apple_id` 22 | Execute the new ~/.bash_profile by either restarting the terminal window or using: 23 | `source ~/.bash_profile` 24 | (if you are using zsh, then ~/.zshrc or ~/.zprofile) 25 | 26 | 27 | Config Appfile: 28 | `app_identifier("your_bundle_id") # The bundle identifier of your app` 29 | `apple_id("your_apple_id") # Your Apple email address` 30 | `itc_team_id("your_app_store_connect_id") # App Store Connect Team ID` 31 | `team_id("your_team_id") # Developer Portal Team ID` 32 | You can follow this link to get neccessary values: https://docs.fastlane.tools/advanced/Appfile/#appfile 33 | 34 | For more information about the Appfile, see: 35 | https://docs.fastlane.tools/advanced/#appfile 36 | 37 | ### To run fastlane on IOS 38 | 39 | ``` 40 | fastlane ios |lane| 41 | ``` 42 | 43 | ## Android 44 | 45 | Config Appfile: 46 | `json_key_file("") # Path to the json secret file` 47 | Follow https://docs.fastlane.tools/actions/supply/#setup to get one 48 | `package_name("com.saigontechnology.rnbaseprojecttypescript") # e.g. com.krausefx.app` 49 | 50 | ### To run fastlane on android 51 | 52 | ``` 53 | fastlane android |lane| 54 | ``` 55 | 56 | # CodePush - How to use it 57 | 58 | We will use codepush through fastlane. 59 | You can run it directly on ios or android folder, or you can do by script. I will mention this in next section. 60 | Currently, this file is set up to push to appcenter. If you want to push to another provider, then you can create a new action for this. 61 | we will go to file /ios/fastlane/Fastfile and /android/fastlane/Fastfile to change 62 | 63 | - **owner_name**: Owner name on appcenter (You can get it on URL) 64 | - **my_app**: App name that you created on appcenter (You can get it on URL) 65 | - **platform**: ios / android (only choose one). 66 | 67 | For example: You want to codepush local to appcenter. Follow this code 68 | 69 | ``` 70 | fastlane android|ios build type:codepush env:Local 71 | ``` 72 | 73 | # Build App and upload app to store by Fastlane 74 | 75 | ### Following with these steps to build and upload: 76 | 77 | **Step 1**: create file env with environment that you want to build (ex: env.staging) 78 | 79 | **Step2**: add variable to env file (You need to add assential variables) 80 | 81 | - APPCENTER_TOKEN_UPLOAD_APP 82 | - APPCENTER_RELEASE_NOTE 83 | - APPCENTER_DISTRIBUTE_DESTINATIONS 84 | - APPCENTER_APP_NAME 85 | - APPCENTER_APP_DISPLAY_NAME 86 | 87 | You can get more variable through this link (https://github.com/microsoft/fastlane-plugin-appcenter) 88 | 89 | **Step 3**: Run this script 90 | **Run directly:** 91 | For android: `fastlane android build env:[your_environment]` 92 | For ios: `fastlane ios build env:[your_environment]` 93 | **Run by script:** 94 | `yarn build [your_env]` 95 | These scripts were created for some sample environment, you can create a own environment if you want, by following with docs. 96 | 97 | # Codepush - Using script quickly 98 | 99 | If you are too bored to repeat the same work "run fastlane to codepush ios or android", then you can you this way. 100 | You just need run once, and then you can drink a cup of coffee and wait for it to show success. 101 | from /template folder you run: 102 | 103 | ``` 104 | yarn codepush [your_env] 105 | ``` 106 | 107 | The terminal will show "Code Push Successful" if succeed and show "Code Push Failed" if fail. 108 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /template/plopfile.js: -------------------------------------------------------------------------------- 1 | const MODULE_TYPE = { 2 | Screen: 'screens', 3 | Store: 'store', 4 | Component: 'component', 5 | } 6 | 7 | module.exports = function (plop) { 8 | const component = [ 9 | { 10 | type: 'add', 11 | path: 'src/components/{{properCase name}}.tsx', 12 | templateFile: 'generators/component/index.js.hbs', 13 | }, 14 | ] 15 | const screensView = [ 16 | { 17 | type: 'add', 18 | path: 'src/screens/{{properCase name}}Component/{{properCase name}}Screen.tsx', 19 | templateFile: 'generators/module/Module.view.js.hbs', 20 | }, 21 | { 22 | type: 'add', 23 | path: 'src/screens/{{properCase name}}Component/index.ts', 24 | templateFile: 'generators/module/Module.index.js.hbs', 25 | }, 26 | { 27 | type: 'modify', 28 | path: 'src/screens/index.ts', 29 | pattern: /\/\/ Screen Export/gi, 30 | template: "// Screen Export\r\nexport * from './{{properCase name}}Component'", 31 | }, 32 | { 33 | type: 'modify', 34 | path: 'src/navigation/RouteKey.ts', 35 | pattern: /\/\** Screen \*\//g, 36 | template: "/** Screen */\r\n {{properCase name}}Screen = '{{properCase name}}Screen',", 37 | }, 38 | { 39 | type: 'modify', 40 | path: 'src/navigation/types.ts', 41 | pattern: /\/\** Type \*\//g, 42 | template: '/** Type */\r\ntype {{properCase name}}ScreenParams = {}', 43 | }, 44 | { 45 | type: 'modify', 46 | path: 'src/navigation/types.ts', 47 | pattern: /\/\** Params \*\//g, 48 | template: '/** Params */\r\n [RouteKey.{{properCase name}}Screen]: {{properCase name}}ScreenParams', 49 | }, 50 | ] 51 | const store = [ 52 | { 53 | type: 'add', 54 | path: 'src/store/types/{{camelCase name}}.ts', 55 | templateFile: 'generators/redux/interface.js.hbs', 56 | }, 57 | { 58 | type: 'add', 59 | path: 'src/store/constants/{{camelCase name}}.ts', 60 | templateFile: 'generators/redux/constants.js.hbs', 61 | }, 62 | { 63 | type: 'add', 64 | path: 'src/store/reducers/{{camelCase name}}.ts', 65 | templateFile: 'generators/redux/reducer.js.hbs', 66 | }, 67 | { 68 | type: 'add', 69 | path: 'src/store/saga/{{camelCase name}}.ts', 70 | templateFile: 'generators/redux/saga.js.hbs', 71 | }, 72 | { 73 | type: 'modify', 74 | path: 'src/store/reducers/index.ts', 75 | pattern: /\/\/ Reducer Imports/gi, 76 | template: "// Reducer Imports\r\nimport {{camelCase name}} from './{{camelCase name}}'", 77 | }, 78 | { 79 | type: 'modify', 80 | path: 'src/store/reducers/index.ts', 81 | pattern: /\/\/ Reducers/gi, 82 | template: '// Reducers\r\n {{camelCase name}},', 83 | }, 84 | { 85 | type: 'modify', 86 | path: 'src/store/reducers/index.ts', 87 | pattern: /\/\/ Reducer Export/gi, 88 | template: "// Reducer Export\r\nexport * from './{{camelCase name}}'", 89 | }, 90 | { 91 | type: 'modify', 92 | path: 'src/store/saga/index.ts', 93 | pattern: /\/\/ Saga Imports/gi, 94 | template: "// Saga Imports\r\nimport {{camelCase name}}Saga from './{{camelCase name}}'", 95 | }, 96 | { 97 | type: 'modify', 98 | path: 'src/store/saga/index.ts', 99 | pattern: /\/\/ Sagas/gi, 100 | template: '// Sagas\r\n ...{{camelCase name}}Saga,', 101 | }, 102 | { 103 | type: 'add', 104 | path: 'src/store/selectors/{{camelCase name}}.ts', 105 | templateFile: 'generators/redux/selectors.js.hbs', 106 | }, 107 | { 108 | type: 'modify', 109 | path: 'src/store/selectors/index.ts', 110 | pattern: /\/\/ Selector/gi, 111 | template: "// Selector\r\nexport * from './{{camelCase name}}'", 112 | }, 113 | { 114 | type: 'modify', 115 | path: 'src/store/types/store.ts', 116 | pattern: /\/\/ Import Type/gi, 117 | template: "// Import Type\r\nimport {I{{properCase name}} } from './{{camelCase name}}'", 118 | }, 119 | { 120 | type: 'modify', 121 | path: 'src/store/types/store.ts', 122 | pattern: /\/\/ State/gi, 123 | template: '// State\r\n {{camelCase name}}: I{{properCase name}}', 124 | }, 125 | { 126 | type: 'modify', 127 | path: 'src/store/types/index.ts', 128 | pattern: /\/\/ Export Type/gi, 129 | template: "// Export Type\r\nexport * from './{{camelCase name}}'", 130 | }, 131 | ] 132 | plop.setGenerator('module', { 133 | description: 'Generates new module with or without redux connection', 134 | prompts: [ 135 | { 136 | type: 'input', 137 | name: 'name', 138 | message: 'Module name (Casing will be modified)', 139 | }, 140 | { 141 | type: 'list', 142 | name: 'type', 143 | message: 'Choose Module type', 144 | choices: ['screens', 'component', 'store'], 145 | }, 146 | ], 147 | actions(data) { 148 | switch (data.type) { 149 | case MODULE_TYPE.Screen: 150 | return screensView 151 | case MODULE_TYPE.Store: 152 | return store 153 | case MODULE_TYPE.Component: 154 | return component 155 | default: 156 | break 157 | } 158 | }, 159 | }) 160 | } 161 | -------------------------------------------------------------------------------- /template/src/components/Toast.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useRef, useState} from 'react' 2 | import {Animated, Image, LayoutAnimation, StyleSheet, Text, TouchableOpacity, View} from 'react-native' 3 | import {FontSizes, Images, colors, hitSlop, metrics} from '../themes' 4 | import Emitter from '../utilities/Emitter' 5 | import {useSafeAreaInsets} from 'react-native-safe-area-context' 6 | 7 | export const TOAST_TYPE = { 8 | SUCCESS: 'SUCCESS', 9 | ERROR: 'ERROR', 10 | INFO: 'INFO', 11 | WARNING: 'WARNING', 12 | } as const 13 | const TOAST_EVENTS = { 14 | showToastMessage: 'SHOW_TOAST_MESSAGE', 15 | } 16 | const DEFAULT_DELAY = 5000 17 | const DEFAULT_DURATION = 300 18 | 19 | interface IToastState { 20 | message: string 21 | type: keyof typeof TOAST_TYPE 22 | subMessage?: string 23 | option?: { 24 | delay?: number 25 | duration?: number 26 | } 27 | } 28 | 29 | const messageContent = { 30 | [TOAST_TYPE.SUCCESS]: { 31 | color: colors.success, 32 | }, 33 | [TOAST_TYPE.ERROR]: { 34 | color: colors.error, 35 | }, 36 | [TOAST_TYPE.INFO]: { 37 | color: colors.info, 38 | }, 39 | [TOAST_TYPE.WARNING]: { 40 | color: colors.warning, 41 | }, 42 | } as const 43 | 44 | const initState = { 45 | message: '', 46 | type: TOAST_TYPE.INFO, 47 | subMessage: '', 48 | } 49 | 50 | export const Toast: React.FC = () => { 51 | const insets = useSafeAreaInsets() 52 | const [state, setState] = useState(initState) 53 | const HEIGHT = insets.bottom + metrics.toast 54 | const animationRef = useRef() 55 | const animation = useRef(new Animated.Value(0)).current 56 | const offset = animation.interpolate({ 57 | inputRange: [0, 1], 58 | outputRange: [HEIGHT, 0 - metrics.xxs], // padding bottom metrics.xxs 59 | }) 60 | const opacity = animation.interpolate({ 61 | inputRange: [0, 1], 62 | outputRange: [0, 1], 63 | }) 64 | 65 | const displayMessage = (args: IToastState): void => { 66 | animation.setValue(0) 67 | setState(args) 68 | animationRef.current?.stop() 69 | setTimeout(() => { 70 | animationRef.current = Animated.sequence([ 71 | // Fade In 72 | Animated.timing(animation, { 73 | toValue: 1, 74 | duration: args.option?.duration || DEFAULT_DURATION, 75 | useNativeDriver: true, 76 | }), 77 | Animated.delay(args.option?.delay || DEFAULT_DELAY), 78 | // Fade Out 79 | Animated.timing(animation, { 80 | toValue: 0, 81 | duration: args.option?.duration || DEFAULT_DURATION, 82 | useNativeDriver: true, 83 | }), 84 | ]) 85 | animationRef.current.start(() => { 86 | LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 87 | }) 88 | }, 100) 89 | } 90 | 91 | const dismiss = () => { 92 | animationRef.current?.stop() 93 | Animated.timing(animation, { 94 | toValue: 0, 95 | duration: DEFAULT_DURATION, 96 | useNativeDriver: true, 97 | }).start(() => setState(initState)) 98 | } 99 | 100 | useEffect(() => { 101 | Emitter.on(TOAST_EVENTS.showToastMessage, displayMessage) 102 | return () => { 103 | Emitter.rm(TOAST_EVENTS.showToastMessage) 104 | } 105 | }, []) 106 | 107 | return ( 108 | 117 | 124 | 125 | 126 | {state.message} 127 | {!!state.subMessage && {state.subMessage}} 128 | 129 | 130 | 131 | 132 | 133 | 134 | ) 135 | } 136 | 137 | export const showToast = (args: IToastState) => { 138 | Emitter.emit(TOAST_EVENTS.showToastMessage, args) 139 | } 140 | 141 | const styles = StyleSheet.create({ 142 | container: { 143 | zIndex: 9999, 144 | position: 'absolute', 145 | bottom: 0, 146 | left: 0, 147 | right: 0, 148 | paddingHorizontal: metrics.paddingHorizontal, 149 | }, 150 | messageContainer: { 151 | flex: 1, 152 | flexDirection: 'row', 153 | justifyContent: 'center', 154 | alignItems: 'flex-start', 155 | borderRadius: metrics.borderRadius, 156 | paddingHorizontal: metrics.xs, 157 | paddingVertical: metrics.xxs, 158 | }, 159 | textContent: { 160 | flex: 1, 161 | paddingHorizontal: metrics.xxs, 162 | flexDirection: 'column', 163 | alignItems: 'flex-start', 164 | justifyContent: 'center', 165 | }, 166 | titleStyle: { 167 | fontSize: FontSizes.title, 168 | color: colors.white, 169 | }, 170 | textStyle: { 171 | fontSize: FontSizes.body, 172 | color: colors.white, 173 | }, 174 | icon: { 175 | width: metrics.icon, 176 | height: metrics.icon, 177 | tintColor: colors.white, 178 | }, 179 | }) 180 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript.xcodeproj/xcshareddata/xcschemes/RNBaseProjectTypeScriptStg.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 61 | 62 | 64 | 67 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 87 | 88 | 89 | 90 | 96 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /template/ios/RNBaseProjectTypeScript.xcodeproj/xcshareddata/xcschemes/RNBaseProjectTypeScriptDev.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 11 | 14 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 33 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 | 61 | 62 | 64 | 67 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 81 | 87 | 88 | 89 | 90 | 96 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | --------------------------------------------------------------------------------