diff --git a/package.json b/package.json index b07c992a..51431520 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rn-boiler-template", - "version": "1.70.23", + "version": "1.70.24", "description": "Clean and minimalist React Native template for a quick start with TypeScript and components", "scripts": { "test": "exit 0" diff --git a/template/babel.config.js b/template/babel.config.js index 3495badb..0295a588 100644 --- a/template/babel.config.js +++ b/template/babel.config.js @@ -5,5 +5,18 @@ module.exports = { plugins: ['transform-remove-console'], }, }, - plugins: ['react-native-reanimated/plugin'], + plugins: [ + 'react-native-reanimated/plugin', + [ + 'module:react-native-dotenv', + { + envName: 'APP_ENV', + moduleName: '@env', + path: '.base.env', + blocklist: null, + allowlist: null, + safe: true, + }, + ], + ], }; diff --git a/template/package.json b/template/package.json index 5f0f4e3f..e183376a 100644 --- a/template/package.json +++ b/template/package.json @@ -45,6 +45,7 @@ "react-native-blurhash": "^1.1.10", "react-native-bootsplash": "^4.4.0", "react-native-config": "^1.4.11", + "react-native-dotenv": "^3.4.6", "react-native-fast-image": "^8.6.3", "react-native-flipper": "^0.176.0", "react-native-gesture-handler": "^2.8.0", diff --git a/template/scripts/android.js b/template/scripts/android.js index a7040216..95f01ea8 100644 --- a/template/scripts/android.js +++ b/template/scripts/android.js @@ -1,10 +1,11 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const { execSync } = require('child_process'); -const { loadEnvFile } = require('./common'); +const { loadEnvFile, setupEnv } = require('./common'); (async function () { const { argv, platform } = process; + await setupEnv(argv[2]); const envJson = await loadEnvFile(); // uninstall android app with adb const devicesString = execSync('adb devices').toString().trim(); diff --git a/template/scripts/common.js b/template/scripts/common.js index 71875076..de8278c8 100644 --- a/template/scripts/common.js +++ b/template/scripts/common.js @@ -1,8 +1,30 @@ /* eslint-disable @typescript-eslint/no-var-requires */ +const { execSync } = require('child_process'); const fs = require('fs'); const path = require('path'); module.exports = { + setupEnv: envPath => { + return new Promise((resolve, reject) => { + let infoJsEnv = ` +# This file was generated by current env while u start/run application. +# Do not edit this file as changes may cause incorrect behavior and will be lost +# once the code is regenerated. +\n`; + fs.readFile(path.join('./', envPath), 'utf8', (err, data) => { + if (err) { + console.error(err); + reject(err); + } + infoJsEnv += data; + execSync('npm cache verify'); + execSync('npx kill-port 8081'); + fs.writeFileSync(path.join('.base.env'), infoJsEnv, 'utf8'); + console.error('✨✨✨✨✨ SET UP Env done ✨✨✨✨✨'); + resolve(infoJsEnv); + }); + }); + }, loadEnvFile: () => { return new Promise((resolve, reject) => { fs.readFile(path.join('./', process.argv[2]), 'utf8', (err, data) => { @@ -10,13 +32,20 @@ module.exports = { console.error(err); reject(err); } + let envDTS = "declare module '@env' {"; const envJson = data.split('\n').reduce((prev, curr) => { const firstEqualSign = curr.indexOf('='); const key = curr.slice(0, firstEqualSign); const value = curr.slice(firstEqualSign + 1); + if (key.trim().length <= 0 || key.includes('#')) { + return prev; + } prev[key] = value; + envDTS += `\n export const ${key}: string;`; return prev; }, {}); + envDTS += '\n}\n'; + fs.writeFileSync(path.join('declare', 'env.d.ts'), envDTS, 'utf8'); console.log({ envJson }); resolve(envJson); }); diff --git a/template/scripts/ios.js b/template/scripts/ios.js index 110587f0..6a59f3c1 100644 --- a/template/scripts/ios.js +++ b/template/scripts/ios.js @@ -1,13 +1,16 @@ /* eslint-disable @typescript-eslint/no-var-requires */ const { execSync } = require('child_process'); -const { loadEnvFile } = require('./common'); +const { loadEnvFile, setupEnv } = require('./common'); (async function () { - if (process.platform !== 'darwin') { + const { argv, platform } = process; + + if (platform !== 'darwin') { console.log('This script is only for macOS'); return; } + await setupEnv(argv[2]); const envJson = await loadEnvFile(); const simulator = 'iPhone 11'; try { diff --git a/template/scripts/setup.js b/template/scripts/setup.js index 109d14d5..f8d2e086 100644 --- a/template/scripts/setup.js +++ b/template/scripts/setup.js @@ -9,22 +9,26 @@ const { execSync } = require('child_process'); execSync('cp -r src/app/assets/fonts android/app/src/main/assets', { stdio: 'inherit', }); - console.log('Link font Android Done!!✨✨✨✨✨'); + console.log( + ' 🧶🧶🧶🧶🧶 Link font Android done!! 🧶🧶🧶🧶🧶', + ); if (process.platform === 'darwin') { execSync('cd ios && touch tmp.xcconfig'); console.log( - ' 🧐🧐🧐🧐🧐 Starting bundle install!! 🧐🧐🧐🧐🧐', + ' ⌛️⌛️⌛️⌛️⌛️ Starting bundle install!! ⏳⏳⏳⏳⏳', ); execSync('bundle install', { stdio: 'inherit', }); - console.log('bundle install Done!!✨✨✨✨✨'); console.log( - ' 🧐🧐🧐🧐🧐 Starting pod install!! 🧐🧐🧐🧐🧐', + ' 💯💯💯💯💯 Bundle install done!! 💯💯💯💯💯', + ); + console.log( + ' ⌛️⌛️⌛️⌛️⌛️ Starting pod install!! ⏳⏳⏳⏳⏳', ); execSync('bundle exec pod install --project-directory=ios', { stdio: 'inherit', }); - console.log(' ✨✨✨✨✨ Pod done!!! ✨✨✨✨✨'); + console.log(' 🥙🥙🥙🥙🥙 Pod done!!! 🥙🥙🥙🥙🥙'); } })(); diff --git a/template/src/app/config/env.ts b/template/src/app/config/env.ts deleted file mode 100644 index 2217f1d7..00000000 --- a/template/src/app/config/env.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Config from 'react-native-config'; - -export const ENVConfig = { - APP_ENV: Config.APP_ENV, - API_URL: Config.API_URL, - APP_DISPLAY_NAME: Config.APP_DISPLAY_NAME, - APP_PLACEHOLDER_NAME: Config.APP_PLACEHOLDER_NAME, - VERSION_CODE: Config.VERSION_CODE, - VERSION_NAME: Config.VERSION_NAME, - BUNDLE_IDENTIFIER: Config.BUNDLE_IDENTIFIER, - DEFAULT_FALLBACK_LNG_I18n: Config.DEFAULT_FALLBACK_LNG_I18n, -}; diff --git a/template/src/app/library/networking/service.ts b/template/src/app/library/networking/service.ts index c1fffeff..5e4a02e1 100644 --- a/template/src/app/library/networking/service.ts +++ b/template/src/app/library/networking/service.ts @@ -2,8 +2,8 @@ import { StyleSheet } from 'react-native'; import { dispatch, getState } from '@common'; import { RESULT_CODE_PUSH_OUT, TIME_OUT } from '@config/api'; -import { ENVConfig } from '@config/env'; import { ParamsNetwork, ResponseBase } from '@config/type'; +import { API_URL } from '@env'; import { AppState } from '@model/app'; import { appActions } from '@redux-slice'; import Axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; @@ -33,7 +33,7 @@ AxiosInstance.interceptors.response.use( originalRequest._retry = true; refreshTokenRequest = refreshTokenRequest ? refreshTokenRequest - : refreshToken(originalRequest); + : refreshToken(); const newToken = await refreshTokenRequest; refreshTokenRequest = null; if (newToken === null) { @@ -48,10 +48,20 @@ AxiosInstance.interceptors.response.use( ); // refresh token -async function refreshToken(originalRequest: Record) { - return AxiosInstance.get(ApiConstants.REFRESH_TOKEN, originalRequest) - .then((res: AxiosResponse) => res.data) - .catch(() => null); +async function refreshToken(): Promise { + return new Promise(rs => { + AxiosInstance.request({ + method: 'POST', + url: ApiConstants.REFRESH_TOKEN, + _retry: true, + baseURL: API_URL, + data: { + refresh_token: '', + }, + } as AxiosRequestConfig) + .then((res: AxiosResponse) => rs(res.data)) + .catch(() => rs(null)); + }); } // base @@ -61,7 +71,7 @@ function Request>( ) { const { token }: AppState = getState('app'); const defaultConfig: AxiosRequestConfig = { - baseURL: ENVConfig.API_URL, + baseURL: API_URL, timeout: TIME_OUT, headers: { 'Content-Type': 'application/json', diff --git a/template/src/app/library/utils/i18n/i18n.ts b/template/src/app/library/utils/i18n/i18n.ts index b9eb3ab5..58cffc71 100644 --- a/template/src/app/library/utils/i18n/i18n.ts +++ b/template/src/app/library/utils/i18n/i18n.ts @@ -1,6 +1,6 @@ import { initReactI18next } from 'react-i18next'; -import { ENVConfig } from '@config/env'; +import { DEFAULT_FALLBACK_LNG_I18n } from '@env'; import i18n, { LanguageDetectorAsyncModule, Resource } from 'i18next'; import { resources } from './locales'; @@ -9,7 +9,7 @@ const languageDetector: LanguageDetectorAsyncModule = { type: 'languageDetector', async: true, // flags below detection to be async detect: (callback: (lng: string | readonly string[] | undefined) => void) => { - callback(ENVConfig.DEFAULT_FALLBACK_LNG_I18n); + callback(DEFAULT_FALLBACK_LNG_I18n); }, init: () => { console.log('init I18n'); @@ -21,7 +21,7 @@ const languageDetector: LanguageDetectorAsyncModule = { export const initOptionsI18n = (source: Resource) => { return { - fallbackLng: ENVConfig.DEFAULT_FALLBACK_LNG_I18n, + fallbackLng: DEFAULT_FALLBACK_LNG_I18n, resources: source, diff --git a/template/tsconfig.json b/template/tsconfig.json index 771b54ef..2990215a 100644 --- a/template/tsconfig.json +++ b/template/tsconfig.json @@ -66,7 +66,8 @@ } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */, // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ "typeRoots": [ - "declare" + "declare", + "declare/env.d.ts" ] /* List of folders to include type definitions from. */, // "types": [], /* Type declaration files to be included in compilation. */ "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */,