├── LICENSE ├── README.md ├── package.json ├── script.sh ├── template.config.js └── template ├── .buckconfig ├── .editorconfig ├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── .prettierrc.js ├── .svgrrc.js ├── .watchmanconfig ├── README.md ├── __mocks__ ├── jest.moduleNameMapper.js └── jest.setup.js ├── __tests__ ├── App.test.skip ├── __badges__ │ ├── failure.svg │ ├── ongoing.svg │ └── success.svg ├── __core__ │ ├── app-core.test.ts │ ├── events-core.test.ts │ └── user-core.test.ts ├── __utils__ │ ├── date.test.ts │ └── sleep.test.ts └── status.svg ├── android ├── app │ ├── _BUCK │ ├── build.gradle │ ├── build_defs.bzl │ ├── debug.keystore │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── appstarter │ │ │ └── DetoxTest.java │ │ ├── debug │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── appstarter │ │ │ └── ReactNativeFlipper.java │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── fonts │ │ │ ├── NotoSansSC-Bold.otf │ │ │ ├── NotoSansSC-Light.otf │ │ │ ├── NotoSansSC-Medium.otf │ │ │ └── NotoSansSC-Regular.otf │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ └── com │ │ │ └── appstarter │ │ │ ├── MainActivity.java │ │ │ ├── MainApplication.java │ │ │ └── NavigationBar │ │ │ ├── NavigationBarModule.java │ │ │ └── NavigationBarPackage.java │ │ └── res │ │ ├── drawable │ │ ├── app_screen.xml │ │ ├── ic_launcher_background.xml │ │ ├── launch_screen.xml │ │ └── rn_edit_text_material.xml │ │ ├── layout │ │ └── launch_screen.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-ldpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ └── styles.xml │ │ └── xml │ │ └── network_security_config.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── app.json ├── babel.config.js ├── bitbucket-pipelines.yml ├── build.sh ├── detox.config.js ├── e2e ├── config.json ├── environment.js └── login │ └── login.e2e.js ├── index.d.ts ├── index.js ├── ios ├── Bridging-Header.h ├── Podfile ├── Podfile.lock ├── appStarter.xcodeproj │ ├── project.pbxproj │ └── xcshareddata │ │ └── xcschemes │ │ ├── DEV Debug.xcscheme │ │ ├── DEV Prod.xcscheme │ │ ├── PROD Debug.xcscheme │ │ ├── PROD Release.xcscheme │ │ ├── STAGING Debug.xcscheme │ │ ├── STAGING Release.xcscheme │ │ ├── appStarter-tvOS.xcscheme │ │ └── appStarter.xcscheme ├── appStarter.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── appStarter │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Bridge.swift │ ├── Dev-Info.plist │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@1x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-76x76@3x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ ├── Icon-Small-50x50@1x.png │ │ │ ├── Icon-Small-50x50@2x.png │ │ │ └── ItunesArtwork@2x.png │ │ └── Contents.json │ ├── Info.plist │ ├── LaunchScreen.storyboard │ ├── Prod-Info.plist │ ├── Staging-Info.plist │ └── main.m ├── appStarterTests │ ├── Info.plist │ └── appStarterTests.m └── exportOptions.plist ├── jest.config.js ├── metro.config.js ├── package.json ├── react-native.config.js ├── scripts ├── build-unsigned-ipa.sh ├── create-pipeline-badge.sh ├── generate-signed-android-keystore.sh ├── images.js ├── postinstall.sh └── run-detox-ci.sh ├── src ├── App.tsx ├── assets │ ├── fonts │ │ ├── NotoSansSC-Bold.otf │ │ ├── NotoSansSC-Light.otf │ │ ├── NotoSansSC-Medium.otf │ │ └── NotoSansSC-Regular.otf │ ├── icn_activity.svg │ ├── icn_arrow.svg │ ├── icn_back_button.svg │ ├── icn_close.svg │ ├── icn_copy.svg │ ├── icn_email.svg │ ├── icn_eye_closed.svg │ ├── icn_eye_open.svg │ ├── icn_home.svg │ ├── icn_lock.svg │ ├── icn_pen.svg │ ├── icn_profile.svg │ ├── icn_reload.svg │ ├── icn_send.svg │ ├── icn_settings.svg │ ├── icn_tips.svg │ ├── icn_wifi.svg │ ├── img_close_button.png │ ├── img_close_button@2x.png │ ├── img_close_button@3x.png │ ├── img_logo.png │ └── index.tsx ├── components │ ├── AnimatedWrapper.tsx │ ├── AppText.tsx │ ├── AsyncRenderWrapper.tsx │ ├── Button.tsx │ ├── FloatingLabelInput.tsx │ ├── Row.tsx │ └── ScreenLoader.tsx ├── config │ ├── app-config.ts │ └── e2e-config.ts ├── core │ ├── app-core.ts │ ├── core.ts │ ├── events-core.ts │ └── user-core.ts ├── locales │ ├── de.json │ ├── en.json │ └── index.ts ├── modals │ ├── AlertModal.tsx │ ├── ModalContainer.tsx │ └── index.ts ├── navigation │ ├── AuthStack.tsx │ ├── Navigation.tsx │ ├── SettingsStack.tsx │ └── TabBar.tsx ├── screens │ ├── activity │ │ └── Activity.tsx │ ├── home │ │ └── Home.tsx │ ├── settings │ │ └── Settings.tsx │ ├── tips │ │ └── Tips.tsx │ └── welcome │ │ └── Welcome.tsx ├── stores │ ├── app-store.ts │ ├── stores.ts │ └── user-store.ts ├── styles │ ├── colors.ts │ ├── device.ts │ ├── fonts.ts │ └── style-guide.ts ├── types │ ├── modals-types.ts │ ├── navigation-types.ts │ └── store-types.ts └── utils │ ├── animate-colors.ts │ ├── date.ts │ ├── linking.ts │ ├── navigation.ts │ ├── sleep.ts │ └── strings.tsx ├── tsconfig.json └── yarn.lock /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 colorfy GmbH 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": "react-native-colorfy-template", 3 | "description": "Comprehensive template for the React Native apps built at colorfy GmbH", 4 | "version": "1.2.0", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/colorfy-software/react-native-colorfy-template.git" 8 | }, 9 | "keywords": [ 10 | "react-native", 11 | "typescript", 12 | "template", 13 | "boilerplate" 14 | ], 15 | "author": "Charles Mangwa (https://github.com/CharlesMangwa), Kristjan Vool (https://github.com/iremlopsum)", 16 | "bugs": { 17 | "url": "https://github.com/colorfy-software/react-native-colorfy-template/issues" 18 | }, 19 | "license": "MIT" 20 | } 21 | -------------------------------------------------------------------------------- /script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | npm i -g detox-cli 4 | 5 | if [[ $uname =~ Darwin ]]; then 6 | brew install svgcleaner 7 | brew install jpegoptim 8 | brew install optipng 9 | brew install tdewolff/tap/minify 10 | brew tap wix/brew 11 | brew install applesimutils 12 | xcode-select --install 13 | fi 14 | -------------------------------------------------------------------------------- /template.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Placeholder name that will be replaced in package.json, index.json, android/, ios/ for a project name. 3 | placeholderName: "appStarter", 4 | 5 | // Directory with the template which will be copied and processed by React Native CLI. Template directory should have package.json with all dependencies specified, including `react-native`. 6 | templateDir: "./template", 7 | 8 | // Path to script, which will be executed after init 9 | postInitScript: "./script.sh" 10 | }; 11 | -------------------------------------------------------------------------------- /template/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /template/.editorconfig: -------------------------------------------------------------------------------- 1 | # Windows files 2 | [*.bat] 3 | end_of_line = crlf 4 | -------------------------------------------------------------------------------- /template/.gitattributes: -------------------------------------------------------------------------------- 1 | # Windows files should use crlf line endings 2 | # https://help.github.com/articles/dealing-with-line-endings/ 3 | *.bat text eol=crlf 4 | -------------------------------------------------------------------------------- /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 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | *.hprof 33 | 34 | # Visual Studio Code 35 | # 36 | .vscode/ 37 | 38 | # node.js 39 | # 40 | node_modules/ 41 | npm-debug.log 42 | yarn-error.log 43 | 44 | # BUCK 45 | buck-out/ 46 | \.buckd/ 47 | !debug.keystore 48 | 49 | # fastlane 50 | # 51 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 52 | # screenshots whenever they are needed. 53 | # For more information about the recommended setup visit: 54 | # https://docs.fastlane.tools/best-practices/source-control/ 55 | 56 | */fastlane/report.xml 57 | */fastlane/Preview.html 58 | */fastlane/screenshots 59 | 60 | # Bundle artifact 61 | *.jsbundle 62 | 63 | # CocoaPods 64 | /ios/Pods/ 65 | 66 | # Tests 67 | __tests__/*.ok 68 | /ios/build/ 69 | .bbrun.sh 70 | 71 | # SonarQube 72 | __coverage__ 73 | .scannerwork 74 | -------------------------------------------------------------------------------- /template/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | bracketSameLine: true, 4 | semi: false, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | printWidth: 120, 8 | arrowParens: 'avoid', 9 | } 10 | -------------------------------------------------------------------------------- /template/.svgrrc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: Add colors present in the SVG icons that you'd like to update from JavaScript here. 3 | * Keep in mind that if the SVG doesn't mention any colors, `props.fill` will be used as default. 4 | * If need be, any SVG should only use one specific color for fill, another for stroke, etc if needed. 5 | */ 6 | 7 | module.exports = { 8 | replaceAttrValues: { 9 | '#000': '{props.fill}', 10 | '#000': '{props.stroke}', 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /template/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /template/__mocks__/jest.moduleNameMapper.js: -------------------------------------------------------------------------------- 1 | module.exports = 'SvgMock' 2 | module.exports.ReactComponent = 'SvgMock' 3 | -------------------------------------------------------------------------------- /template/__mocks__/jest.setup.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | require('jest-fetch-mock').enableMocks() 3 | jest.mock('react-native-reanimated', () => require('react-native-reanimated/mock')) 4 | jest.mock('react-native-reanimated', () => { 5 | // eslint-disable-next-line @typescript-eslint/no-var-requires 6 | const Reanimated = require('react-native-reanimated/mock') 7 | 8 | // NOTE: The mock for `call` immediately calls the callback which is incorrect 9 | // So we override it with a no-op 10 | // eslint-disable-next-line @typescript-eslint/no-empty-function 11 | Reanimated.default.call = () => {} 12 | 13 | return Reanimated 14 | }) 15 | 16 | // NOTE: Silence the warning: Animated: `useNativeDriver` is not supported because the native animated module is missing 17 | jest.mock('react-native/Libraries/LayoutAnimation/LayoutAnimation', () => ({ 18 | easeInEaseOut: jest.fn(), 19 | })) 20 | 21 | jest.mock('react-native-localize', () => require('react-native-localize/mock.js')) 22 | 23 | jest.mock('react-native-device-info', () => require('react-native-device-info/jest/react-native-device-info-mock.js')) 24 | 25 | jest.mock('@react-native-community/netinfo', () => require('@react-native-community/netinfo/jest/netinfo-mock.js')) 26 | 27 | jest.mock('react-native-permissions', () => require('react-native-permissions/mock')) 28 | 29 | jest.mock('date-fns', () => ({ 30 | __esModule: true, 31 | isEqual: (dateLeft, dateRight) => new Date(dateLeft).getTime() === new Date(dateRight).getTime(), 32 | isSameMonth: (dateLeft, dateRight) => 33 | new Date(dateLeft).getFullYear() === new Date(dateRight).getFullYear() && 34 | new Date(dateLeft).getMonth() === new Date(dateRight).getMonth(), 35 | getTime: date => new Date(date).getTime(), 36 | startOfMonth: date => { 37 | const output = new Date(date) 38 | output.setDate(1) 39 | output.setHours(0, 0, 0, 0) 40 | return output 41 | }, 42 | })) 43 | 44 | jest.mock('@react-native-clipboard/clipboard', () => ({ 45 | setString: jest.fn(), 46 | })) 47 | 48 | jest.mock('react-native-mmkv', () => { 49 | class MMKVMock { 50 | constructor(configuration) { 51 | this.config = configuration 52 | } 53 | 54 | __INTERNAL_MOCK_STORAGE__ = {} 55 | 56 | set = () => async (key, value) => (MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id][key] = value) 57 | 58 | getString = () => async key => MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id][key] ?? null 59 | 60 | contains = () => key => Boolean(MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id][key]) 61 | 62 | delete = () => key => { 63 | if (MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id][key]) { 64 | delete MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id][key] 65 | } 66 | } 67 | 68 | clearAll = () => (MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id] = {}) 69 | 70 | getAllKeys = () => Object.keys(MMKVMock.__INTERNAL_MOCK_STORAGE__[this.id]) 71 | } 72 | 73 | return { MMKV: MMKVMock } 74 | }) 75 | -------------------------------------------------------------------------------- /template/__tests__/App.test.skip: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react-native' 2 | 3 | import App from '../src/App' 4 | 5 | // FIXME: This test is failing on CI since update to RN 0.64.2 6 | // eslint-disable-next-line jest/no-disabled-tests 7 | describe.skip('🌍 App:', () => { 8 | const { toJSON } = render() 9 | 10 | it('renders correctly', () => { 11 | expect(toJSON()).toMatchSnapshot() 12 | 13 | expect.assertions(1) 14 | }) 15 | }) 16 | -------------------------------------------------------------------------------- /template/__tests__/__badges__/failure.svg: -------------------------------------------------------------------------------- 1 | buildbuildfailedfailed -------------------------------------------------------------------------------- /template/__tests__/__badges__/ongoing.svg: -------------------------------------------------------------------------------- 1 | buildbuildongoing...ongoing... -------------------------------------------------------------------------------- /template/__tests__/__badges__/success.svg: -------------------------------------------------------------------------------- 1 | buildbuildsucceededsucceeded -------------------------------------------------------------------------------- /template/__tests__/__core__/app-core.test.ts: -------------------------------------------------------------------------------- 1 | import core from '../../src/core/core' 2 | import appStore, { initialState } from '../../src/stores/app-store' 3 | 4 | describe('📱 Core > app:', () => { 5 | it.each([ 6 | [{ isFirstDisplayOfHome: true }, initialState], 7 | [{ isFirstDisplayOfHome: false }, { ...initialState, isFirstDisplayOfHome: false }], 8 | [ 9 | { pushPermissions: false }, 10 | { 11 | ...initialState, 12 | isFirstDisplayOfHome: false, 13 | pushPermissions: false, 14 | }, 15 | ], 16 | ])('update() does update the store', (updateData, expectedStore) => { 17 | core.app.update(updateData) 18 | const updatedStore = appStore.getState().data 19 | expect(updatedStore).toStrictEqual(expectedStore) 20 | 21 | expect.assertions(1) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /template/__tests__/__core__/events-core.test.ts: -------------------------------------------------------------------------------- 1 | import core from '../../src/core/core' 2 | 3 | describe('📣 Core > events:', () => { 4 | const testChannel = 'jest' 5 | const testEvent = { message: 'hello world!' } 6 | 7 | beforeEach(() => { 8 | core.events.clearAll() 9 | }) 10 | 11 | it('send() does send events', () => { 12 | expect(() => core.events.send(testChannel, testEvent)).not.toThrowError() 13 | }) 14 | 15 | it('listen() does listen to events', () => { 16 | core.events.listen(testChannel, event => { 17 | expect(event).toStrictEqual(testEvent) 18 | }) 19 | core.events.send(testChannel, testEvent) 20 | 21 | expect.assertions(1) 22 | }) 23 | 24 | it('clearAll() clears all events handlers', () => { 25 | core.events.listen(testChannel, event => { 26 | // NOTE: This way we now for sure there is at least one event handler registered. 27 | expect(event).toStrictEqual(testEvent) 28 | }) 29 | core.events.send(testChannel, testEvent) 30 | 31 | const eventHandlerMap = core.events.clearAll() 32 | 33 | expect(eventHandlerMap).toStrictEqual(new Map()) 34 | 35 | expect.assertions(2) 36 | }) 37 | }) 38 | -------------------------------------------------------------------------------- /template/__tests__/__core__/user-core.test.ts: -------------------------------------------------------------------------------- 1 | import type { StoresDataType } from '../../src/types/store-types' 2 | 3 | import core from '../../src/core/core' 4 | import appStore, { initialState as initialAppState } from '../../src/stores/app-store' 5 | import userStore, { initialState as initialUserState } from '../../src/stores/user-store' 6 | 7 | const storesToReset: (keyof StoresDataType)[] = ['app', 'user'] 8 | 9 | describe('🙍 Core > user:', () => { 10 | it.each([ 11 | [{ UID: '42' }, { UID: '42' }], 12 | [{ UID: '43' }, { UID: '43' }], 13 | [{ isActive: true }, { UID: '43', isActive: true }], 14 | [{ UID: undefined }, { UID: undefined, isActive: true }], 15 | [ 16 | { UID: '42', emails: { unverified: ['test@jest.com'] } }, 17 | { UID: '42', emails: { unverified: ['test@jest.com'] }, isActive: true }, 18 | ], 19 | ])('update() does update the store', (updateData, expectedStore) => { 20 | // @ts-expect-error NOTE: We know the argument isn't a complete user object. 21 | core.user.update(updateData) 22 | const updatedStore = userStore.getState().data 23 | expect(updatedStore).toStrictEqual(expectedStore) 24 | }) 25 | 26 | it('logout() resets stores properly', async () => { 27 | // NOTE: We first populate the stores to be sure that they're indeed being reset afterwards 28 | core.user.update({ UID: '42' }) 29 | const userID = userStore.getState().data.UID 30 | 31 | expect(userID).toStrictEqual('42') 32 | 33 | // NOTE: Now we make sure we test all the expected stores were reset 34 | await core.user.logout() 35 | 36 | storesToReset.forEach(store => { 37 | switch (store) { 38 | case 'app': { 39 | const resetAppState = appStore.getState().data 40 | expect(resetAppState).toStrictEqual(initialAppState) 41 | break 42 | } 43 | case 'user': { 44 | const resetUserState = userStore.getState().data 45 | expect(resetUserState).toStrictEqual(initialUserState) 46 | break 47 | } 48 | default: 49 | throw Error(`Some store being reset by core.user.logout() is not being tested by Jest`) 50 | } 51 | }) 52 | 53 | expect.assertions(3) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /template/__tests__/__utils__/date.test.ts: -------------------------------------------------------------------------------- 1 | import { addMinutesToDate } from '../../src/utils/date' 2 | 3 | describe('📆 Utils > date:', () => { 4 | const date = new Date() 5 | const minutesToAdd = 5 6 | const amountOfMsInAMinute = 60_000 7 | 8 | it('addMinutesToDate() throws an error if an invalid Date instance is provided as 1st argument', () => { 9 | expect(() => 10 | // @ts-expect-error NOTE: We know the argument isn't a Date 11 | addMinutesToDate(undefined, minutesToAdd), 12 | ).toThrowError() 13 | // @ts-expect-error NOTE: We know the argument isn't a Date 14 | expect(() => addMinutesToDate('a', minutesToAdd)).toThrowError() 15 | // @ts-expect-error NOTE: We know the argument isn't a Date 16 | expect(() => addMinutesToDate(null, minutesToAdd)).toThrowError() 17 | expect(() => 18 | // @ts-expect-error NOTE: We know the argument isn't a Date 19 | addMinutesToDate(new Date() + 5, minutesToAdd), 20 | ).toThrowError() 21 | 22 | expect.assertions(4) 23 | }) 24 | 25 | it('addMinutesToDate() throws an error if an invalid minutes value is provided as 2nd argument', () => { 26 | // @ts-expect-error NOTE: We know the argument isn't a number 27 | expect(() => addMinutesToDate(date, undefined)).toThrowError() 28 | // @ts-expect-error NOTE: We know the argument isn't a number 29 | expect(() => addMinutesToDate(date, 'a')).toThrowError() 30 | // @ts-expect-error NOTE: We know the argument isn't a number 31 | expect(() => addMinutesToDate(date, null)).toThrowError() 32 | 33 | expect.assertions(3) 34 | }) 35 | 36 | it('addMinutesToDate() adds a given amount of time to a given Date', () => { 37 | const newDate = addMinutesToDate(date, minutesToAdd) 38 | expect((newDate.getTime() - date.getTime()) / amountOfMsInAMinute).toEqual(minutesToAdd) 39 | 40 | expect.assertions(1) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /template/__tests__/__utils__/sleep.test.ts: -------------------------------------------------------------------------------- 1 | import sleep from '../../src/utils/sleep' 2 | 3 | describe('😴 Utils > sleep:', () => { 4 | it('throws an error if an invalid value is provided', async () => { 5 | // @ts-expect-error NOTE: We know the argument isn't a number 6 | await expect(async () => await sleep(undefined)).rejects.toThrowError() 7 | // @ts-expect-error NOTE: We know the argument isn't a number 8 | await expect(async () => await sleep('a')).rejects.toThrowError() 9 | // @ts-expect-error NOTE: We know the argument isn't a number 10 | await expect(async () => await sleep(null)).rejects.toThrowError() 11 | expect.assertions(3) 12 | }) 13 | 14 | it('pauses code execution for a given amount of time', async () => { 15 | const pauseDuration = 2000 16 | const timestampStart = new Date() 17 | 18 | await sleep(pauseDuration) 19 | 20 | const timestampEnd = new Date() 21 | 22 | expect(timestampEnd.getTime() - timestampStart.getTime()).toBeGreaterThanOrEqual(pauseDuration - 10) 23 | 24 | expect.assertions(1) 25 | }) 26 | 27 | it('does not pause code for more than the given amount of time', async () => { 28 | const pauseDuration = 2000 29 | const timestampStart = new Date() 30 | 31 | await sleep(pauseDuration) 32 | 33 | const timestampEnd = new Date() 34 | 35 | expect(timestampEnd.getTime() - timestampStart.getTime()).toBeLessThan(pauseDuration + 1000) 36 | 37 | expect.assertions(1) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /template/__tests__/status.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | build 22 | build 23 | never built 24 | never built 25 | 26 | -------------------------------------------------------------------------------- /template/android/app/_BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.appstarter", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.appstarter", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /template/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /template/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/debug.keystore -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /template/android/app/src/androidTest/java/com/appstarter/DetoxTest.java: -------------------------------------------------------------------------------- 1 | package com.appstarter; 2 | 3 | import com.wix.detox.Detox; 4 | import com.wix.detox.config.DetoxConfig; 5 | 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import androidx.test.ext.junit.runners.AndroidJUnit4; 11 | import androidx.test.filters.LargeTest; 12 | import androidx.test.rule.ActivityTestRule; 13 | 14 | @RunWith(AndroidJUnit4.class) 15 | @LargeTest 16 | public class DetoxTest { 17 | @Rule 18 | // Replace 'MainActivity' with the value of android:name entry in 19 | // in AndroidManifest.xml 20 | public ActivityTestRule mActivityRule = new ActivityTestRule<>(MainActivity.class, false, false); 21 | 22 | @Test 23 | public void runDetoxTests() { 24 | DetoxConfig detoxConfig = new DetoxConfig(); 25 | detoxConfig.idlePolicyConfig.masterTimeoutSec = 90; 26 | detoxConfig.idlePolicyConfig.idleResourceTimeoutSec = 60; 27 | detoxConfig.rnContextLoadTimeoutSec = (com.appstarter.BuildConfig.DEBUG ? 180 : 60); 28 | 29 | Detox.runTests(mActivityRule, detoxConfig); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /template/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /template/android/app/src/debug/java/com/appstarter/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.appstarter; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /template/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 32 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/NotoSansSC-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/assets/fonts/NotoSansSC-Bold.otf -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/NotoSansSC-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/assets/fonts/NotoSansSC-Light.otf -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/NotoSansSC-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/assets/fonts/NotoSansSC-Medium.otf -------------------------------------------------------------------------------- /template/android/app/src/main/assets/fonts/NotoSansSC-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/assets/fonts/NotoSansSC-Regular.otf -------------------------------------------------------------------------------- /template/android/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/appstarter/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.appstarter; 2 | 3 | import com.facebook.react.ReactActivity; 4 | import com.facebook.react.ReactActivityDelegate; 5 | import com.facebook.react.ReactRootView; 6 | import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; 7 | 8 | import org.devio.rn.splashscreen.SplashScreen; 9 | import androidx.appcompat.app.ActionBar; 10 | import android.os.Bundle; 11 | 12 | public class MainActivity extends ReactActivity { 13 | 14 | /** 15 | * Returns the name of the main component registered from JavaScript. This is used to schedule 16 | * rendering of the component. 17 | */ 18 | @Override 19 | protected String getMainComponentName() { 20 | return "appStarter"; 21 | } 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | ActionBar actionBar = getSupportActionBar(); 26 | actionBar.hide(); 27 | SplashScreen.show(this); 28 | super.onCreate(savedInstanceState); 29 | } 30 | 31 | @Override 32 | protected ReactActivityDelegate createReactActivityDelegate() { 33 | return new ReactActivityDelegate(this, getMainComponentName()) { 34 | @Override 35 | protected ReactRootView createRootView() { 36 | return new RNGestureHandlerEnabledRootView(MainActivity.this); 37 | } 38 | }; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/appstarter/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.appstarter; 2 | 3 | import com.appstarter.NavigationBar.NavigationBarPackage; 4 | 5 | import java.util.List; 6 | import java.lang.reflect.InvocationTargetException; 7 | 8 | import android.app.Application; 9 | import android.content.Context; 10 | 11 | import com.facebook.react.PackageList; 12 | import com.facebook.react.ReactApplication; 13 | import com.facebook.react.ReactInstanceManager; 14 | import com.facebook.react.ReactNativeHost; 15 | import com.facebook.react.ReactPackage; 16 | import com.facebook.soloader.SoLoader; 17 | 18 | import com.facebook.react.bridge.JSIModulePackage; 19 | import com.swmansion.reanimated.ReanimatedJSIModulePackage; 20 | 21 | public class MainApplication extends Application implements ReactApplication { 22 | 23 | private final ReactNativeHost mReactNativeHost = 24 | new ReactNativeHost(this) { 25 | @Override 26 | public boolean getUseDeveloperSupport() { 27 | return BuildConfig.DEBUG; 28 | } 29 | 30 | @Override 31 | protected List getPackages() { 32 | @SuppressWarnings("UnnecessaryLocalVariable") 33 | List packages = new PackageList(this).getPackages(); 34 | // Packages that cannot be autolinked yet can be added manually here, for example: 35 | packages.add(new NavigationBarPackage()); 36 | return packages; 37 | } 38 | 39 | @Override 40 | protected String getJSMainModuleName() { 41 | return "index"; 42 | } 43 | 44 | @Override 45 | protected JSIModulePackage getJSIModulePackage() { 46 | return new ReanimatedJSIModulePackage(); 47 | } 48 | }; 49 | 50 | @Override 51 | public ReactNativeHost getReactNativeHost() { 52 | return mReactNativeHost; 53 | } 54 | 55 | @Override 56 | public void onCreate() { 57 | super.onCreate(); 58 | SoLoader.init(this, /* native exopackage */ false); 59 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 60 | } 61 | 62 | /** 63 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 64 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 65 | * 66 | * @param context 67 | * @param reactInstanceManager 68 | */ 69 | private static void initializeFlipper( 70 | Context context, ReactInstanceManager reactInstanceManager) { 71 | if (BuildConfig.DEBUG) { 72 | try { 73 | /* 74 | We use reflection here to pick up the class that initializes Flipper, 75 | since Flipper library is not available in release mode 76 | */ 77 | Class aClass = Class.forName("com.appstarter.ReactNativeFlipper"); 78 | aClass 79 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 80 | .invoke(null, context, reactInstanceManager); 81 | } catch (ClassNotFoundException e) { 82 | e.printStackTrace(); 83 | } catch (NoSuchMethodException e) { 84 | e.printStackTrace(); 85 | } catch (IllegalAccessException e) { 86 | e.printStackTrace(); 87 | } catch (InvocationTargetException e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/appstarter/NavigationBar/NavigationBarModule.java: -------------------------------------------------------------------------------- 1 | package com.appstarter.NavigationBar; 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext; 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 5 | 6 | import java.util.Map; 7 | import java.util.HashMap; 8 | 9 | import android.os.Build; 10 | import android.app.Activity; 11 | import android.view.Display; 12 | import android.graphics.Rect; 13 | import android.view.KeyEvent; 14 | import android.view.WindowManager; 15 | import android.util.DisplayMetrics; 16 | import android.view.KeyCharacterMap; 17 | import android.content.res.Resources; 18 | import android.view.ViewConfiguration; 19 | 20 | 21 | public class NavigationBarModule extends ReactContextBaseJavaModule { 22 | private ReactApplicationContext mContext; 23 | 24 | @Override 25 | public String getName() { 26 | return "NavigationBar"; 27 | } 28 | 29 | public NavigationBarModule(ReactApplicationContext reactContext) { 30 | super(reactContext); 31 | mContext = reactContext; 32 | } 33 | 34 | public Map getConstants() { 35 | Map constants = new HashMap<>(); 36 | 37 | constants.put("defaultHeight", getDefaultHeight()); 38 | 39 | return constants; 40 | } 41 | 42 | // Retrieved from https://stackoverflow.com/a/62301619 43 | private int getDefaultHeight() { 44 | Activity activity = getCurrentActivity(); 45 | 46 | if (activity == null) { 47 | return 0; 48 | } 49 | 50 | Rect screenWindow = new Rect(); 51 | DisplayMetrics displayMetrics = new DisplayMetrics(); 52 | activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(screenWindow); 53 | activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics); 54 | return displayMetrics.heightPixels - (screenWindow.top + screenWindow.height()); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /template/android/app/src/main/java/com/appstarter/NavigationBar/NavigationBarPackage.java: -------------------------------------------------------------------------------- 1 | package com.appstarter.NavigationBar; 2 | 3 | import java.util.List; 4 | import java.util.Arrays; 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.uimanager.ViewManager; 11 | import com.facebook.react.bridge.JavaScriptModule; 12 | import com.facebook.react.bridge.ReactApplicationContext; 13 | 14 | 15 | public class NavigationBarPackage implements ReactPackage { 16 | 17 | @Override 18 | public List createViewManagers(ReactApplicationContext reactContext) { 19 | return Collections.emptyList(); 20 | } 21 | 22 | @Override 23 | public List createNativeModules(ReactApplicationContext reactContext) { 24 | List modules = new ArrayList<>(); 25 | modules.add(new NavigationBarModule(reactContext)); 26 | return modules; 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/drawable/app_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/drawable/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/drawable/rn_edit_text_material.xml: -------------------------------------------------------------------------------- 1 | 2 | 16 | 21 | 22 | 23 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/layout/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /template/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #121212 4 | #121212 5 | #FFFFFF 6 | 7 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 19 | 20 | 23 | 24 | 31 | 32 | -------------------------------------------------------------------------------- /template/android/app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | localhost 5 | 10.0.2.2 6 | 10.0.3.2 7 | 8 | 9 | -------------------------------------------------------------------------------- /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 = "30.0.3" 6 | minSdkVersion = 21 7 | compileSdkVersion = 30 8 | targetSdkVersion = 30 9 | kotlinVersion = "1.4.21" 10 | ndkVersion = "22.0.7026061" 11 | } 12 | repositories { 13 | google() 14 | mavenCentral() 15 | } 16 | dependencies { 17 | classpath("com.android.tools.build:gradle:4.2.2") 18 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 19 | // NOTE: Do not place your application dependencies here; they belong 20 | // in the individual module build.gradle files 21 | } 22 | } 23 | 24 | subprojects { 25 | afterEvaluate {project -> 26 | if (project.hasProperty("android")) { 27 | android { 28 | compileSdkVersion 30 29 | buildToolsVersion '30.0.3' 30 | } 31 | } 32 | } 33 | } 34 | 35 | allprojects { 36 | repositories { 37 | maven { 38 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 39 | url("$rootDir/../node_modules/react-native/android") 40 | } 41 | maven { 42 | // Android JSC is installed from npm 43 | url("$rootDir/../node_modules/jsc-android/dist") 44 | } 45 | mavenCentral { 46 | // We don't want to fetch react-native from Maven Central as there are 47 | // older versions over there. 48 | content { 49 | excludeGroup "com.facebook.react" 50 | } 51 | } 52 | 53 | google() 54 | maven { url 'https://www.jitpack.io' } 55 | 56 | maven { 57 | // All of Detox' artifacts are provided via the npm module 58 | url "$rootDir/../node_modules/detox/Detox-android" 59 | } 60 | } 61 | } 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: -Xmx1024m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError 15 | org.gradle.daemon=true 16 | org.gradle.parallel=true 17 | org.gradle.configureondemand=false 18 | 19 | # When configured, Gradle will run in incubating parallel mode. 20 | # This option should only be used with decoupled projects. More details, visit 21 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 22 | # org.gradle.parallel=true 23 | 24 | # AndroidX package structure to make it clearer which packages are bundled with the 25 | # Android operating system, and which are packaged with your app's APK 26 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 27 | android.useAndroidX=true 28 | # Automatically convert third-party libraries to use AndroidX 29 | android.enableJetifier=true 30 | 31 | # Version of flipper SDK to use with React Native 32 | FLIPPER_VERSION=0.99.0 33 | -------------------------------------------------------------------------------- /template/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /template/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /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 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | @rem Execute Gradle 73 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 74 | 75 | :end 76 | @rem End local scope for the variables with windows NT shell 77 | if "%ERRORLEVEL%"=="0" goto mainEnd 78 | 79 | :fail 80 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 81 | rem the _cmd.exe /c_ return code! 82 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 83 | exit /b 1 84 | 85 | :mainEnd 86 | if "%OS%"=="Windows_NT" endlocal 87 | 88 | :omega 89 | -------------------------------------------------------------------------------- /template/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'appStarter' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /template/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appStarter", 3 | "displayName": "appStarter" 4 | } -------------------------------------------------------------------------------- /template/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | plugins: [ 4 | [ 5 | '@babel/plugin-transform-react-jsx', 6 | { 7 | runtime: 'automatic', 8 | }, 9 | 'react-native-reanimated/plugin', 10 | ], 11 | ], 12 | env: { 13 | production: { 14 | plugins: ['transform-remove-console'], 15 | }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /template/bitbucket-pipelines.yml: -------------------------------------------------------------------------------- 1 | image: node:latest 2 | 3 | definitions: 4 | steps: 5 | - step: &clear-cache 6 | name: 'Resetting cache' 7 | script: 8 | - pipe: atlassian/bitbucket-clear-cache:3.1.1 9 | variables: 10 | BITBUCKET_USERNAME: $BITBUCKET_USERNAME 11 | BITBUCKET_APP_PASSWORD: $BITBUCKET_APP_PASSWORD 12 | - step: &install 13 | name: 'Installing Dependencies' 14 | caches: 15 | - node 16 | script: 17 | - yarn --ignore-engines install 18 | - step: &linter 19 | name: 'Running Linter' 20 | caches: 21 | - node 22 | script: 23 | - yarn run lint 24 | - cd __tests__ && touch lint.ok 25 | artifacts: 26 | - __tests__/** 27 | - step: &ts 28 | name: 'Running Type Checker' 29 | caches: 30 | - node 31 | script: 32 | - yarn run type 33 | - cd __tests__ && touch ts.ok 34 | artifacts: 35 | - __tests__/** 36 | - step: &unit 37 | name: 'Running Unit Tests' 38 | caches: 39 | - node 40 | script: 41 | - yarn run unit 42 | - cd __tests__ && touch unit.ok 43 | artifacts: 44 | - __tests__/** 45 | 46 | pipelines: 47 | default: 48 | - step: *clear-cache 49 | - step: *install 50 | - parallel: 51 | - step: *linter 52 | - step: *ts 53 | - step: *unit 54 | branches: 55 | '{staging,main}': 56 | - step: *clear-cache 57 | - step: 58 | name: 'Resetting Badge' 59 | script: 60 | - scripts/create-pipeline-badge.sh 'reset' 61 | artifacts: 62 | - __tests__/** 63 | - step: 64 | name: 'Uploading Badge' 65 | script: 66 | - pipe: atlassian/bitbucket-upload-file:0.3.2 67 | variables: 68 | BITBUCKET_USERNAME: $BITBUCKET_USERNAME 69 | BITBUCKET_APP_PASSWORD: $BITBUCKET_APP_PASSWORD 70 | FILENAME: '__tests__/status.svg' 71 | - step: *install 72 | - parallel: 73 | - step: *linter 74 | - step: *ts 75 | - step: *unit 76 | - step: 77 | name: 'Creating Badge' 78 | script: 79 | - scripts/create-pipeline-badge.sh 80 | artifacts: 81 | - __tests__/** 82 | - step: 83 | name: 'Uploading Badge' 84 | script: 85 | - pipe: atlassian/bitbucket-upload-file:0.3.2 86 | variables: 87 | BITBUCKET_USERNAME: $BITBUCKET_USERNAME 88 | BITBUCKET_APP_PASSWORD: $BITBUCKET_APP_PASSWORD 89 | FILENAME: '__tests__/status.svg' 90 | -------------------------------------------------------------------------------- /template/e2e/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "testEnvironment": "./environment", 3 | "testRunner": "jest-circus/runner", 4 | "testTimeout": 1200000, 5 | "testRegex": "\\.e2e\\.js$", 6 | "reporters": ["detox/runners/jest/streamlineReporter"], 7 | "verbose": true 8 | } 9 | -------------------------------------------------------------------------------- /template/e2e/environment.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const { DetoxCircusEnvironment, SpecReporter, WorkerAssignReporter } = require('detox/runners/jest-circus') 3 | 4 | class CustomDetoxEnvironment extends DetoxCircusEnvironment { 5 | constructor(config, context) { 6 | super(config, context) 7 | 8 | // Can be safely removed, if you are content with the default value (=300000ms) 9 | this.initTimeout = 300000 10 | 11 | // This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level. 12 | // This is strictly optional. 13 | this.registerListeners({ 14 | SpecReporter, 15 | WorkerAssignReporter, 16 | }) 17 | } 18 | } 19 | 20 | module.exports = CustomDetoxEnvironment 21 | -------------------------------------------------------------------------------- /template/e2e/login/login.e2e.js: -------------------------------------------------------------------------------- 1 | /* eslint-env detox/detox, jest */ 2 | import path from 'path' 3 | 4 | import E2E_CONFIG from '../../src/config/e2e-config' 5 | 6 | const describeFn = 7 | !E2E_CONFIG.RUN_ONLY || E2E_CONFIG.RUN_ONLY.includes(path.basename(__filename, '.e2e.js')) ? describe : describe.skip 8 | 9 | describeFn('👋 Login screen', () => { 10 | beforeAll(async () => { 11 | await device.launchApp() 12 | }) 13 | 14 | it('renders the default elements', async () => { 15 | await expect(element(by.id('Login'))).toBeVisible() 16 | await expect(element(by.id('Login.Logo'))).toBeVisible() 17 | await expect(element(by.id('Login.Button'))).toBeVisible() 18 | }) 19 | 20 | it('displays home after login button press', async () => { 21 | await expect(element(by.id('Login.Button'))).toBeVisible() 22 | await element(by.id('Login.Button')).tap() 23 | 24 | await expect(element(by.id('Home'))).toBeVisible() 25 | await expect(element(by.id('Home.Title'))).toBeVisible() 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /template/index.d.ts: -------------------------------------------------------------------------------- 1 | type Entries = { 2 | [K in keyof T]: [K, T[K]] 3 | }[keyof T][] 4 | 5 | declare namespace NodeJS { 6 | export interface Global { 7 | HermesInternal: null | Record 8 | window: Window 9 | document: Document 10 | __REMOTEDEV__: boolean 11 | enableStoreLogging?: boolean 12 | } 13 | } 14 | 15 | declare module '*.svg' { 16 | import { SvgProps } from 'react-native-svg' 17 | const content: React.FC 18 | export default content 19 | } 20 | 21 | declare module '*.png' 22 | declare module 'react-native-redash/lib/typescript/v1' 23 | -------------------------------------------------------------------------------- /template/index.js: -------------------------------------------------------------------------------- 1 | import 'react-native-gesture-handler' 2 | import NetInfo from '@react-native-community/netinfo' 3 | import { AppRegistry, LogBox, Platform, UIManager } from 'react-native' 4 | 5 | import App from './src/App' 6 | 7 | import { name as appName } from './app.json' 8 | 9 | LogBox.ignoreLogs([ 10 | "[react-native-gesture-handler] Seems like you're using an old API with gesture components, check out new Gestures system!", 11 | ]) 12 | 13 | if (Platform.OS === 'android') { 14 | UIManager.setLayoutAnimationEnabledExperimental?.(true) 15 | } 16 | 17 | NetInfo.configure({ shouldFetchWiFiSSID: true }) 18 | 19 | AppRegistry.registerComponent(appName, () => App) 20 | -------------------------------------------------------------------------------- /template/ios/Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /template/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '12.1' 5 | 6 | def shared_pods 7 | config = use_native_modules! 8 | use_react_native!( 9 | :path => config[:reactNativePath], 10 | # To enable Hermes on iOS, change `false` to `true` and then install pods. 11 | :hermes_enabled => true 12 | ) 13 | 14 | # Enables Flipper. 15 | # 16 | # Note that if you have use_frameworks! enabled, Flipper will not work and 17 | # you should disable the next few line. 18 | use_flipper!() 19 | 20 | permissions_path = '../node_modules/react-native-permissions/ios' 21 | 22 | pod 'Permission-Notifications', :path => "#{permissions_path}/Notifications" 23 | end 24 | 25 | 26 | target 'appStarter' do 27 | shared_pods 28 | 29 | target 'appStarterTests' do 30 | inherit! :complete 31 | # Pods for testing 32 | end 33 | end 34 | 35 | target 'DEV' do 36 | shared_pods 37 | inherit! :search_paths 38 | end 39 | 40 | target 'STAGING' do 41 | shared_pods 42 | inherit! :search_paths 43 | end 44 | 45 | target 'PROD' do 46 | shared_pods 47 | inherit! :search_paths 48 | end 49 | 50 | post_install do |installer| 51 | react_native_post_install(installer) 52 | __apply_Xcode_12_5_M1_post_install_workaround(installer) 53 | end 54 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/DEV Debug.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/DEV Prod.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/PROD Debug.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/PROD Release.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/STAGING Debug.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/STAGING Release.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/appStarter-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcodeproj/xcshareddata/xcschemes/appStarter.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/ios/appStarter.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /template/ios/appStarter/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /template/ios/appStarter/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #import "RNSplashScreen.h" 8 | 9 | #ifdef FB_SONARKIT_ENABLED 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | static void InitializeFlipper(UIApplication *application) { 18 | FlipperClient *client = [FlipperClient sharedClient]; 19 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 20 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 21 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 22 | [client addPlugin:[FlipperKitReactPlugin new]]; 23 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 24 | [client start]; 25 | } 26 | #endif 27 | 28 | @implementation AppDelegate 29 | 30 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 31 | { 32 | #ifdef FB_SONARKIT_ENABLED 33 | InitializeFlipper(application); 34 | #endif 35 | 36 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 37 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 38 | moduleName:@"appStarter" 39 | initialProperties:nil]; 40 | 41 | if (@available(iOS 13.0, *)) { 42 | rootView.backgroundColor = [UIColor systemBackgroundColor]; 43 | } else { 44 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 45 | } 46 | 47 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 48 | UIViewController *rootViewController = [UIViewController new]; 49 | rootViewController.view = rootView; 50 | self.window.rootViewController = rootViewController; 51 | [self.window makeKeyAndVisible]; 52 | 53 | // Sets background color as #121212 during Metro compilation 54 | // UIView* launchScreenView = [[UIView alloc] initWithFrame:CGRectMake(0,0,[[UIScreen mainScreen] bounds].size.width,[[UIScreen mainScreen] bounds].size.height)]; 55 | // launchScreenView.backgroundColor = [[UIColor alloc]initWithRed:0.07 green:0.07 blue:0.07 alpha:1]; 56 | // rootView.loadingView = launchScreenView; 57 | 58 | // Displays logs in Release mode 59 | // RCTSetLogThreshold(RCTLogLevelInfo - 1); 60 | 61 | // Forces date picker UI for iOS 14 62 | if (@available(iOS 14, *)) { 63 | UIDatePicker *picker = [UIDatePicker appearance]; 64 | picker.preferredDatePickerStyle = UIDatePickerStyleWheels; 65 | } 66 | 67 | [RNSplashScreen show]; 68 | 69 | return YES; 70 | } 71 | 72 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 73 | { 74 | #if DEBUG 75 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 76 | #else 77 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 78 | #endif 79 | } 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /template/ios/appStarter/Bridge.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bridge.swift 3 | // appStarter 4 | // 5 | // Created by Charles Mangwa on 21.07.21. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /template/ios/appStarter/Dev-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Starter Dev 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UIAppFonts 41 | 42 | NotoSansSC-Bold.otf 43 | NotoSansSC-Light.otf 44 | NotoSansSC-Medium.otf 45 | NotoSansSC-Regular.otf 46 | 47 | UILaunchStoryboardName 48 | LaunchScreen 49 | UIRequiredDeviceCapabilities 50 | 51 | armv7 52 | 53 | UISupportedInterfaceOrientations 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | 58 | UIViewControllerBasedStatusBarAppearance 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/ios/appStarter/Images.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /template/ios/appStarter/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /template/ios/appStarter/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Starter Dev 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocalNetworkUsageDescription 39 | App Starter needs to access your location to detect your current WiFi network. 40 | NSLocationAlwaysAndWhenInUseUsageDescription 41 | App Starter needs to access your location to detect your current WiFi network. 42 | NSLocationAlwaysUsageDescription 43 | App Starter needs to access your location to detect your current WiFi network. 44 | NSLocationWhenInUseUsageDescription 45 | App Starter needs to access your location to detect your current WiFi network. 46 | UIAppFonts 47 | 48 | NotoSansSC-Bold.otf 49 | NotoSansSC-Light.otf 50 | NotoSansSC-Medium.otf 51 | NotoSansSC-Regular.otf 52 | 53 | UILaunchStoryboardName 54 | LaunchScreen 55 | UIRequiredDeviceCapabilities 56 | 57 | armv7 58 | 59 | UISupportedInterfaceOrientations 60 | 61 | UIInterfaceOrientationPortrait 62 | UIInterfaceOrientationPortraitUpsideDown 63 | 64 | UIViewControllerBasedStatusBarAppearance 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /template/ios/appStarter/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /template/ios/appStarter/Prod-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | App Starter 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UIAppFonts 41 | 42 | NotoSansSC-Bold.otf 43 | NotoSansSC-Light.otf 44 | NotoSansSC-Medium.otf 45 | NotoSansSC-Regular.otf 46 | 47 | UILaunchStoryboardName 48 | LaunchScreen 49 | UIRequiredDeviceCapabilities 50 | 51 | armv7 52 | 53 | UISupportedInterfaceOrientations 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | 58 | UIViewControllerBasedStatusBarAppearance 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /template/ios/appStarter/Staging-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Starter Staging 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UIAppFonts 41 | 42 | NotoSansSC-Bold.otf 43 | NotoSansSC-Light.otf 44 | NotoSansSC-Medium.otf 45 | NotoSansSC-Regular.otf 46 | 47 | UILaunchStoryboardName 48 | LaunchScreen 49 | UIRequiredDeviceCapabilities 50 | 51 | armv7 52 | 53 | UISupportedInterfaceOrientations 54 | 55 | UIInterfaceOrientationPortrait 56 | UIInterfaceOrientationPortraitUpsideDown 57 | 58 | UIViewControllerBasedStatusBarAppearance 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /template/ios/appStarter/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /template/ios/appStarterTests/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/ios/appStarterTests/appStarterTests.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 appStarterTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation appStarterTests 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(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /template/ios/exportOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | generateAppStoreInformation 6 | 7 | provisioningProfiles 8 | 9 | com.appstarter.prod 10 | INSERT_VALUE_HERE 11 | com.appstarter.prod.OneSignalNotificationServiceExtensionProd 12 | INSERT_VALUE_HERE 13 | 14 | method 15 | app-store 16 | signingStyle 17 | manual 18 | stripSwiftSymbols 19 | 20 | teamID 21 | INSERT_VALUE_HERE 22 | uploadBitcode 23 | 24 | uploadSymbols 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /template/metro.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const { getDefaultConfig } = require('metro-config') 3 | 4 | module.exports = (async () => { 5 | const { 6 | resolver: { sourceExts, assetExts }, 7 | } = await getDefaultConfig() 8 | return { 9 | transformer: { 10 | babelTransformerPath: require.resolve('react-native-svg-transformer'), 11 | getTransformOptions: async () => ({ 12 | transform: { experimentalImportSupport: false, inlineRequires: true }, 13 | }), 14 | }, 15 | resolver: { 16 | assetExts: assetExts.filter((ext) => ext !== 'svg'), 17 | sourceExts: [...sourceExts, 'svg'], 18 | }, 19 | } 20 | })() 21 | -------------------------------------------------------------------------------- /template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "appstarter", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "npx react-native start", 7 | "android": "./build.sh run android dev", 8 | "ios": "./build.sh run ios dev", 9 | "lint": "npx eslint '*/**/*.{js,ts,tsx}' --fix", 10 | "type": "yarn tsc", 11 | "unit": "yarn jest --updateSnapshot --verbose", 12 | "test": "yarn lint && yarn type && yarn unit && ./build.sh detox --ci", 13 | "test-commit": "yarn lint && yarn type && yarn unit", 14 | "postinstall": "./scripts/postinstall.sh" 15 | }, 16 | "pre-commit": "test-commit", 17 | "dependencies": { 18 | "@colorfy-software/zfy": "^1.0.0", 19 | "@react-native-clipboard/clipboard": "^1.9.0", 20 | "@react-native-community/hooks": "^2.8.1", 21 | "@react-native-community/masked-view": "^0.1.11", 22 | "@react-native-community/netinfo": "^8.0.0", 23 | "@react-navigation/bottom-tabs": "^6.2.0", 24 | "@react-navigation/native": "^6.0.8", 25 | "@react-navigation/stack": "^6.1.1", 26 | "cross-fetch": "^3.1.5", 27 | "date-fns": "^2.28.0", 28 | "google-protobuf": "^3.19.4", 29 | "i18n-js": "^3.8.0", 30 | "immer": "^9.0.12", 31 | "mitt": "^3.0.0", 32 | "react": "17.0.2", 33 | "react-native": "^0.67.3", 34 | "react-native-device-info": "^8.4.9", 35 | "react-native-fast-image": "^8.5.11", 36 | "react-native-gesture-handler": "^2.2.0", 37 | "react-native-localize": "^2.2.0", 38 | "react-native-mmkv": "^2.1.0", 39 | "react-native-modalfy": "^3.1.0", 40 | "react-native-navigation-bar-color": "^2.0.1", 41 | "react-native-parsed-text": "^0.0.22", 42 | "react-native-permissions": "^3.3.0", 43 | "react-native-reanimated": "^2.4.1", 44 | "react-native-safe-area-context": "^3.4.0", 45 | "react-native-screens": "^3.12.0", 46 | "react-native-splash-screen": "^3.3.0", 47 | "react-native-svg": "^12.1.1", 48 | "react-string-replace": "^0.4.4", 49 | "zustand": "^3.7.1" 50 | }, 51 | "devDependencies": { 52 | "@babel/core": "^7.17.5", 53 | "@babel/runtime": "^7.17.2", 54 | "@react-native-community/eslint-config": "^3.0.1", 55 | "@testing-library/jest-native": "^4.0.4", 56 | "@testing-library/react-hooks": "^7.0.2", 57 | "@testing-library/react-native": "^9.0.0", 58 | "@types/hoist-non-react-statics": "^3.3.1", 59 | "@types/i18n-js": "^3.8.2", 60 | "@types/jest": "^27.4.1", 61 | "@types/react": "^17.0.39", 62 | "@types/react-is": "^17.0.3", 63 | "@types/react-native": "^0.67.0", 64 | "@types/react-test-renderer": "^17.0.1", 65 | "@typescript-eslint/eslint-plugin": "^5.12.1", 66 | "@typescript-eslint/parser": "^5.12.1", 67 | "babel-jest": "^27.5.1", 68 | "babel-plugin-transform-remove-console": "^6.9.4", 69 | "detox": "^19.4.5 < 19.5.2 || > 19.5.2", 70 | "detox-recorder": "^1.0.151", 71 | "eslint": "^8.10.0", 72 | "eslint-config-prettier": "^8.4.0", 73 | "eslint-config-standard": "^16.0.3", 74 | "eslint-plugin-detox": "^1.0.0", 75 | "eslint-plugin-flowtype": "^8.0.3", 76 | "eslint-plugin-import": "^2.25.4", 77 | "eslint-plugin-node": "^11.1.0", 78 | "eslint-plugin-prettier": "^4.0.0", 79 | "eslint-plugin-promise": "^6.0.0", 80 | "eslint-plugin-react": "^7.29.2", 81 | "eslint-plugin-react-hooks": "^4.3.0", 82 | "eslint-plugin-react-native": "^4.0.0", 83 | "eslint-plugin-standard": "^5.0.0", 84 | "jest": "^27.5.1", 85 | "jest-circus": "^27.5.1", 86 | "jest-fetch-mock": "^3.0.3", 87 | "jest-sonar": "^0.2.12", 88 | "metro-react-native-babel-preset": "^0.66.2", 89 | "pre-commit": "^1.2.2", 90 | "prettier": "2.5.1", 91 | "react-native-svg-transformer": "^1.0.0", 92 | "react-test-renderer": "17.0.2", 93 | "typescript": "^4.5.5" 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /template/react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | assets: ['src/assets/fonts'], 3 | } 4 | -------------------------------------------------------------------------------- /template/scripts/build-unsigned-ipa.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | NC='\033[00m' 6 | RED='\033[01;31m' 7 | GREEN='\033[32m' 8 | BLUE_LINK='\033[4;34m' 9 | 10 | function readJson { 11 | UNAMESTR=$(uname) 12 | if [[ "$UNAMESTR" == 'Linux' ]]; then 13 | SED_EXTENDED='-r' 14 | elif [[ "$UNAMESTR" == 'Darwin' ]]; then 15 | SED_EXTENDED='-E' 16 | fi 17 | 18 | VALUE=$(grep -m 1 "\"${2}\"" "${1}" | sed ${SED_EXTENDED} 's/^ *//;s/.*: *"//;s/",?//') 19 | 20 | if [ ! "$VALUE" ]; then 21 | echo -e "${RED}Error: Cannot find \"${2}\" in ${1}${NC}" >&2 22 | exit 1 23 | else 24 | echo "$VALUE" 25 | fi 26 | } 27 | 28 | CURRENT_PATH=$(pwd) 29 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && cd .. && pwd)" 30 | APP_NAME="$(readJson app.json name)" 31 | 32 | if [[ $# = 0 ]]; then 33 | echo -e "\n${RED}You didn't provide any scheme name! ie: 'STAGING' or 'PROD' ${NC} \n" 34 | elif [[ $# = 1 ]]; then 35 | echo -e "\n${RED}You didn't provide the desired build number${NC} \n" 36 | elif [[ $# -gt 2 ]]; then 37 | echo -e "\n${RED}You provided too many arguments, expected only 2. ie: 'PROD' 42 ${NC} \n" 38 | else 39 | osascript -e 'display alert "💡 Disable Flipper 💡" message "Remember to comment use_flipper!() out of the Podfile and run pod install before building unsigned IPAs!"' 40 | cd "${ROOT}"/ios 41 | 42 | SCHEME="$(tr '[:lower:]' '[:upper:]' <<<"${1}")" 43 | BUILD_NUMBER=${2} 44 | IPA_PATH="${ROOT}"/ios/IPAs/${SCHEME}/${SCHEME}_${BUILD_NUMBER} 45 | 46 | mkdir -p "${IPA_PATH}" 47 | 48 | xcodebuild -workspace "${ROOT}"/ios/"${APP_NAME}".xcworkspace -scheme "${SCHEME} Release" -sdk iphoneos -configuration AppStoreDistribution archive -archivePath "${IPA_PATH}"/"${SCHEME} Release".xcarchive 49 | xcodebuild -exportArchive -archivePath "${IPA_PATH}"/"${SCHEME} Release".xcarchive -exportOptionsPlist "${ROOT}"/ios/exportOptions.plist -exportPath "${IPA_PATH}" 50 | 51 | echo -e "Unsigned IPA file available at: ${BLUE_LINK}${IPA_PATH}${NC}\n" 52 | 53 | echo -e "${GREEN}Done!${NC}" 54 | 55 | open "${IPA_PATH}" 56 | 57 | cd "${CURRENT_PATH}" 58 | fi 59 | -------------------------------------------------------------------------------- /template/scripts/create-pipeline-badge.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | LINT_OK=__tests__/lint.ok 4 | TS_OK=__tests__/ts.ok 5 | UNIT_OK=__tests__/unit.ok 6 | 7 | STATUS_BADGE=__tests__/status.svg 8 | SUCCESS_BADGE=__tests__/__badges__/success.svg 9 | FAILURE_BADGE=__tests__/__badges__/failure.svg 10 | 11 | if [[ "$1" == "reset" ]]; then 12 | cp $FAILURE_BADGE $STATUS_BADGE 13 | echo "🚨 Failure badge created 🚨" 14 | elif [[ $# = 0 && -f "$LINT_OK" && -f "$TS_OK" && -f "$UNIT_OK" ]]; then 15 | cp $SUCCESS_BADGE $STATUS_BADGE 16 | echo "✅ Success badge created! ✅" 17 | rm -f $LINT_OK && rm -f $TS_OK && rm -f $UNIT_OK 18 | fi 19 | -------------------------------------------------------------------------------- /template/scripts/generate-signed-android-keystore.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURRENT_PATH=$(pwd) 4 | UUID=$(python -c 'import sys,uuid; sys.stdout.write(uuid.uuid4().hex)') 5 | PATH_TO_JAVA=$(/usr/libexec/java_home) 6 | 7 | NC='\033[0m' 8 | BLUE='\033[1;34m' 9 | GREEN='\033[01;32m' 10 | YELLOW='\033[0;33m' 11 | BLUE_LINK='\033[4;34m' 12 | 13 | echo -e "Current path is ${BLUE_LINK}${CURRENT_PATH}${NC} \n" 14 | echo -e "Generating Android keystore file using UUID ${BLUE}$UUID${NC} as name \n" 15 | 16 | cd "$PATH_TO_JAVA" || exit 17 | sudo keytool -genkey -v -keystore "$UUID".keystore -alias "$UUID" -keyalg RSA -keysize 2048 -validity 10000 18 | 19 | echo -e "\nMoving keystore file to ${BLUE_LINK}${CURRENT_PATH}/android/app${NC}\n" 20 | cp -r -f ./"$UUID".keystore "${CURRENT_PATH}"/android/app 21 | 22 | cd "$CURRENT_PATH" || exit 23 | 24 | { 25 | echo "" 26 | echo "MYAPP_UPLOAD_STORE_FILE=$UUID.keystore" 27 | echo "MYAPP_UPLOAD_KEY_ALIAS=$UUID" 28 | echo "MYAPP_UPLOAD_STORE_PASSWORD=_**ENTER-THE-PASSWORD-YOU-PROVIDED**_" 29 | echo "MYAPP_UPLOAD_KEY_PASSWORD=_**ENTER-THE-PASSWORD-YOU-PROVIDED**_" 30 | } >> ./android/gradle.properties 31 | 32 | echo -e "${GREEN}✔️ KEYSTORE GENERATED\n${NC}" 33 | echo -e "${YELLOW}Don't forget to add the passwords to ${BLUE_LINK}${CURRENT_PATH}/android/gradle.properties${NC}${NC}" 34 | 35 | open ./android/gradle.properties 36 | -------------------------------------------------------------------------------- /template/scripts/images.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-var-requires */ 2 | const fs = require('fs') 3 | const path = require('path') 4 | 5 | const getFilesName = () => { 6 | const files = fs 7 | .readdirSync('src/assets') 8 | .filter(file => file.endsWith('.svg') || file.endsWith('.png') || file.endsWith('.gif')) 9 | .map(file => file.replace('@2x.png', '.png').replace('@3x.png', '.png')) 10 | 11 | return Array.from(new Set(files)) 12 | } 13 | 14 | const getComponentName = s => 15 | s 16 | .replace(/_./g, x => x[1].toUpperCase()) 17 | .replace(/\.\w+$/g, '') 18 | .substring(3) 19 | 20 | const getImport = s => `require('./${s}')${s.match(/.svg$/) ? '.default' : ''}` 21 | const getType = s => (s.match(/.svg$/) ? 'FC' : 'ImageSourcePropType') 22 | const getFileNameInCamelCase = s => s.replace(/_./g, x => x[1].toUpperCase()).replace(/\.\w+$/g, '') 23 | 24 | const generate = () => { 25 | console.log('\nGenerating src/assets/index.tsx...') 26 | 27 | const types = getFilesName().reduce( 28 | (output, name) => { 29 | const isVector = name.match(/.svg$/) 30 | const type = ` ${getFileNameInCamelCase(name)}: ${getType(name)}` 31 | return { 32 | bitmap: [...output.bitmap, ...(!isVector ? [type] : [])], 33 | vector: [...output.vector, ...(isVector ? [type] : [])], 34 | } 35 | }, 36 | { bitmap: [], vector: [] }, 37 | ) 38 | 39 | const properties = getFilesName() 40 | .map( 41 | name => `/** 42 | * ![${name}](file://${path.resolve(__dirname, '../src/assets', name)}) 43 | */ 44 | ${getFileNameInCamelCase(name)}: ${getImport(name)}`, 45 | ) 46 | .join(',\n ') 47 | 48 | const components = getFilesName().reduce( 49 | (output, name) => { 50 | const isIcon = name.startsWith('icn') && name.endsWith('.svg') 51 | const isImage = name.startsWith('img') 52 | 53 | const iconComponent = ` /** 54 | * ![${name}](file://${path.resolve(__dirname, '../src/assets', name)}) 55 | */ 56 | ${getComponentName( 57 | name, 58 | )}: ({ size, fill, style, stroke, testID, color = 'transparent' }: IconPropsType): JSX.Element => { 59 | const Svg = ${getImport(name)} 60 | return ( 61 | 62 | ) 63 | } 64 | ` 65 | const imageComponent = ` /** 66 | * ![${name}](file://${path.resolve(__dirname, '../src/assets', name)}) 67 | */ 68 | ${getComponentName( 69 | name, 70 | )}: (props: Omit): JSX.Element => 71 | ` 72 | 73 | return { 74 | icons: [...output.icons, ...(isIcon ? [iconComponent] : [])], 75 | images: [...output.images, ...(isImage ? [imageComponent] : [])], 76 | } 77 | }, 78 | { icons: [], images: [] }, 79 | ) 80 | 81 | const file = `// 82 | // NOTE: THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 83 | // 84 | 85 | /* eslint-disable @typescript-eslint/no-var-requires */ 86 | 87 | import type { FC } from 'react' 88 | import type { SvgProps } from 'react-native-svg' 89 | import FastImage, { FastImageProps } from 'react-native-fast-image' 90 | import type { ImageSourcePropType, ViewProps, ViewStyle, StyleProp } from 'react-native' 91 | 92 | export type AssetsBitmapType = { 93 | ${types.bitmap.join('\n')} 94 | } 95 | 96 | export type AssetsVectorType = { 97 | ${types.vector.join('\n')} 98 | } 99 | 100 | export interface IconPropsType { 101 | size?: number 102 | color?: string 103 | fill?: string 104 | stroke?: string 105 | style?: StyleProp 106 | testID?: ViewProps['testID'] 107 | } 108 | 109 | export type IconType = typeof Icons[keyof typeof Icons] 110 | 111 | export const Icons = { 112 | ${components.icons} 113 | } 114 | 115 | export const Images = { 116 | ${components.images} 117 | } 118 | 119 | export const Assets = { 120 | ${properties}, 121 | } 122 | ` 123 | 124 | fs.writeFileSync('src/assets/index.tsx', file, 'utf8') 125 | } 126 | 127 | generate() 128 | -------------------------------------------------------------------------------- /template/scripts/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | uname=$(uname) 4 | 5 | npx jetify 6 | 7 | if [[ -z $APPCENTER_ANDROID_VARIANT && -z $APPCENTER_XCODE_SCHEME ]]; then 8 | # NOTE: Makes the Detox mock file non-commitable (only Detox updates it when it runs). 9 | # Use --no-assume-unchanged if you want to reverse this. 10 | git update-index --assume-unchanged src/config/e2e-config.ts 11 | fi 12 | 13 | if [[ $uname =~ Darwin && -z $APPCENTER_ANDROID_VARIANT ]]; then 14 | npx pod-install ios 15 | else 16 | echo "\033[34m\nWe didn't setup CocoaPods as you're not using macOS.\033[0m" 17 | fi 18 | 19 | echo "\033[0;32m\nDonesies!\033[0m" 20 | -------------------------------------------------------------------------------- /template/scripts/run-detox-ci.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function setupAndroidEmulator { 4 | # NOTE: Let the code know if it should swap some functions with 5 | # their mocked counterpart for E2E testing (see user-core.ts for an example) 6 | echo "💡 Setting up E2E_CONFIG.IS_MOCKING_ENABLED for Detox" 7 | sed -i '' 's/false/true/g' src/config/e2e-config.ts 8 | 9 | echo "🏁 Setup Android Emulator" 10 | SIMULATOR_IMAGE="system-images;android-28;google_apis;x86" 11 | SIMULATOR_NAME="Pixel2" 12 | 13 | ANDROID_HOME=~/Library/Android/sdk 14 | ANDROID_SDK_ROOT=~/Library/Android/sdk 15 | ANDROID_AVD_HOME=~/.android/avd 16 | PATH="$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools:$PATH" 17 | 18 | echo "🌬️ Cleaning up previous Android Emulator instances" 19 | rm -f "${ANDROID_AVD_HOME}"/*.avd/*.lock 20 | 21 | echo "🤝 Accepts all SDK licences" 22 | yes | sdkmanager --licenses 23 | 24 | echo "📥 Download Emulator Image" 25 | sdkmanager --install "$SIMULATOR_IMAGE" 26 | 27 | echo "🆕 Create Emulator '$SIMULATOR_NAME' with image '$SIMULATOR_IMAGE'" 28 | echo "no" | avdmanager --verbose create avd --force --name "$SIMULATOR_NAME" --device "pixel" --package "$SIMULATOR_IMAGE" --tag "google_apis" --abi "x86" 29 | } 30 | 31 | function setupiOSSimulator { 32 | sed -i '' 's/false/true/g' src/config/e2e-config.ts 33 | brew tap wix/brew 34 | brew install applesimutils 35 | npx detox clean-framework-cache 36 | npx detox build-framework-cache 37 | } 38 | 39 | function cleanupArtifacts { 40 | echo "🧹 Clean up generated artifacts" 41 | rm -f /Users/runner/work/1/s/android/app/build/outputs/apk/androidTest/prod/release/app-prod-release-androidTest.apk 42 | rm -f /Users/runner/work/1/s/android/app/build/outputs/apk/androidTest/staging/release/app-staging-release-androidTest.apk 43 | } 44 | 45 | if [[ "$APPCENTER_XCODE_SCHEME" == "STAGING Release" ]]; then 46 | setupiOSSimulator 47 | npx detox build -c ios.staging.release.ci 48 | npx detox test -c ios.staging.release.ci --loglevel verbose --retries 3 --cleanup 49 | elif [[ "$APPCENTER_XCODE_SCHEME" == "PROD Release" ]]; then 50 | setupiOSSimulator 51 | npx detox build -c ios.staging.release.ci 52 | npx detox test -c ios.staging.release.ci --loglevel verbose --retries 3 --cleanup 53 | elif [[ "$APPCENTER_ANDROID_VARIANT" == "stagingRelease" ]]; then 54 | npx detox build -c android.staging.release --loglevel verbose 55 | setupAndroidEmulator 56 | npx detox test -c android.staging.release --loglevel verbose --retries 3 --gpu guest --headless 57 | cleanupArtifacts 58 | elif [[ "$APPCENTER_ANDROID_VARIANT" == "prodRelease" ]]; then 59 | npx detox build -c android.prod.release --loglevel verbose 60 | setupAndroidEmulator 61 | npx detox test -c android.prod.release --loglevel verbose --retries 3 --gpu guest --headless 62 | cleanupArtifacts 63 | fi 64 | -------------------------------------------------------------------------------- /template/src/assets/fonts/NotoSansSC-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/fonts/NotoSansSC-Bold.otf -------------------------------------------------------------------------------- /template/src/assets/fonts/NotoSansSC-Light.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/fonts/NotoSansSC-Light.otf -------------------------------------------------------------------------------- /template/src/assets/fonts/NotoSansSC-Medium.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/fonts/NotoSansSC-Medium.otf -------------------------------------------------------------------------------- /template/src/assets/fonts/NotoSansSC-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/fonts/NotoSansSC-Regular.otf -------------------------------------------------------------------------------- /template/src/assets/icn_activity.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_back_button.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_copy.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /template/src/assets/icn_email.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_eye_closed.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_eye_open.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_home.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_lock.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_pen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_profile.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_reload.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_send.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_settings.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/icn_tips.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /template/src/assets/icn_wifi.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /template/src/assets/img_close_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/img_close_button.png -------------------------------------------------------------------------------- /template/src/assets/img_close_button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/img_close_button@2x.png -------------------------------------------------------------------------------- /template/src/assets/img_close_button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/img_close_button@3x.png -------------------------------------------------------------------------------- /template/src/assets/img_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/colorfy-software/react-native-colorfy-template/ec6d41014e2c2e0536c9039a0b5f7e5ed43d1466/template/src/assets/img_logo.png -------------------------------------------------------------------------------- /template/src/components/AnimatedWrapper.tsx: -------------------------------------------------------------------------------- 1 | import type { StyleProp, ViewStyle } from 'react-native' 2 | import React, { memo, PropsWithChildren, useEffect, useState } from 'react' 3 | import Animated, { useAnimatedStyle, withDelay, withSpring } from 'react-native-reanimated' 4 | 5 | export const SPRING_CONFIG = { 6 | damping: 50, 7 | mass: 2, 8 | stiffness: 350, 9 | overshootClamping: false, 10 | restDisplacementThreshold: 0.001, 11 | restSpeedThreshold: 0.001, 12 | } 13 | 14 | export const STAGGER_DELAY = 85 15 | 16 | interface AnimatedWrapperPropsType { 17 | testID?: ViewStyle['testID'] 18 | hideDirection?: 'up' | 'down' 19 | isVisible?: boolean | undefined 20 | staggerIndex?: number | undefined 21 | backStaggerIndex?: number | undefined 22 | style?: StyleProp> 23 | } 24 | 25 | const AnimatedWrapper = ({ 26 | testID, 27 | children, 28 | isVisible, 29 | hideDirection = 'up', 30 | staggerIndex = 0, 31 | backStaggerIndex = 0, 32 | style = {}, 33 | }: PropsWithChildren): JSX.Element => { 34 | // NOTE: Using a state to control the animation on mount when no isVisible is provided 35 | const [visibilityState, setVisibilityState] = useState(isVisible) 36 | 37 | useEffect(() => { 38 | if (typeof isVisible === 'undefined') { 39 | setVisibilityState(true) 40 | } 41 | // eslint-disable-next-line react-hooks/exhaustive-deps 42 | }, []) 43 | 44 | const visibility = visibilityState || isVisible 45 | 46 | const animatedStyles = useAnimatedStyle(() => { 47 | const direction = hideDirection === 'up' ? -100 : 100 48 | 49 | return { 50 | opacity: withSpring( 51 | withDelay(Number(visibility), STAGGER_DELAY * (visibility ? staggerIndex : backStaggerIndex)), 52 | SPRING_CONFIG, 53 | ), 54 | transform: [ 55 | { 56 | translateY: withSpring( 57 | withDelay(visibility ? 0 : direction, STAGGER_DELAY * (visibility ? staggerIndex : backStaggerIndex)), 58 | SPRING_CONFIG, 59 | ), 60 | }, 61 | ], 62 | } 63 | }) 64 | 65 | return ( 66 | 70 | {children} 71 | 72 | ) 73 | } 74 | 75 | export default memo(AnimatedWrapper) 76 | -------------------------------------------------------------------------------- /template/src/components/AppText.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { Text as TextModule, StyleProp, TextStyle, TextProps, GestureResponderEvent } from 'react-native' 3 | 4 | import { COLORS, FONT_FAMILY, TYPOGRAPHY } from '../styles/style-guide' 5 | 6 | interface PropsType { 7 | type?: keyof typeof TYPOGRAPHY 8 | color?: string 9 | children?: React.ReactNode 10 | onPress?: ((event: GestureResponderEvent) => void) | undefined 11 | style?: StyleProp 12 | fontSize?: TextStyle['fontSize'] 13 | bold?: boolean 14 | light?: boolean 15 | ellipsizeMode?: 'head' | 'middle' | 'tail' | 'clip' | undefined 16 | numberOfLines?: number | undefined 17 | selectable?: boolean | undefined 18 | testID?: TextProps['testID'] 19 | } 20 | 21 | const AppText = ({ 22 | bold, 23 | light, 24 | type = 'body', 25 | color, 26 | children, 27 | style, 28 | numberOfLines, 29 | ellipsizeMode, 30 | selectable = true, 31 | fontSize, 32 | onPress, 33 | testID, 34 | ...textProps 35 | }: PropsType): JSX.Element => { 36 | const predefinedStyles = (type && TYPOGRAPHY[type]) || {} 37 | const passedStyles = style || {} 38 | const boldStyles: { 39 | fontFamily?: TextStyle['fontFamily'] 40 | } = bold ? { fontFamily: FONT_FAMILY.notoSansScBold } : {} 41 | const lightStyles: { 42 | fontFamily?: TextStyle['fontFamily'] 43 | } = light ? { fontFamily: FONT_FAMILY.notoSansScLight } : {} 44 | const compiledStyles: StyleProp = [ 45 | predefinedStyles, 46 | { fontSize: fontSize ?? predefinedStyles.fontSize }, 47 | passedStyles, 48 | boldStyles, 49 | lightStyles, 50 | { color: color || (style as TextStyle)?.color || COLORS.text }, 51 | ] 52 | 53 | return ( 54 | 62 | {children} 63 | 64 | ) 65 | } 66 | 67 | export default memo(AppText) 68 | -------------------------------------------------------------------------------- /template/src/components/AsyncRenderWrapper.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, memo, Fragment } from 'react' 2 | import { View, ViewProps, StyleSheet } from 'react-native' 3 | 4 | import ScreenLoader from './ScreenLoader' 5 | 6 | interface PropsType { 7 | loader?: JSX.Element 8 | showContent?: boolean 9 | backgroundColor?: string 10 | loaderColor?: string 11 | testID?: ViewProps['testID'] 12 | } 13 | 14 | const WaitForThread = ({ 15 | children, 16 | loader, 17 | backgroundColor, 18 | loaderColor, 19 | showContent, 20 | testID, 21 | }: React.PropsWithChildren): JSX.Element => { 22 | const [interactionsDone, setInteractionsDone] = useState(false) 23 | 24 | useEffect(() => { 25 | let timer: number | undefined 26 | 27 | if (typeof showContent === 'undefined') { 28 | timer = window.setTimeout(() => { 29 | setInteractionsDone(true) 30 | }, 10) 31 | } 32 | 33 | return (): void => clearTimeout(timer) 34 | }, [showContent]) 35 | 36 | return ( 37 | 38 | {(interactionsDone || (typeof showContent !== 'undefined' && showContent)) && {children}} 39 | {loader || ( 40 | 49 | )} 50 | 51 | ) 52 | } 53 | 54 | export default memo(WaitForThread) 55 | 56 | const styles = StyleSheet.create({ 57 | container: { 58 | flex: 1, 59 | }, 60 | }) 61 | -------------------------------------------------------------------------------- /template/src/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react' 2 | import { ActivityIndicator, Pressable, StyleSheet, StyleProp, TextStyle, ViewStyle, PressableProps } from 'react-native' 3 | 4 | import AppText from './AppText' 5 | 6 | import { COLORS, DEVICE } from '../styles/style-guide' 7 | 8 | interface PropsType { 9 | children?: React.ReactNode 10 | title?: string 11 | color?: string 12 | loading?: boolean 13 | disabled?: boolean 14 | secondary?: boolean 15 | tertiary?: boolean 16 | onPress?: () => void 17 | style?: StyleProp 18 | testID?: PressableProps['testID'] 19 | titleStyle?: StyleProp 20 | } 21 | 22 | const Button = ({ 23 | style, 24 | color, 25 | title, 26 | testID, 27 | loading, 28 | disabled, 29 | secondary, 30 | tertiary, 31 | children, 32 | onPress, 33 | titleStyle, 34 | ...pressableProps 35 | }: PropsType): JSX.Element => { 36 | const isNotPrimary = secondary || tertiary 37 | 38 | const borderRadius = isNotPrimary ? 20 : 8 39 | const textColor = isNotPrimary ? color || COLORS.secondary : 'white' 40 | const borderColor = secondary ? color || COLORS.secondary : 'transparent' 41 | const backgroundColor = isNotPrimary ? 'transparent' : color || COLORS.secondary 42 | 43 | return ( 44 | 60 | {loading ? ( 61 | 62 | ) : ( 63 | children || ( 64 | 65 | {title} 66 | 67 | ) 68 | )} 69 | 70 | ) 71 | } 72 | 73 | const styles = StyleSheet.create({ 74 | container: { 75 | height: 60, 76 | justifyContent: 'center', 77 | alignItems: 'center', 78 | paddingVertical: DEVICE.verticalScale(5), 79 | paddingHorizontal: DEVICE.horizontalScale(25), 80 | borderWidth: 2, 81 | }, 82 | title: { 83 | fontSize: DEVICE.horizontalScale(16), 84 | lineHeight: 24, 85 | textAlign: 'center', 86 | }, 87 | }) 88 | 89 | export default memo(Button) 90 | -------------------------------------------------------------------------------- /template/src/components/Row.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ActivityIndicator, 3 | Pressable, 4 | StyleProp, 5 | StyleSheet, 6 | Switch, 7 | TextStyle, 8 | View, 9 | ViewProps, 10 | ViewStyle, 11 | } from 'react-native' 12 | import type { PropsWithChildren } from 'react' 13 | 14 | import AppText from './AppText' 15 | 16 | import { Icons } from '../assets' 17 | import { COLORS, DEVICE } from '../styles/style-guide' 18 | 19 | interface PropsType { 20 | title?: string 21 | value?: boolean 22 | loading?: boolean 23 | onPress?: () => void 24 | style?: StyleProp 25 | testID?: ViewProps['testID'] 26 | titleStyle?: StyleProp 27 | icon?: typeof Icons[keyof typeof Icons] 28 | } 29 | 30 | const Row = ({ 31 | icon: Icon = Icons.Arrow, 32 | titleStyle, 33 | children, 34 | loading, 35 | onPress, 36 | testID, 37 | title, 38 | value, 39 | style, 40 | }: PropsWithChildren): JSX.Element => { 41 | const shouldRenderToggle = typeof value !== null && typeof value !== 'undefined' 42 | const selectable = !shouldRenderToggle && !onPress 43 | 44 | const renderToggle = () => onPress?.()} /> 45 | const renderIcon = () => 46 | 47 | return ( 48 | onPress?.()}> 53 | 54 | {title || children} 55 | 56 | 57 | {loading && ( 58 | 59 | 60 | 61 | )} 62 | {shouldRenderToggle ? renderToggle() : onPress ? renderIcon() : null} 63 | 64 | 65 | ) 66 | } 67 | 68 | const styles = StyleSheet.create({ 69 | container: { 70 | alignSelf: 'stretch', 71 | flexDirection: 'row', 72 | alignItems: 'center', 73 | justifyContent: 'space-between', 74 | paddingVertical: DEVICE.verticalScale(12), 75 | }, 76 | text: { 77 | maxWidth: '80%', 78 | }, 79 | iconContainer: { 80 | flexDirection: 'row', 81 | alignItems: 'center', 82 | justifyContent: 'center', 83 | }, 84 | loaderContainer: { 85 | marginRight: DEVICE.horizontalScale(10), 86 | }, 87 | icon: { 88 | transform: [{ rotate: '180deg' }], 89 | }, 90 | }) 91 | 92 | export default Row 93 | -------------------------------------------------------------------------------- /template/src/components/ScreenLoader.tsx: -------------------------------------------------------------------------------- 1 | import { memo, useEffect, useRef, useState } from 'react' 2 | import Animated, { Value } from 'react-native-reanimated' 3 | import { StyleSheet, ActivityIndicator, ViewProps } from 'react-native' 4 | 5 | import AnimatedWrapper, { SPRING_CONFIG } from './AnimatedWrapper' 6 | 7 | import { COLORS } from '../styles/style-guide' 8 | 9 | interface PropsType { 10 | shown: boolean 11 | loaderColor?: string 12 | backgroundColor?: string 13 | testID?: ViewProps['testID'] 14 | } 15 | 16 | const ScreenLoader = ({ loaderColor, backgroundColor: bgColor, testID, shown }: PropsType): JSX.Element | null => { 17 | const color = loaderColor || 'white' 18 | const backgroundColor = bgColor || COLORS.primary 19 | const transition = useRef(new Value(Number(shown))).current 20 | // NOTE: This is needed for Detox not go into an infinite loop because of the ongoing loader animation. 21 | const [shouldRender, setShouldRender] = useState(shown) 22 | 23 | useEffect(() => { 24 | if (shown) { 25 | if (!shouldRender) setShouldRender(true) 26 | Animated.spring(transition, { 27 | toValue: 1, 28 | ...SPRING_CONFIG, 29 | }).start() 30 | } else { 31 | Animated.spring(transition, { 32 | toValue: 0, 33 | ...SPRING_CONFIG, 34 | }).start(({ finished }) => { 35 | if (finished) setShouldRender(false) 36 | }) 37 | } 38 | }, [shouldRender, shown, transition]) 39 | 40 | if (!shouldRender) return null 41 | 42 | return ( 43 | 44 | 45 | 46 | 47 | 48 | ) 49 | } 50 | 51 | const styles = StyleSheet.create({ 52 | container: { 53 | ...StyleSheet.absoluteFillObject, 54 | alignItems: 'center', 55 | justifyContent: 'center', 56 | backgroundColor: COLORS.secondary, 57 | zIndex: 2, 58 | }, 59 | }) 60 | 61 | function propsAreEqual(prevProps: PropsType, nextProps: PropsType): boolean { 62 | return prevProps.shown === nextProps.shown 63 | } 64 | 65 | export default memo(ScreenLoader, propsAreEqual) 66 | -------------------------------------------------------------------------------- /template/src/config/app-config.ts: -------------------------------------------------------------------------------- 1 | import DeviceInfo from 'react-native-device-info' 2 | 3 | export const CONFIG = { 4 | /** 5 | * Backend URL 6 | * @constant 'https://rnct9852.free.beeceptor.com' 7 | */ 8 | END_POINT: 'https://rnct9852.free.beeceptor.com', 9 | /** 10 | * Boolean that indicates if the app is running with remote debugging enabled. 11 | */ 12 | IS_REMOTE_DEBUGGING: global?.location?.pathname?.includes('/debugger-ui') || global?.__REMOTEDEV__, 13 | /** 14 | * Function that indicates if the app is running the DEV target 15 | */ 16 | IS_DEV: (): boolean => { 17 | const bundleId = DeviceInfo.getBundleId() 18 | if (bundleId === 'com.appstarter.dev') return true 19 | return false 20 | }, 21 | /** 22 | * Function that indicates if the app is running the STAGING target 23 | */ 24 | IS_STAGING: (): boolean => { 25 | const bundleId = DeviceInfo.getBundleId() 26 | if (bundleId === 'com.appstarter.staging') return true 27 | return false 28 | }, 29 | /** 30 | * Function that indicates if the app is running the PROD target 31 | */ 32 | IS_PROD: (): boolean => { 33 | const bundleId = DeviceInfo.getBundleId() 34 | if (bundleId === 'com.appstarter.prod') return true 35 | return false 36 | }, 37 | } 38 | 39 | export default CONFIG 40 | -------------------------------------------------------------------------------- /template/src/config/e2e-config.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTE: GENERATED CODE -- DO NOT EDIT! 3 | */ 4 | 5 | import en from '../locales/en.json' 6 | import de from '../locales/de.json' 7 | 8 | const translations = { en, de } 9 | 10 | const E2E_CONFIG = { 11 | /** 12 | * Boolean that indicates if the app is running Detox and 13 | * therefore should use the mocked version of the code when 14 | * it exists. 15 | */ 16 | IS_MOCKING_ENABLED: false, 17 | /** 18 | * Name of the only test files to run. 19 | * Only meant for debugging, run all test cases by default. 20 | * @example ['login'] 21 | * @default undefined 22 | */ 23 | RUN_ONLY: undefined, 24 | } 25 | 26 | export const getE2EString = ( 27 | locale: Locale, 28 | fn: (strings: typeof translations[Locale]) => string | string[], 29 | ): string => { 30 | const item = fn(translations[locale]) 31 | let variable = null 32 | let string = '' 33 | 34 | if (typeof item === 'object') { 35 | string = item[0] 36 | variable = item[1] 37 | } else string = item 38 | 39 | return string.replace(/({{|@@)(.+?)(@@|}})/g, variable ?? '__NO VARIABLE ARGUMENT PROVIDED__').replace(/\*/g, '') 40 | } 41 | 42 | export default E2E_CONFIG 43 | -------------------------------------------------------------------------------- /template/src/core/app-core.ts: -------------------------------------------------------------------------------- 1 | import appStore from '../stores/app-store' 2 | 3 | import type { AppType } from '../types/store-types' 4 | 5 | const updateApp = appStore.getState().update 6 | 7 | // NOTE: Make sure to always write tests for every new method you add 8 | class App { 9 | /** 10 | * Updates the app store 11 | * 12 | * @param data - Object to update the app store with 13 | */ 14 | update = (data: Partial): void => { 15 | updateApp(app => { 16 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 17 | app = Object.assign(app, data) 18 | }) 19 | } 20 | } 21 | 22 | export default new App() 23 | -------------------------------------------------------------------------------- /template/src/core/core.ts: -------------------------------------------------------------------------------- 1 | import app from './app-core' 2 | import user from './user-core' 3 | import events from './events-core' 4 | 5 | class Core { 6 | /** 7 | * Used to handle data general to the whole app functioning 8 | */ 9 | app = app 10 | 11 | /** 12 | * Used to handle user data 13 | */ 14 | user = user 15 | 16 | /** 17 | * Used to handle events emission/subscription 18 | */ 19 | events = events 20 | } 21 | 22 | export default new Core() 23 | -------------------------------------------------------------------------------- /template/src/core/events-core.ts: -------------------------------------------------------------------------------- 1 | import mitt, { EventHandlerMap, Handler } from 'mitt' 2 | 3 | type EventMessage = Record | string | number 4 | 5 | // NOTE: Make sure to always write tests for every new method you add. 6 | 7 | /** 8 | * This is a Vue inspired global event handler. 9 | * You can send events with specific keys, and then listen to those keys elsewhere. 10 | * @example DrawerMenu.tsx => core.events.send('drawer_menu_pan', { panX, panY }) 11 | * @example Home.tsx => core.events.listen('drawer_menu_pan', (message) => console.log(message)) // An now you can animate home screen easily in sync with drawer for ie. 12 | */ 13 | class Events { 14 | emitter = mitt<{ [key: string]: EventMessage }>() 15 | 16 | /** 17 | * Sends a message to a given channel. 18 | * @param channel - `string`— Channel to send the message toStrictEqual. 19 | * @param message - `Record`— Data to send. 20 | * @example DrawerMenu.tsx => core.events.send('drawer_menu_pan', { panX, panY }) 21 | */ 22 | send = (channel: string, message: EventMessage): void => { 23 | this.emitter.emit(channel, message) 24 | } 25 | 26 | /** 27 | * Listens to a given channel. 28 | * @param channel - `string`— Channel to listen to. 29 | * @param onMessage - `Handler`— Listen to call when a message is received. 30 | * @example Home.tsx => core.events.listen('drawer_menu_pan', (message) => console.log(message)) // An now you can animate home screen easily in sync with drawer for ie. 31 | */ 32 | listen = (channel: string, onMessage: Handler): void => { 33 | this.emitter.on(channel, onMessage) 34 | } 35 | 36 | /** 37 | * Clears all the events listeners. 38 | * @returns `EventHandlerMap`— The empty event handler Map. 39 | */ 40 | clearAll = (): EventHandlerMap<{ [key: string]: EventMessage }> => { 41 | this.emitter.all.clear() 42 | return this.emitter.all 43 | } 44 | } 45 | 46 | export default new Events() 47 | -------------------------------------------------------------------------------- /template/src/core/user-core.ts: -------------------------------------------------------------------------------- 1 | import { stores } from '../stores/stores' 2 | import userStore from '../stores/user-store' 3 | 4 | import type { UserType } from '../types/store-types' 5 | 6 | const updateUser = userStore.getState().update 7 | 8 | // NOTE: Make sure to always write tests for every new method you add 9 | class User { 10 | /** 11 | * Updates the user store 12 | * 13 | * @param data - Object to update the user store with 14 | */ 15 | update = (data: Partial): void => { 16 | updateUser(user => { 17 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 18 | user = Object.assign(user, data) 19 | }) 20 | } 21 | 22 | /** 23 | * Returns if user is logged in. Uses ID from user store to determine logged in state 24 | */ 25 | isLoggedIn = (): boolean => Boolean(userStore.getState().data.UID) 26 | 27 | /** 28 | * Reset stores when logging out 29 | */ 30 | logout = () => stores.reset() 31 | } 32 | 33 | export default new User() 34 | -------------------------------------------------------------------------------- /template/src/locales/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "activity": "Aktivität", 4 | "home": "Home", 5 | "settings": "Einstellungen", 6 | "tips": "Tips", 7 | "logout": "Abmelden" 8 | }, 9 | "errors": { 10 | "requiredField": "Dieses Feld ist erforderlich", 11 | "passwordTooLong": "Das Passwort muss aus unter 30 Zeichen bestehen.", 12 | "invalidEmail": "Tut mir leid, *{{value}}* ist keine gültige E-Mail-Adresse. Bitte überprüfen Sie die E-Mail-Adresse, die Sie eingegeben haben, und versuchen Sie es noch mal.", 13 | "passwordRules": "Das Passwort muss *mindestens aus acht Zeichen* bestehen und drei der folgenden Zeichen enthalten: *Großbuchstabe, Kleinbuchstabe, Zahl und/oder Sonderzeichen*." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /template/src/locales/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "general": { 3 | "activity": "Activity", 4 | "home": "Home", 5 | "settings": "Settings", 6 | "tips": "Tips", 7 | "logout": "Log out" 8 | }, 9 | "errors": { 10 | "requiredField": "This field is required", 11 | "passwordTooLong": "Password needs to be less than 30 characters long.", 12 | "invalidEmail": "Sorry, *{{value}}* is not a valid email address. Please double check the email you've entered and try again.", 13 | "passwordRules": "Your password must be *at least 8 characters long*, with at least three of the following kinds of characters: *uppercase, lowercase, number, and/or symbols*." 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /template/src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import * as RNLocalize from 'react-native-localize' 2 | import I18n from 'i18n-js' 3 | 4 | import en from './en.json' 5 | import de from './de.json' 6 | 7 | const translations = { en, de } 8 | 9 | I18n.translations = translations 10 | I18n.fallbacks = true 11 | 12 | const fallback = { languageTag: 'en', isRTL: false } 13 | const { languageTag } = RNLocalize.findBestAvailableLanguage(Object.keys(I18n.translations)) || fallback 14 | 15 | I18n.locale = languageTag 16 | 17 | export const currentLocale = I18n.currentLocale() 18 | export const getLocalizedString = (value: string): string => 19 | I18n.t(value) || `[missing "${currentLocale}.${value}" translation]` 20 | export const getLocalizedStringWithParam = (name: string, value: string): string => I18n.t(name, { value }) 21 | export const getLocalizedStringWithParams = (name: string, values: Record): string => 22 | I18n.t(name, { ...values }) 23 | -------------------------------------------------------------------------------- /template/src/modals/AlertModal.tsx: -------------------------------------------------------------------------------- 1 | import { Image, StyleSheet, View } from 'react-native' 2 | import type { ModalComponentProp } from 'react-native-modalfy' 3 | 4 | import type { ModalsParamsType } from '../types/modals-types' 5 | 6 | import ModalContainer from './ModalContainer' 7 | import AppText from '../components/AppText' 8 | 9 | type PropsType = ModalComponentProp 10 | 11 | const AlertModal = ({ modal: { params = {} as ModalsParamsType['AlertModal'] } }: PropsType): JSX.Element => { 12 | const { 13 | titleColor, 14 | imageStyle, 15 | noAction, 16 | noHeader, 17 | message, 18 | testIDs, 19 | actions, 20 | title, 21 | image, 22 | closeBehavior = 'pop', 23 | } = params 24 | 25 | return ( 26 | 34 | {/* NOTE: ILLUSTRATION */} 35 | {image && ( 36 | 37 | 38 | 39 | 40 | 41 | )} 42 | 43 | {/* NOTE: MESSAGE */} 44 | 45 | 46 | {message} 47 | 48 | 49 | 50 | ) 51 | } 52 | 53 | const styles = StyleSheet.create({ 54 | illustrationWrapper: { 55 | alignSelf: 'center', 56 | flexDirection: 'row', 57 | paddingTop: 24, 58 | alignItems: 'center', 59 | }, 60 | illustrationContainer: { 61 | flex: 1, 62 | width: 128, 63 | height: 128, 64 | alignSelf: 'center', 65 | flexDirection: 'column', 66 | paddingTop: 24, 67 | justifyContent: 'center', 68 | alignItems: 'center', 69 | marginBottom: 26, 70 | }, 71 | illustrationImage: { 72 | width: 128, 73 | height: 128, 74 | }, 75 | messageContainer: { 76 | alignSelf: 'center', 77 | marginTop: 41, 78 | }, 79 | messageText: { 80 | textAlign: 'center', 81 | lineHeight: 24, 82 | }, 83 | }) 84 | 85 | export default AlertModal 86 | -------------------------------------------------------------------------------- /template/src/modals/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AlertModal } from './AlertModal' 2 | -------------------------------------------------------------------------------- /template/src/navigation/AuthStack.tsx: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from '@react-navigation/stack' 2 | 3 | import type { AuthStackParamsType } from '../types/navigation-types' 4 | 5 | import Welcome from '../screens/welcome/Welcome' 6 | 7 | const AuthStack = createStackNavigator() 8 | 9 | export default (): JSX.Element => ( 10 | 11 | 12 | {/* NOTE: You can add the auth screens here */} 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /template/src/navigation/SettingsStack.tsx: -------------------------------------------------------------------------------- 1 | import { createStackNavigator } from '@react-navigation/stack' 2 | 3 | import type { SettingsStackParamsType } from '../types/navigation-types' 4 | 5 | import Settings from '../screens/settings/Settings' 6 | 7 | const SettingsStack = createStackNavigator() 8 | 9 | export default (): JSX.Element => ( 10 | 11 | 12 | {/* NOTE: You can add the profile screens here */} 13 | 14 | ) 15 | -------------------------------------------------------------------------------- /template/src/navigation/TabBar.tsx: -------------------------------------------------------------------------------- 1 | import { View, Pressable, PressableProps, StyleSheet } from 'react-native' 2 | import type { BottomTabBarProps, BottomTabNavigationOptions } from '@react-navigation/bottom-tabs' 3 | 4 | import { Icons, IconType } from '../assets' 5 | 6 | import AppText from '../components/AppText' 7 | 8 | import { getLocalizedString } from '../locales' 9 | import { COLORS, DEVICE } from '../styles/style-guide' 10 | 11 | interface TabBarItemType { 12 | isFocused: boolean 13 | routeName: keyof typeof LABELS 14 | onPress: PressableProps['onPress'] 15 | options: BottomTabNavigationOptions 16 | onLongPress: PressableProps['onLongPress'] 17 | } 18 | 19 | const LABELS = { 20 | home: getLocalizedString('general.home'), 21 | tips: getLocalizedString('general.tips'), 22 | activity: getLocalizedString('general.activity'), 23 | settingsstack: getLocalizedString('general.settings'), 24 | } 25 | 26 | const getIcon = (routeName: keyof typeof LABELS): IconType => { 27 | switch (routeName) { 28 | case 'home': 29 | return Icons.Home 30 | case 'activity': 31 | return Icons.Activity 32 | case 'settingsstack': 33 | return Icons.Settings 34 | case 'tips': 35 | return Icons.Tips 36 | default: 37 | return Icons.Settings 38 | } 39 | } 40 | 41 | const TabBarItem = ({ isFocused, routeName, onLongPress, onPress, options }: TabBarItemType): JSX.Element => { 42 | const color = isFocused ? COLORS.secondary : COLORS.icon 43 | const Icon = getIcon(routeName) 44 | return ( 45 | 53 | 54 | 55 | {LABELS[routeName]} 56 | 57 | 58 | ) 59 | } 60 | 61 | const TabBar = ({ state, descriptors, navigation }: BottomTabBarProps): JSX.Element | null => ( 62 | 63 | {state.routes.map((route, index) => { 64 | const { options } = descriptors[route.key] 65 | const label = 66 | options.tabBarLabel !== undefined 67 | ? options.tabBarLabel 68 | : options.title !== undefined 69 | ? options.title 70 | : route.name 71 | 72 | const isFocused = state.index === index 73 | 74 | const onPress = (): void => { 75 | const event = navigation.emit({ 76 | type: 'tabPress', 77 | target: route.key, 78 | canPreventDefault: true, 79 | }) 80 | 81 | if (!isFocused && !event.defaultPrevented) { 82 | navigation.navigate(route.name) 83 | } 84 | } 85 | 86 | const onLongPress = (): void => { 87 | navigation.emit({ 88 | type: 'tabLongPress', 89 | target: route.key, 90 | }) 91 | } 92 | 93 | return ( 94 | 105 | ) 106 | })} 107 | 108 | ) 109 | 110 | const styles = StyleSheet.create({ 111 | container: { 112 | flexDirection: 'row', 113 | width: DEVICE.vw(100), 114 | height: DEVICE.hasNotch ? 49 + DEVICE.indicatorPadding : 55, 115 | alignItems: 'center', 116 | justifyContent: 'center', 117 | backgroundColor: 'white', 118 | elevation: 5, 119 | shadowColor: '#000', 120 | shadowOffset: { width: 0, height: 3 }, 121 | shadowOpacity: 0.16, 122 | shadowRadius: 12, 123 | }, 124 | tab: { 125 | width: DEVICE.vw(100 / Object.keys(LABELS).length), 126 | height: 75, 127 | alignItems: 'center', 128 | justifyContent: DEVICE.hasNotch ? 'flex-start' : 'center', 129 | paddingTop: DEVICE.hasNotch ? 5 : 0, 130 | }, 131 | }) 132 | 133 | export default TabBar 134 | -------------------------------------------------------------------------------- /template/src/screens/activity/Activity.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View } from 'react-native' 2 | 3 | import AppText from '../../components/AppText' 4 | 5 | import { getLocalizedString } from '../../locales' 6 | 7 | const Activity = (): JSX.Element => ( 8 | 9 | {getLocalizedString('general.activity')} 10 | 11 | ) 12 | 13 | const styles = StyleSheet.create({ 14 | container: { 15 | flex: 1, 16 | justifyContent: 'center', 17 | alignItems: 'center', 18 | }, 19 | }) 20 | 21 | export default Activity 22 | -------------------------------------------------------------------------------- /template/src/screens/home/Home.tsx: -------------------------------------------------------------------------------- 1 | import { StyleSheet, View } from 'react-native' 2 | import { useModal } from 'react-native-modalfy' 3 | 4 | import type { ModalsParamsType } from '../../types/modals-types' 5 | 6 | import AppText from '../../components/AppText' 7 | import Button from '../../components/Button' 8 | 9 | import { Assets } from '../../assets' 10 | import { DEVICE } from '../../styles/style-guide' 11 | import { getLocalizedString } from '../../locales' 12 | 13 | const Home = (): JSX.Element => { 14 | const { openModal } = useModal() 15 | return ( 16 | 17 | 18 | {getLocalizedString('general.home')} 19 | 20 |