diff --git a/.buckconfig b/.buckconfig new file mode 100644 index 00000000000..934256cb29d --- /dev/null +++ b/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/.env.production b/.env.production new file mode 100644 index 00000000000..da495ca71fc --- /dev/null +++ b/.env.production @@ -0,0 +1,24 @@ +ENV='production' + +VERSION='1.0.0' +BUILD='1' + +# Feature Toggling +FEATURE_FLAG_CHEAT_CODES=false +FEATURE_FLAG_CODE_PUSH=true +FEATURE_FLAG_CODE_PUSH_MANUAL=false + +# iOS +IOS_APP_ID='tech.bam.passculture' +IOS_APP_NAME='PassCulture' +IOS_PROVISIONING_PROFILE_SPECIFIER_DEVELOPMENT='' +IOS_PROVISIONING_PROFILE_SPECIFIER_RELEASE='' + +# Android +ANDROID_APP_ID='tech.bam.passculture' +ANDROID_APP_NAME='PassCulture' + +# CodePush +CODEPUSH_KEY_ANDROID='' +CODEPUSH_KEY_IOS='' +CODEPUSH_DEPLOYMENT_NAME='Production' diff --git a/.env.staging b/.env.staging new file mode 100644 index 00000000000..a55c0e87a09 --- /dev/null +++ b/.env.staging @@ -0,0 +1,24 @@ +ENV='staging' + +VERSION='1.0.0' +BUILD='1' + +# Feature Toggling +FEATURE_FLAG_CHEAT_CODES=true +FEATURE_FLAG_CODE_PUSH=true +FEATURE_FLAG_CODE_PUSH_MANUAL=true + +# iOS +IOS_APP_ID='tech.bam.passculture.staging' +IOS_APP_NAME='PassCulture S' +IOS_PROVISIONING_PROFILE_SPECIFIER_DEVELOPMENT='match Development tech.bam.passculture.staging' +IOS_PROVISIONING_PROFILE_SPECIFIER_RELEASE='match InHouse tech.bam.passculture.staging' + +# Android +ANDROID_APP_ID='com.passculture' +ANDROID_APP_NAME='PassCulture S' + +# CodePush +CODEPUSH_KEY_ANDROID='' +CODEPUSH_KEY_IOS='' +CODEPUSH_DEPLOYMENT_NAME='Staging' diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000000..7744e56b393 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +node_modules +package.json +rn-cli.config.js +.eslintrc.js +android +ios +__mocks__ diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000000..b58b6cc65fb --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,50 @@ +module.exports = { + plugins: ['react-native'], + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + extends: [ + 'eslint:recommended', + 'plugin:react/recommended', // Uses the recommended rules from @eslint-plugin-react + 'plugin:react-native/all', // Enables all rules from react-native + 'plugin:@typescript-eslint/recommended', // Uses the recommended rules from @typescript-eslint/eslint-plugin + 'plugin:prettier/recommended', // Enables eslint-plugin-prettier and displays prettier errors as ESLint errors. Make sure this is always the last configuration in the extends array. + ], + parserOptions: { + ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', // Allows for the use of imports + ecmaFeatures: { + jsx: true, // Allows for the parsing of JSX + }, + }, + rules: { + // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs + // e.g. "@typescript-eslint/explicit-function-return-type": "off", + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + 'react/prop-types': 'off', + 'react-native/sort-styles': 'off', + 'no-console': ['error', { allow: ['warn', 'error'] }], + }, + settings: { + react: { + version: 'detect', // Tells eslint-plugin-react to automatically detect the version of React to use + }, + }, + env: { + 'react-native/react-native': true, + }, + // Glob based definitions + overrides: [ + { + files: ['**/*.test.ts', '**/*.test.tsx'], + env: { + jest: true, + }, + rules: { + '@typescript-eslint/explicit-function-return-type': 'off', + }, + }, + ], +}; diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..008a98312ba --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.pbxproj -text +*.keystore.properties filter=crypt diff=crypt +*.secret filter=crypt diff=crypt diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000000..c720639c382 --- /dev/null +++ b/.gitignore @@ -0,0 +1,76 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +/fastlane/report.xml +/fastlane/Preview.html +/fastlane/screenshots + +# Bundle artifact +*.jsbundle + +# CocoaPods +/ios/Pods + +# JEST +.jest/ +coverage/ + +/dist +*.back + +# ESLint +.eslintcache + +# @react-native-config codegen +ios/react-native-config.xcconfig +ios/envfile + +# Lingui +src/locales/_build +src/locales/*/*.js \ No newline at end of file diff --git a/.linguirc b/.linguirc new file mode 100644 index 00000000000..8a58d133b19 --- /dev/null +++ b/.linguirc @@ -0,0 +1,9 @@ +{ + "localeDir": "src/locales/", + "srcPathDirs": ["src/"], + "srcPathIgnorePatterns": [ + "/__tests__/" + ], + "format": "po", + "sourceLocale": "en" +} diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..6c7b69a0156 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +.gitignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000000..82259fe768c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +node_modules +package.json diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..4cc6aa62289 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "trailingComma": "es5", + "singleQuote": true, + "printWidth": 100 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..e83a8102a0c --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "editor.formatOnSave": true, + "prettier.requireConfig": true, + "eslint.enable": true, + "eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "files.associations": { + "Fastfile*": "ruby", + "Appfile": "ruby", + "Matchfile": "ruby", + "yarn.lock": "text", + ".env*": "dotenv" + }, + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/.watchmanconfig b/.watchmanconfig new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/Gemfile b/Gemfile new file mode 100644 index 00000000000..b38f3c1ff33 --- /dev/null +++ b/Gemfile @@ -0,0 +1,7 @@ +source "https://rubygems.org" + +gem "fastlane" +gem "cocoapods" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 00000000000..c753fd9943c --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,221 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.2) + activesupport (4.2.11.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + addressable (2.7.0) + public_suffix (>= 2.0.2, < 5.0) + algoliasearch (1.27.1) + httpclient (~> 2.8, >= 2.8.3) + json (>= 1.5.1) + atomos (0.1.3) + babosa (1.0.3) + claide (1.0.3) + cocoapods (1.8.4) + activesupport (>= 4.0.2, < 5) + claide (>= 1.0.2, < 2.0) + cocoapods-core (= 1.8.4) + cocoapods-deintegrate (>= 1.0.3, < 2.0) + cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-plugins (>= 1.0.0, < 2.0) + cocoapods-search (>= 1.0.0, < 2.0) + cocoapods-stats (>= 1.0.0, < 2.0) + cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-try (>= 1.1.0, < 2.0) + colored2 (~> 3.1) + escape (~> 0.0.4) + fourflusher (>= 2.3.0, < 3.0) + gh_inspector (~> 1.0) + molinillo (~> 0.6.6) + nap (~> 1.0) + ruby-macho (~> 1.4) + xcodeproj (>= 1.11.1, < 2.0) + cocoapods-core (1.8.4) + activesupport (>= 4.0.2, < 6) + algoliasearch (~> 1.0) + concurrent-ruby (~> 1.1) + fuzzy_match (~> 2.0.4) + nap (~> 1.0) + cocoapods-deintegrate (1.0.4) + cocoapods-downloader (1.3.0) + cocoapods-plugins (1.0.0) + nap + cocoapods-search (1.0.0) + cocoapods-stats (1.1.0) + cocoapods-trunk (1.4.1) + nap (>= 0.8, < 2.0) + netrc (~> 0.11) + cocoapods-try (1.1.0) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + concurrent-ruby (1.1.5) + declarative (0.0.10) + declarative-option (0.1.0) + digest-crc (0.4.1) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.5) + emoji_regex (1.0.1) + escape (0.0.4) + excon (0.72.0) + faraday (0.17.3) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.7) + fastlane (2.141.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 0.17) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.13.1) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.29.2, < 0.37.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.3.0, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-appcenter (1.8.0) + fourflusher (2.3.1) + fuzzy_match (2.0.4) + gh_inspector (1.1.3) + google-api-client (0.36.4) + addressable (~> 2.5, >= 2.5.1) + googleauth (~> 0.9) + httpclient (>= 2.8.1, < 3.0) + mini_mime (~> 1.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.12) + google-cloud-core (1.5.0) + google-cloud-env (~> 1.0) + google-cloud-errors (~> 1.0) + google-cloud-env (1.3.0) + faraday (~> 0.11) + google-cloud-errors (1.0.0) + google-cloud-storage (1.25.1) + addressable (~> 2.5) + digest-crc (~> 0.4) + google-api-client (~> 0.33) + google-cloud-core (~> 1.2) + googleauth (~> 0.9) + mini_mime (~> 1.0) + googleauth (0.10.0) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.12) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + i18n (0.9.5) + concurrent-ruby (~> 1.0) + json (2.3.0) + jwt (2.1.0) + memoist (0.16.2) + mini_magick (4.10.1) + mini_mime (1.0.2) + minitest (5.14.0) + molinillo (0.6.6) + multi_json (1.14.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + nap (1.1.0) + naturally (2.2.0) + netrc (0.11.0) + os (1.0.1) + plist (3.5.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + ruby-macho (1.4.0) + rubyzip (1.3.0) + security (0.1.3) + signet (0.12.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.8) + CFPropertyList + naturally + slack-notifier (2.3.2) + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + thread_safe (0.3.6) + tty-cursor (0.7.1) + tty-screen (0.7.1) + tty-spinner (0.9.3) + tty-cursor (~> 0.7) + tzinfo (1.2.6) + thread_safe (~> 0.1) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.6) + unicode-display_width (1.6.1) + word_wrap (1.0.0) + xcodeproj (1.15.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + cocoapods + fastlane + fastlane-plugin-appcenter + +BUNDLED WITH + 2.1.2 diff --git a/README.md b/README.md index 56ac3cdcebc..e322f2f1357 100644 --- a/README.md +++ b/README.md @@ -1 +1,143 @@ -# pass-culture-app-native \ No newline at end of file +# PassCulture + +This project and readme have been auto generated by [react-native-make](https://github.com/bamlab/react-native-make) + +## Getting Started + +### Requirements + +Make sure you have installed: + +- Git +- XCode +- Android SDK +- node + - yarn + - react-native-cli +- ruby + - bundler (`sudo gem install bundler`) +- transcrypt (`brew install transcrypt`) + +### Installation + +```bash +git clone git@github.com:bamlab/project-name.git +cd project-name +bundle install +yarn +bundle exec pod repo update +cd ios && bundle exec pod install && cd .. +``` + +### Configuration + +Ask the password to a BAM's architect. + +``` +transcrypt -c aes-256-cbc -p '' +``` + +### Run the app + +Launch the app in your simulator: + +- iOS: `react-native run-ios --scheme PassCulture-Staging` +- Android: `react-native run-android --variant=stagingDebug` + +## Features + +When you generated the repository with [react-native-make](https://github.com/bamlab/react-native-make) the following feature must be present: + +- A repository should have been created at https://github.com/bamlab/project-name +- An application have been created and deployed on appcenter +- You can deploy automatically with fastlane to appcenter +- You can deploy with codepush to staging too +- We installed the following libraries for you: + - @react-native-community/async-storage + - react-native-localize + - lingui-js + - typesafe-actions + - jest + - prettier + - eslint + +## Finding specific features location + +To find where a feature is installed / used, run a search on the whole project using the tag + +| Name |  Tag | +| ----------------------- | ------------------------ | +| Translations | @translations | +| Redux | @redux | +| Redux Persist | @redux-persist | +| Redux Persist Sensitive | @redux-persist-sensitive | +| React Navigation | @react-navigation | +| CodePush | @codepush | +| Storybook | @storybook | + +## Testing + +You can run the tests with `yarn test`. This command will: + +- Run `eslint` on your project +- Check the typescript types +- Run the `jest` tests + +You can run the jest tests in watch mode with: + +```bash +yarn jest --watch +``` + +You can also get the coverage with: + +```bash +yarn jest --coverage +``` + +## Automatic generation + +You can automatically generate files by running the following commands: + +- `yarn plop atom` : generate an empty atom +- `yarn plop module` : generate a Redux module whose state contains `value` +- `yarn plop page` : generate an empty page + +## Deploy + +A staging environment have been created for you. This environment push the application on Appcenter. + +You can find the pushed app at: + +- https://appcenter.ms/orgs/BAM.tech/apps/passculture-staging-ios +- https://appcenter.ms/orgs/BAM.tech/apps/passculture-staging-android/ + +To deploy in staging, just run + +``` +./scripts/deploy.sh -t hard +``` + +There is few options on the deploy command. Please run the following command to have all usages: + +``` +./scripts/deploy.sh -h +``` + +### CodePush + +Most of the time, on staging, you didn't change anything in the native code. If you changed only javascript code, you can run: + +``` +./scripts/deploy.sh +``` + +Then the build should be faster as only the javascript code have been published. + +To download updates from the application, just open it and click on the "check update" button. + +## Troubleshooting + +### Android: No value has been specified for property 'manifestOutputDirectory'. + +In Android Studio: File > Settings > Experimental > Gradle -> uncheck "Only sync the active variant" checkbox. diff --git a/__mocks__/@bam.tech/react-native-config.ts b/__mocks__/@bam.tech/react-native-config.ts new file mode 100644 index 00000000000..56bf55ff188 --- /dev/null +++ b/__mocks__/@bam.tech/react-native-config.ts @@ -0,0 +1 @@ +export default () => ({}); diff --git a/__mocks__/@react-native-community/async-storage.ts b/__mocks__/@react-native-community/async-storage.ts new file mode 100644 index 00000000000..c04eec53260 --- /dev/null +++ b/__mocks__/@react-native-community/async-storage.ts @@ -0,0 +1 @@ +export { default } from '@react-native-community/async-storage/jest/async-storage-mock'; diff --git a/__mocks__/@storybook/react-native.tsx b/__mocks__/@storybook/react-native.tsx new file mode 100644 index 00000000000..e3b900878de --- /dev/null +++ b/__mocks__/@storybook/react-native.tsx @@ -0,0 +1,7 @@ +import React from 'react'; // @storybook +import { View } from 'react-native'; + +const configure = jest.fn(); +const getStorybookUI = jest.fn().mockImplementation(() => () => ); + +export { configure, getStorybookUI }; diff --git a/__mocks__/react-native-code-push.ts b/__mocks__/react-native-code-push.ts new file mode 100644 index 00000000000..eca2348ead8 --- /dev/null +++ b/__mocks__/react-native-code-push.ts @@ -0,0 +1,30 @@ +const getUpdateMetadata = jest.fn(() => + Promise.resolve({ label: 'label', description: 'decription' }) +); +const sync = jest.fn(); +const InstallMode = { + IMMEDIATE: 0, +}; +const SyncStatus = { + UP_TO_DATE: 'UP_TO_DATE', + UPDATE_INSTALLED: 'UPDATE_INSTALLED', + UPDATE_IGNORED: 'UPDATE_IGNORED', + UNKNOWN_ERROR: 'UNKNOWN_ERROR', + SYNC_IN_PROGRESS: 'SYNC_IN_PROGRESS', + CHECKING_FOR_UPDATE: 'CHECKING_FOR_UPDATE', + AWAITING_USER_ACTION: 'AWAITING_USER_ACTION', + DOWNLOADING_PACKAGE: 'DOWNLOADING_PACKAGE', + INSTALLING_UPDATE: 'INSTALLING_UPDATE', +}; +const CheckFrequency = { MANUAL: 'MANUAL' }; + +const CodePush = jest.fn(() => app => app); + +export default Object.assign(CodePush, { + getUpdateMetadata, + sync, + InstallMode, + SyncStatus, + CheckFrequency, + CodePush, +}); diff --git a/__mocks__/react-native-localize.ts b/__mocks__/react-native-localize.ts new file mode 100644 index 00000000000..aba807808e7 --- /dev/null +++ b/__mocks__/react-native-localize.ts @@ -0,0 +1,20 @@ +class RNLocalize { + static registeredHandler = () => {}; + static findBestAvailableLanguage = jest.fn(() => undefined); + + static addEventListener = jest.fn((_, handler) => { + RNLocalize.registeredHandler = handler; + }); + static removeEventListener = jest.fn(); + + static simulateLocalizationChange = () => { + RNLocalize.registeredHandler(); + }; + + static findBestAvailableLanguage = jest.fn(() => ({ + languageTag: 'en', + isRTL: false, + })); +} + +module.exports = RNLocalize; diff --git a/__mocks__/react-native-maps.ts b/__mocks__/react-native-maps.ts new file mode 100644 index 00000000000..7fb808dd9f0 --- /dev/null +++ b/__mocks__/react-native-maps.ts @@ -0,0 +1,27 @@ +const React = require.requireActual('react'); +const MapView = require.requireActual('react-native-maps'); + +class MockCallout extends React.Component { + render() { + return React.createElement('Callout', this.props, this.props.children); + } +} + +class MockMarker extends React.Component { + render() { + return React.createElement('Marker', this.props, this.props.children); + } +} + +class MockMapView extends React.Component { + render() { + return React.createElement('MapView', this.props, this.props.children); + } +} + +MockCallout.propTypes = MapView.Callout.propTypes; +MockMarker.propTypes = MapView.Marker.propTypes; +MockMapView.propTypes = MapView.propTypes; +MockMapView.Marker = MockMarker; +MockMapView.Callout = MockCallout; +module.exports = MockMapView; diff --git a/__mocks__/redux-persist-sensitive-storage.ts b/__mocks__/redux-persist-sensitive-storage.ts new file mode 100644 index 00000000000..b548227cc29 --- /dev/null +++ b/__mocks__/redux-persist-sensitive-storage.ts @@ -0,0 +1,3 @@ +import AsyncStorage from '@react-native-community/async-storage'; + +export default () => AsyncStorage; diff --git a/android/app/BUCK b/android/app/BUCK new file mode 100644 index 00000000000..57a39253be6 --- /dev/null +++ b/android/app/BUCK @@ -0,0 +1,55 @@ +# To learn about Buck see [Docs](https://buckbuild.com/). +# To run your application with Buck: +# - install Buck +# - `npm start` - to start the packager +# - `cd android` +# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` +# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck +# - `buck install -r android/app` - compile, install and run application +# + +load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") + +lib_deps = [] + +create_aar_targets(glob(["libs/*.aar"])) + +create_jar_targets(glob(["libs/*.jar"])) + +android_library( + name = "all-libs", + exported_deps = lib_deps, +) + +android_library( + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], +) + +android_build_config( + name = "build_config", + package = "com.passculture", +) + +android_resource( + name = "res", + package = "com.passculture", + res = "src/main/res", +) + +android_binary( + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], +) diff --git a/android/app/build.gradle b/android/app/build.gradle new file mode 100644 index 00000000000..6681786d196 --- /dev/null +++ b/android/app/build.gradle @@ -0,0 +1,249 @@ +// Template last updated for 0.60.3 + +apply plugin: "com.android.application" + +// @react-native-config +project.ext.envConfigFiles = [ + stagingdebug: ".env.staging", + stagingrelease: ".env.staging", + productiondebug: ".env.production", + productionrelease: ".env.production", + "": ".env.staging", // Default (must be the last in the list so as not to override others) +] +apply from: project(':bam.tech_react-native-config').projectDir.getPath() + "/dotenv.gradle" + +import com.android.build.OutputFile + +/** + * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets + * and bundleReleaseJsAndAssets). + * These basically call `react-native bundle` with the correct arguments during the Android build + * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the + * bundle directly from the development server. Below you can see all the possible configurations + * and their defaults. If you decide to add a configuration block, make sure to add it before the + * `apply from: "../../node_modules/react-native/react.gradle"` line. + * + * project.ext.react = [ + * // the name of the generated asset file containing your JS bundle + * bundleAssetName: "index.android.bundle", + * + * // the entry file for bundle generation. If none specified and + * // "index.android.js" exists, it will be used. Otherwise "index.js" is + * // default. Can be overridden with ENTRY_FILE environment variable. + * + * // https://reactnative.dev/docs/performance#enable-the-ram-format + * bundleCommand: "ram-bundle", + * + * // whether to bundle JS and assets in debug mode + * bundleInDebug: false, + * + * // whether to bundle JS and assets in release mode + * bundleInRelease: true, + * + * // whether to bundle JS and assets in another build variant (if configured). + * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants + * // The configuration property can be in the following formats + * // 'bundleIn${productFlavor}${buildType}' + * // 'bundleIn${buildType}' + * // bundleInFreeDebug: true, + * // bundleInPaidRelease: true, + * // bundleInBeta: true, + * + * // whether to disable dev mode in custom build variants (by default only disabled in release) + * // for example: to disable dev mode in the staging build type (if configured) + * devDisabledInStaging: true, + * // The configuration property can be in the following formats + * // 'devDisabledIn${productFlavor}${buildType}' + * // 'devDisabledIn${buildType}' + * + * // the root of your project, i.e. where "package.json" lives + * root: "../../", + * + * // where to put the JS bundle asset in debug mode + * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", + * + * // where to put the JS bundle asset in release mode + * jsBundleDirRelease: "$buildDir/intermediates/assets/release", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in debug mode + * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in release mode + * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", + * + * // by default the gradle tasks are skipped if none of the JS files or assets change; this means + * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to + * // date; if you have any other folders that you want to ignore for performance reasons (gradle + * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ + * // for example, you might want to remove it from here. + * inputExcludes: ["android/**", "ios/**"], + * + * // override which node gets called and with what additional arguments + * nodeExecutableAndArgs: ["node"], + * + * // supply additional arguments to the packager + * extraPackagerArgs: [] + * ] + */ + +project.ext.react = [ + enableHermes: false, // clean and rebuild if changing +] + +apply from: "../../node_modules/react-native/react.gradle" +apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" // @codepush + +/** + * Set this to true to create two separate APKs instead of one: + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = false + +/** + * The preferred build flavor of JavaScriptCore. + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +/** + * Whether to enable the Hermes VM. + * + * This should be set on project.ext.react and mirrored here. If it is not set + * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode + * and the benefits of using Hermes will therefore be sharply reduced. + */ +def enableHermes = project.ext.react.get("enableHermes", false); + +/** + * Signing + * + * The app is signed with keystore located in the keystores/ folder. + * Keystores passwords are crypted with transcrypt and located in + * keystores/*.keystore.properties files. + */ + def keystorePropertiesFile = rootProject.file("keystores/${project.env.get("ENV")}.keystore.properties") +def keystoreProperties = new Properties() +keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + defaultConfig { + applicationId project.env.get("ANDROID_APP_ID") + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + resValue "string", "app_name", project.env.get("ANDROID_APP_NAME") + resValue "string", "build_config_package", "com.passculture" // @react-native-config + versionCode (project.env.get("BUILD") as Integer) + versionName project.env.get("VERSION") + buildConfigField "String", "CODEPUSH_KEY", "\"${project.env.get("CODEPUSH_KEY_ANDROID")}\"" // @codepush + multiDexEnabled true + } + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" + } + } + signingConfigs { + config { + keyAlias keystoreProperties['keyAlias'] + keyPassword keystoreProperties['keyPassword'] + storeFile rootProject.file("keystores/${keystoreProperties['storeFile']}") + storePassword keystoreProperties['storePassword'] + } + } + buildTypes { + debug { + } + release { + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + signingConfig signingConfigs.config + } + } + + flavorDimensions "environment" + productFlavors { + staging { + dimension "environment" + } + production { + dimension "environment" + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // https://developer.android.com/studio/build/configure-apk-splits.html + def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } +} + +dependencies { + implementation 'androidx.multidex:multidex:2.0.1' + implementation fileTree(dir: "libs", include: ["*.jar"]) + //noinspection GradleDynamicVersion + implementation "com.facebook.react:react-native:+" // From node_modules + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}@aar") { + exclude group:'com.facebook.fbjni' + } + debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + exclude group:'com.squareup.okhttp3', module:'okhttp' + } + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { + exclude group:'com.facebook.flipper' + } + implementation 'androidx.appcompat:appcompat:1.1.0-rc01' // @react-navigation + implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' // @react-navigation + if (enableHermes) { + def hermesPath = "../../node_modules/hermes-engine/android/"; + debugImplementation files(hermesPath + "hermes-debug.aar") + releaseImplementation files(hermesPath + "hermes-release.aar") + } else { + implementation jscFlavor + } +} + +// Run this once to be able to run the application with BUCK +// puts all compile dependencies into folder libs for BUCK to use +task copyDownloadableDepsToLibs(type: Copy) { + from configurations.compile + into 'libs' +} + +apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/android/app/build_defs.bzl b/android/app/build_defs.bzl new file mode 100644 index 00000000000..fff270f8d1d --- /dev/null +++ b/android/app/build_defs.bzl @@ -0,0 +1,19 @@ +"""Helper definitions to glob .aar and .jar targets""" + +def create_aar_targets(aarfiles): + for aarfile in aarfiles: + name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] + lib_deps.append(":" + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +def create_jar_targets(jarfiles): + for jarfile in jarfiles: + name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] + lib_deps.append(":" + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) diff --git a/android/app/proguard-rules.pro b/android/app/proguard-rules.pro new file mode 100644 index 00000000000..295a0644797 --- /dev/null +++ b/android/app/proguard-rules.pro @@ -0,0 +1,16 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +-keep class com.facebook.hermes.unicode.** { *; } +-keep class com.facebook.jni.** { *; } + +# @react-native-config +-keep class com.passculture.BuildConfig { *; } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 00000000000..fa26aa56e1c --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/android/app/src/debug/java/com/passculture/ReactNativeFlipper.java b/android/app/src/debug/java/com/passculture/ReactNativeFlipper.java new file mode 100644 index 00000000000..b6adeba46c4 --- /dev/null +++ b/android/app/src/debug/java/com/passculture/ReactNativeFlipper.java @@ -0,0 +1,67 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.passculture; +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.react.ReactFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + final FlipperClient client = AndroidFlipperClient.getInstance(context); + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new ReactFlipperPlugin()); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceManager.ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } + } +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000000..6a13aa76ccc --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/android/app/src/main/java/com/passculture/MainActivity.java b/android/app/src/main/java/com/passculture/MainActivity.java new file mode 100644 index 00000000000..8a7171e240c --- /dev/null +++ b/android/app/src/main/java/com/passculture/MainActivity.java @@ -0,0 +1,29 @@ +package com.passculture; + +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactActivityDelegate; //@react-navigation +import com.facebook.react.ReactRootView; //@react-navigation +import com.swmansion.gesturehandler.react.RNGestureHandlerEnabledRootView; //@react-navigation + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "PassCulture"; + } + + // @react-navigation (https://reactnavigation.org/docs/en/next/getting-started.html) + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new ReactActivityDelegate(this, getMainComponentName()) { + @Override + protected ReactRootView createRootView() { + return new RNGestureHandlerEnabledRootView(MainActivity.this); + } + }; + } +} diff --git a/android/app/src/main/java/com/passculture/MainApplication.java b/android/app/src/main/java/com/passculture/MainApplication.java new file mode 100644 index 00000000000..d7fe3938fa9 --- /dev/null +++ b/android/app/src/main/java/com/passculture/MainApplication.java @@ -0,0 +1,93 @@ +package com.passculture; + +import android.app.Application; +import android.util.Log; +import android.content.Context; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.PackageList; +import com.facebook.hermes.reactexecutor.HermesExecutorFactory; +import com.facebook.react.bridge.JavaScriptExecutorFactory; +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.soloader.SoLoader; +import com.microsoft.codepush.react.CodePush; // @codepush +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + /* + * Override the getJSBundleFile method in order to let + * the CodePush runtime determine where to get the JS + * bundle location from on each app start + */ + @Override + protected String getJSBundleFile() { + return CodePush.getJSBundleFile(); // @codepush + } + + @Override + protected List getPackages() { + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return packages; + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + } + + /** + * Loads Flipper in React Native templates. Call this in the onCreate method with something like + * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + * + * @param context + * @param reactInstanceManager + */ + private static void initializeFlipper( + Context context, ReactInstanceManager reactInstanceManager) { + if (BuildConfig.DEBUG) { + try { + /* + We use reflection here to pick up the class that initializes Flipper, + since Flipper library is not available in release mode + */ + Class aClass = Class.forName("com.passculture.ReactNativeFlipper"); + aClass + .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) + .invoke(null, context, reactInstanceManager); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } + } + } +} diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000000..a2f5908281d Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 00000000000..1b523998081 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000000..ff10afd6e18 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 00000000000..115a4c768a2 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000000..dcd3cd80833 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 00000000000..459ca609d3a Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000000..8ca12fe024b Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 00000000000..8e19b410a1b Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 00000000000..b824ebdd48d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 00000000000..4c19a13c239 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000000..8542005550c --- /dev/null +++ b/android/app/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000000..62fe59fa485 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 00000000000..fa8bf80ba05 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,48 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = "29.0.2" + minSdkVersion = 16 + compileSdkVersion = 29 + targetSdkVersion = 29 + supportLibVersion = "28.0.0" + } + repositories { + google() + jcenter() + } + dependencies { + classpath("com.android.tools.build:gradle:3.5.3") + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenLocal() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url("$rootDir/../node_modules/react-native/android") + } + maven { + // Android JSC is installed from npm + url("$rootDir/../node_modules/jsc-android/dist") + } + + google() + jcenter() + maven { url 'https://www.jitpack.io' } + } +} + +subprojects { subproject -> + afterEvaluate{ + if((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) { + android { + compileSdkVersion rootProject.ext.compileSdkVersion + } + } + } +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 00000000000..3bdbd3d4e90 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,28 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.54.0 diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000000..5c2d1cf016b Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000000..84226702066 --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew new file mode 100755 index 00000000000..2fe81a7d95e --- /dev/null +++ b/android/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/android/gradlew.bat b/android/gradlew.bat new file mode 100644 index 00000000000..b742c9917f7 --- /dev/null +++ b/android/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/android/keystores/staging.keystore.properties b/android/keystores/staging.keystore.properties new file mode 100644 index 00000000000..15594d74746 --- /dev/null +++ b/android/keystores/staging.keystore.properties @@ -0,0 +1,4 @@ +keyAlias=passculture +storeFile=staging.keystore +storePassword='' +keyPassword='' diff --git a/android/settings.gradle b/android/settings.gradle new file mode 100644 index 00000000000..e7abc43bd7f --- /dev/null +++ b/android/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'PassCulture' +apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) +include ':app', ':react-native-code-push' +project(':react-native-code-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-code-push/android/app') diff --git a/app.json b/app.json new file mode 100644 index 00000000000..6b185b71b0e --- /dev/null +++ b/app.json @@ -0,0 +1,4 @@ +{ + "name": "PassCulture", + "displayName": "PassCulture" +} \ No newline at end of file diff --git a/babel.config.js b/babel.config.js new file mode 100644 index 00000000000..66be7d432e1 --- /dev/null +++ b/babel.config.js @@ -0,0 +1,4 @@ +module.exports = { + presets: ['module:metro-react-native-babel-preset'], + plugins: ['macros'], +}; diff --git a/doc/i18n/add-language.md b/doc/i18n/add-language.md new file mode 100644 index 00000000000..78193a40c7d --- /dev/null +++ b/doc/i18n/add-language.md @@ -0,0 +1,6 @@ +## How to add a new language + +For example, to add German: + +- `yarn translations:add-locale de` +- `yarn translations:extract` diff --git a/doc/i18n/add-translation.md b/doc/i18n/add-translation.md new file mode 100644 index 00000000000..98a1ab3c65e --- /dev/null +++ b/doc/i18n/add-translation.md @@ -0,0 +1,21 @@ +## How to add a new French translation key + +- in your component, add the following code : + +``` +import { t } from '@lingui/macro'; +import { i18n } from 'path/to/lib/i18n'; +export const MyComponent = () => ( + + + i18n._( + /*i18n: Here goes the description for the translator*/ t`The English translation` + ) + + +) +``` + +- run `yarn translation:extract` +- add the French translation in the `src/locales/fr/messages.po` file +- run `yarn translations:compile` diff --git a/fastlane/.env b/fastlane/.env new file mode 100644 index 00000000000..95120032a72 --- /dev/null +++ b/fastlane/.env @@ -0,0 +1,20 @@ +# Shared config +IOS_PROJECT_PATH='ios' +ANDROID_PROJECT_DIR='android' +IOS_PROJECT_NAME='PassCulture' +IOS_PLIST_PATH='PassCulture/Info.plist' +IOS_PUSH_NOTIFICATIONS=1 + +# iOS +IOS_DEVELOPMENT_TEAM="BAM" +IOS_USER_ID="developers.bam@gmail.com" +IOS_ITC_TEAM_NAME="BAM" +IOS_TEAM_ID="58628H666T" + +DEPLOYMENT_PLATFORM="appcenter" + +# Appcenter +APPCENTER_ORGANISATION='BAM.tech' +APPCENTER_IOS_APP_ID='passculture-ios' +APPCENTER_ANDROID_APP_ID='passculture-android' +APPCENTER_DISTRIBUTE_GROUPS='Collaborators, Public' diff --git a/fastlane/.env.production b/fastlane/.env.production new file mode 100644 index 00000000000..9306568a20b --- /dev/null +++ b/fastlane/.env.production @@ -0,0 +1,11 @@ +ENV='production' + +# iOS +MATCH_TYPE='appstore' +IOS_SCHEME='PassCulture-Production' +IOS_IPA_DIRECTORY='ios/build' +IOS_IPA_NAME='app-staging.ipa' + +# Android +ANDROID_GRADLE_TASK='assembleProductionRelease' +ANDROID_APK_PATH='android/app/build/outputs/apk/production/release/app-staging-release.apk' diff --git a/fastlane/.env.secret b/fastlane/.env.secret new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fastlane/.env.staging b/fastlane/.env.staging new file mode 100644 index 00000000000..5f1fcf20f6a --- /dev/null +++ b/fastlane/.env.staging @@ -0,0 +1,12 @@ +ENV='staging' + +# iOS +MATCH_TYPE='enterprise' +IOS_SCHEME='PassCulture-Staging' +IOS_IPA_DIRECTORY='ios/build' +IOS_IPA_NAME='app-staging.ipa' + + +# Android +ANDROID_GRADLE_TASK='assembleStagingRelease' +ANDROID_APK_PATH='android/app/build/outputs/apk/staging/release/app-staging-release.apk' diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 00000000000..e0dbd17db08 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,5 @@ +# See https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md +app_identifier ENV['IOS_APP_ID'] +apple_id ENV['IOS_USER_ID'] +team_id ENV['IOS_TEAM_ID'] +itc_team_name ENV['IOS_ITC_TEAM_NAME'] diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 00000000000..3e133156fcb --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,96 @@ +fastlane_require 'dotenv' +fastlane_require 'spaceship' +release_notes_command = "git log HEAD --pretty=format:\"%s\" -1" + +import("FastfileConfig") +import("FastfileCheck") +import("FastfileEnvironment") +import("FastfileIOSSigning") + + + +# iOS + +platform :ios do + + lane :build do |options| + gym( + scheme: ENV['IOS_SCHEME'], + output_directory: "#{ENV['IOS_IPA_DIRECTORY']}", + output_name: "#{ENV['IOS_IPA_NAME']}", + silent: true, + workspace: "#{ENV['IOS_PROJECT_PATH']}/PassCulture.xcworkspace") + end + + lane :deploy_appCenter do |options| + appcenter_upload( + api_token: ENV['APPCENTER_API_TOKEN'], + owner_name: ENV['APPCENTER_ORGANISATION'], + owner_type: "organization", + app_name: ENV['APPCENTER_IOS_APP_ID'], + ipa: "#{ENV['IOS_IPA_DIRECTORY']}/#{ENV['IOS_IPA_NAME']}", + release_notes: %x[#{release_notes_command}], + destinations: ENV['APPCENTER_DISTRIBUTE_GROUPS'] + ) + gsp_path = "#{ENV['IOS_PROJECT_PATH']}/PassCulture/GoogleService-Info.plist" + upload_symbols_to_crashlytics(binary_path:"#{ENV['IOS_PROJECT_PATH']}/Pods/Fabric/upload-symbols", gsp_path: gsp_path) + end + + lane :deploy do |options| + if options[:codepush] then # @codepush + release_notes = %x[#{release_notes_command}] + sh "cd .. && appcenter codepush release-react -d #{ENV['CODEPUSH_DEPLOYMENT_NAME']} -a #{ENV['APPCENTER_ORGANISATION']}/#{ENV['APPCENTER_IOS_APP_ID']} --target-binary-version \"#{ENV['VERSION']}\" --description \"#{release_notes}\" --disable-duplicate-release-error" + else + match( + shallow_clone: true, + clone_branch_directly: true, + ) + build + if ENV['DEPLOYMENT_PLATFORM'] === 'appcenter' then + deploy_appCenter + else + pilot( + distribute_external: false, + skip_waiting_for_build_processing: true + ) + end + end + end + +end + +# Android + +platform :android do + lane :build do |options| + gradle( + task: ENV['ANDROID_GRADLE_TASK'], + project_dir: ENV['ANDROID_PROJECT_DIR'] + ) + end + + lane :deploy_appCenter do |options| + appcenter_upload( + api_token: ENV['APPCENTER_API_TOKEN'], + owner_name: ENV['APPCENTER_ORGANISATION'], + owner_type: "organization", + app_name: ENV['APPCENTER_ANDROID_APP_ID'], + apk: ENV["ANDROID_APK_PATH"], + release_notes: %x[#{release_notes_command}], + destinations: ENV['APPCENTER_DISTRIBUTE_GROUPS'] + ) + end + + lane :deploy do |options| + if options[:codepush] then # @codepush + release_notes = %x[#{release_notes_command}] + sh "cd .. && appcenter codepush release-react -d #{ENV['CODEPUSH_DEPLOYMENT_NAME']} -a #{ENV['APPCENTER_ORGANISATION']}/#{ENV['APPCENTER_ANDROID_APP_ID']} --target-binary-version \"#{ENV['VERSION']}\" --description \"#{release_notes}\" --disable-duplicate-release-error" + else + build + if ENV['DEPLOYMENT_PLATFORM'] === 'appcenter' then + deploy_appCenter + end + end + end + +end diff --git a/fastlane/FastfileCheck b/fastlane/FastfileCheck new file mode 100644 index 00000000000..4e30237d6d9 --- /dev/null +++ b/fastlane/FastfileCheck @@ -0,0 +1,13 @@ +private_lane :check_env_exists do |options| + if !File.file?("../.env.#{ENV['ENV']}") + UI.user_error!("Env #{ENV['ENV']} does not exist") + end +end + +private_lane :check_git_status do |options| + ensure_git_branch( + branch: "#{options[:name]}" + ) + ensure_git_status_clean + git_pull +end diff --git a/fastlane/FastfileConfig b/fastlane/FastfileConfig new file mode 100644 index 00000000000..fa24f95d0e3 --- /dev/null +++ b/fastlane/FastfileConfig @@ -0,0 +1,6 @@ +# Skip docs generation +skip_docs + +before_all do |lane, options| + load_env_file +end diff --git a/fastlane/FastfileEnvironment b/fastlane/FastfileEnvironment new file mode 100644 index 00000000000..05e8655ad1d --- /dev/null +++ b/fastlane/FastfileEnvironment @@ -0,0 +1,9 @@ +fastlane_require 'dotenv' + +private_lane :load_env_file do |options| + check_env_exists( + env: ENV["ENV"], + ) + Dotenv.overload(".env.secret") + Dotenv.overload("../.env.#{ENV["ENV"]}") +end diff --git a/fastlane/FastfileIOSSigning b/fastlane/FastfileIOSSigning new file mode 100644 index 00000000000..16359591440 --- /dev/null +++ b/fastlane/FastfileIOSSigning @@ -0,0 +1,67 @@ +platform :ios do + lane :generate_certificates do |options| + produce( + app_identifier: ENV['IOS_APP_ID'], + team_id: ENV['IOS_TEAM_ID'], + app_name: ENV['IOS_APP_NAME'], + skip_itc: true + ) + match( + app_identifier: ENV['IOS_APP_ID'], + type: 'development', + shallow_clone: true, + clone_branch_directly: true, + force_for_new_devices: true + ) + match( + app_identifier: ENV['IOS_APP_ID'], + shallow_clone: true, + clone_branch_directly: true, + force_for_new_devices: true + ) + update_provisioning_profile + end + + + lane :download_certificates do |options| + match( + app_identifier: ENV['IOS_APP_ID'], + type: 'development', + shallow_clone: true, + clone_branch_directly: true, + readonly: true + ) + match( + app_identifier: ENV['IOS_APP_ID'], + shallow_clone: true, + clone_branch_directly: true, + readonly: true + ) + update_provisioning_profile + end + + private_lane :update_provisioning_profile do |options| + development_profile_env_name = "sigh_#{ENV['IOS_APP_ID']}_development_profile-name" + release_profile_env_name = "sigh_#{ENV['IOS_APP_ID']}_#{ENV['MATCH_TYPE']}_profile-name" + sh "sed -i '' -e \"s/IOS_PROVISIONING_PROFILE_SPECIFIER_DEVELOPMENT=.*/IOS_PROVISIONING_PROFILE_SPECIFIER_DEVELOPMENT='#{ENV[development_profile_env_name]}'/g\" ../.env.#{ENV['ENV']}" + sh "sed -i '' -e \"s/IOS_PROVISIONING_PROFILE_SPECIFIER_RELEASE=.*/IOS_PROVISIONING_PROFILE_SPECIFIER_RELEASE='#{ENV[release_profile_env_name]}'/g\" ../.env.#{ENV['ENV']}" + end + + lane :setup_push do + Spaceship.login + Spaceship.select_team + keyName = "Push JWT #{ENV['IOS_APP_NAME']} #{ENV['ENV']}".gsub!(/[^0-9A-Za-z ]/, '') + key = Spaceship::Portal::Key.create(name: keyName, apns: true) + puts "Key ID is" + puts key.id + File.write("#{keyName}.p8", key.download) + end + + lane :add_device do |options| + register_device( + name: options[:name], + udid: options[:udid], + ) + update_provisioning_profile + end +end diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 00000000000..3d9a147412c --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1,6 @@ +git_url("git@github.com:bamlab/certificates.git") + +storage_mode("git") +git_branch("master") + +username("developers.bam@gmail.com") diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 00000000000..756bff8e135 --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-appcenter' diff --git a/index.js b/index.js new file mode 100644 index 00000000000..9bd04ecf765 --- /dev/null +++ b/index.js @@ -0,0 +1,5 @@ +import { AppRegistry } from 'react-native'; +import { App } from './src/App'; +import { name as appName } from './app.json'; + +AppRegistry.registerComponent(appName, () => App); diff --git a/ios/PassCulture-Bridging-Header.h b/ios/PassCulture-Bridging-Header.h new file mode 100644 index 00000000000..e11d920b120 --- /dev/null +++ b/ios/PassCulture-Bridging-Header.h @@ -0,0 +1,3 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// diff --git a/ios/PassCulture-tvOS/Info.plist b/ios/PassCulture-tvOS/Info.plist new file mode 100644 index 00000000000..1f8cd95cb1f --- /dev/null +++ b/ios/PassCulture-tvOS/Info.plist @@ -0,0 +1,53 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/PassCulture-tvOSTests/Info.plist b/ios/PassCulture-tvOSTests/Info.plist new file mode 100644 index 00000000000..886825ccc9b --- /dev/null +++ b/ios/PassCulture-tvOSTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ios/PassCulture.xcodeproj/project.pbxproj b/ios/PassCulture.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..e77d1cd021d --- /dev/null +++ b/ios/PassCulture.xcodeproj/project.pbxproj @@ -0,0 +1,492 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 042C3F171E8246C0835AF4E6 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 65C1955A99E745B9B354F57A /* AppDelegate.m */; }; + 2A5B3D8DAFAA4030AF31D9D7 /* libPods-PassCulture.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F625F4D6377B4766806FA581 /* libPods-PassCulture.a */; }; + 7323393B84C441F488A4C363 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C7D886722584EDD87A2BB44 /* main.m */; }; + 8FEB737BACB244CF8BB8C051 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D33106CFBBB44FF5B75130D1 /* SplashScreen.storyboard */; }; + CE363314E2974BF48FF6C850 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A8881E10DAB84DAA99A29F65 /* Images.xcassets */; }; + DC3FC3142D9D4BD68F82D428 /* react-native-config.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = A5AEB52C0E7D445DA62F0A79 /* react-native-config.xcconfig */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 030D8707D3F8414C91A9D78A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = PassCulture/AppDelegate.h; sourceTree = ""; }; + 1A03DEF7037048CDA61975E3 /* Pods-PassCulture-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture-tvOS.release.xcconfig"; path = "Target Support Files/Pods-PassCulture-tvOS/Pods-PassCulture-tvOS.release.xcconfig"; sourceTree = ""; }; + 213DDAFA58E048DDBD77AC65 /* Pods-PassCulture.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture.release.xcconfig"; path = "Target Support Files/Pods-PassCulture/Pods-PassCulture.release.xcconfig"; sourceTree = ""; }; + 22B7F87517A74CA28F4E566F /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; }; + 248BEE7FC3B549C19C948BCD /* Pods-PassCultureTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCultureTests.release.xcconfig"; path = "Target Support Files/Pods-PassCultureTests/Pods-PassCultureTests.release.xcconfig"; sourceTree = ""; }; + 254052E46BEE4844B06EC6CF /* libPods-PassCulture-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PassCulture-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3746B404099846C48BD4E3A3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = PassCulture/Info.plist; sourceTree = ""; }; + 3E4699FC5BE84397808E76EF /* Pods-PassCulture.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture.debug.xcconfig"; path = "Target Support Files/Pods-PassCulture/Pods-PassCulture.debug.xcconfig"; sourceTree = ""; }; + 4C7D886722584EDD87A2BB44 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = PassCulture/main.m; sourceTree = ""; }; + 4CAEF50B8632400B8EEB6469 /* PassCulture.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PassCulture.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5F350A4B89F548B599D643BB /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "PassCulture/GoogleService-Info.plist"; sourceTree = ""; }; + 65C1955A99E745B9B354F57A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = PassCulture/AppDelegate.m; sourceTree = ""; }; + 6748156527C24E50B9E1F238 /* Pods-PassCulture-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-PassCulture-tvOSTests/Pods-PassCulture-tvOSTests.release.xcconfig"; sourceTree = ""; }; + 7365A19A886E4D88A9D9D4C2 /* PassCulture-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PassCulture-Bridging-Header.h"; sourceTree = ""; }; + 75F5044862EC419997CE98CD /* Pods-PassCulture-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-PassCulture-tvOSTests/Pods-PassCulture-tvOSTests.debug.xcconfig"; sourceTree = ""; }; + 866FC5E348B2460296FB5F7B /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + A5AEB52C0E7D445DA62F0A79 /* react-native-config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "react-native-config.xcconfig"; sourceTree = ""; }; + A746D931C0C9478587300253 /* Pods-PassCulture-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCulture-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-PassCulture-tvOS/Pods-PassCulture-tvOS.debug.xcconfig"; sourceTree = ""; }; + A859DE5FB7B343058FFFB3C3 /* libPods-PassCulture-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PassCulture-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + A8881E10DAB84DAA99A29F65 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = PassCulture/Images.xcassets; sourceTree = ""; }; + C3EB4C7EF21447BFA7F60E12 /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; + D13F61BEEAE54B13ABB83BDB /* Pods-PassCultureTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PassCultureTests.debug.xcconfig"; path = "Target Support Files/Pods-PassCultureTests/Pods-PassCultureTests.debug.xcconfig"; sourceTree = ""; }; + D33106CFBBB44FF5B75130D1 /* SplashScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SplashScreen.storyboard; sourceTree = ""; }; + D9306B1F89B44991A1F9A9F6 /* libPods-PassCultureTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PassCultureTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + ED0101357B234447B64D029A /* PassCulture-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PassCulture-Bridging-Header.h"; sourceTree = ""; }; + F625F4D6377B4766806FA581 /* libPods-PassCulture.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PassCulture.a"; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FF9265DAA24E49F5A7E5EDBC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2A5B3D8DAFAA4030AF31D9D7 /* libPods-PassCulture.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 2F09B5F584694FA397052BFB /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 4A62C898BE944215BF1A5561 = { + isa = PBXGroup; + children = ( + 4F80AFA8A04449E4B81411BE /* PassCulture */, + 2F09B5F584694FA397052BFB /* Libraries */, + EB02EDD0A6CC42B28A408830 /* Products */, + 77E58495325A4481BF007316 /* Frameworks */, + D2B9297F2C754E49AAED936C /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 4F80AFA8A04449E4B81411BE /* PassCulture */ = { + isa = PBXGroup; + children = ( + 866FC5E348B2460296FB5F7B /* main.jsbundle */, + 030D8707D3F8414C91A9D78A /* AppDelegate.h */, + 65C1955A99E745B9B354F57A /* AppDelegate.m */, + A8881E10DAB84DAA99A29F65 /* Images.xcassets */, + 3746B404099846C48BD4E3A3 /* Info.plist */, + 4C7D886722584EDD87A2BB44 /* main.m */, + 5F350A4B89F548B599D643BB /* GoogleService-Info.plist */, + A5AEB52C0E7D445DA62F0A79 /* react-native-config.xcconfig */, + ED0101357B234447B64D029A /* PassCulture-Bridging-Header.h */, + 7365A19A886E4D88A9D9D4C2 /* PassCulture-Bridging-Header.h */, + D33106CFBBB44FF5B75130D1 /* SplashScreen.storyboard */, + ); + name = PassCulture; + sourceTree = ""; + }; + 77E58495325A4481BF007316 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C3EB4C7EF21447BFA7F60E12 /* JavaScriptCore.framework */, + 22B7F87517A74CA28F4E566F /* JavaScriptCore.framework */, + F625F4D6377B4766806FA581 /* libPods-PassCulture.a */, + 254052E46BEE4844B06EC6CF /* libPods-PassCulture-tvOS.a */, + A859DE5FB7B343058FFFB3C3 /* libPods-PassCulture-tvOSTests.a */, + D9306B1F89B44991A1F9A9F6 /* libPods-PassCultureTests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + D2B9297F2C754E49AAED936C /* Pods */ = { + isa = PBXGroup; + children = ( + 3E4699FC5BE84397808E76EF /* Pods-PassCulture.debug.xcconfig */, + 213DDAFA58E048DDBD77AC65 /* Pods-PassCulture.release.xcconfig */, + A746D931C0C9478587300253 /* Pods-PassCulture-tvOS.debug.xcconfig */, + 1A03DEF7037048CDA61975E3 /* Pods-PassCulture-tvOS.release.xcconfig */, + 75F5044862EC419997CE98CD /* Pods-PassCulture-tvOSTests.debug.xcconfig */, + 6748156527C24E50B9E1F238 /* Pods-PassCulture-tvOSTests.release.xcconfig */, + D13F61BEEAE54B13ABB83BDB /* Pods-PassCultureTests.debug.xcconfig */, + 248BEE7FC3B549C19C948BCD /* Pods-PassCultureTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + EB02EDD0A6CC42B28A408830 /* Products */ = { + isa = PBXGroup; + children = ( + 4CAEF50B8632400B8EEB6469 /* PassCulture.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5A4125C241FC47CCB40AF3D4 /* PassCulture */ = { + isa = PBXNativeTarget; + buildConfigurationList = D19BBFEC21BA49D2987E1E5F /* Build configuration list for PBXNativeTarget "PassCulture" */; + buildPhases = ( + 75A637501C1E4DD9838EF662 /* [CP] Check Pods Manifest.lock */, + E92F681689E943388A4606BA /* Start Packager */, + 4CD2851957C741289489E339 /* Sources */, + FF9265DAA24E49F5A7E5EDBC /* Frameworks */, + E9A16669FD424A8AA0F84376 /* Resources */, + 9603618410A54CC49C747AE9 /* Bundle React Native code and images */, + 43BBA1AD232846FE981BE1BF /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = PassCulture; + productName = PassCulture; + productReference = 4CAEF50B8632400B8EEB6469 /* PassCulture.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + CD2E04E3714A40F5958ADCD0 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = PassCulture; + TargetAttributes = { + 5A4125C241FC47CCB40AF3D4 = { + LastSwiftMigration = 1130; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 798F19CA4A3D47C18E2967A0 /* Build configuration list for PBXProject "PassCulture" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + Base, + ); + mainGroup = 4A62C898BE944215BF1A5561; + productRefGroup = EB02EDD0A6CC42B28A408830 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5A4125C241FC47CCB40AF3D4 /* PassCulture */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E9A16669FD424A8AA0F84376 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE363314E2974BF48FF6C850 /* Images.xcassets in Resources */, + DC3FC3142D9D4BD68F82D428 /* react-native-config.xcconfig in Resources */, + 8FEB737BACB244CF8BB8C051 /* SplashScreen.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 43BBA1AD232846FE981BE1BF /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-PassCulture/Pods-PassCulture-resources.sh", + "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PassCulture/Pods-PassCulture-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 75A637501C1E4DD9838EF662 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-PassCulture-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 9603618410A54CC49C747AE9 /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh\n"; + }; + E92F681689E943388A4606BA /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4CD2851957C741289489E339 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 042C3F171E8246C0835AF4E6 /* AppDelegate.m in Sources */, + 7323393B84C441F488A4C363 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 088BAA7B42054CE9AB04E28D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3E4699FC5BE84397808E76EF /* Pods-PassCulture.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; + DEVELOPMENT_TEAM = "$(IOS_TEAM_ID)"; + ENABLE_BITCODE = NO; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "FB_SONARKIT_ENABLED=1", + ); + INFOPLIST_FILE = PassCulture/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(IOS_APP_ID)"; + PRODUCT_NAME = PassCulture; + PROVISIONING_PROFILE_SPECIFIER = "$(IOS_PROVISIONING_PROFILE_SPECIFIER_DEVELOPMENT)"; + SWIFT_OBJC_BRIDGING_HEADER = "PassCulture-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 6AF8480A0AB74BD4819AE26A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 213DDAFA58E048DDBD77AC65 /* Pods-PassCulture.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = "$(IOS_TEAM_ID)"; + INFOPLIST_FILE = PassCulture/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "$(IOS_APP_ID)"; + PRODUCT_NAME = PassCulture; + PROVISIONING_PROFILE_SPECIFIER = "$(IOS_PROVISIONING_PROFILE_SPECIFIER_RELEASE)"; + SWIFT_OBJC_BRIDGING_HEADER = "PassCulture-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 8A3B40C36B9540BD8C1E6A7F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A5AEB52C0E7D445DA62F0A79 /* react-native-config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 8B1B496D862E4D80B517A091 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A5AEB52C0E7D445DA62F0A79 /* react-native-config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; + LIBRARY_SEARCH_PATHS = ( + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 798F19CA4A3D47C18E2967A0 /* Build configuration list for PBXProject "PassCulture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8A3B40C36B9540BD8C1E6A7F /* Debug */, + 8B1B496D862E4D80B517A091 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D19BBFEC21BA49D2987E1E5F /* Build configuration list for PBXNativeTarget "PassCulture" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 088BAA7B42054CE9AB04E28D /* Debug */, + 6AF8480A0AB74BD4819AE26A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = CD2E04E3714A40F5958ADCD0 /* Project object */; +} diff --git a/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Production.xcscheme b/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Production.xcscheme new file mode 100644 index 00000000000..fda524e5423 --- /dev/null +++ b/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Production.xcscheme @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Staging.xcscheme b/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Staging.xcscheme new file mode 100644 index 00000000000..9408fb61c0f --- /dev/null +++ b/ios/PassCulture.xcodeproj/xcshareddata/xcschemes/PassCulture-Staging.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/PassCulture.xcworkspace/contents.xcworkspacedata b/ios/PassCulture.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..832f72c370d --- /dev/null +++ b/ios/PassCulture.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/ios/PassCulture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/PassCulture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/ios/PassCulture.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ios/PassCulture/AppDelegate.h b/ios/PassCulture/AppDelegate.h new file mode 100644 index 00000000000..ef1de86a2a8 --- /dev/null +++ b/ios/PassCulture/AppDelegate.h @@ -0,0 +1,8 @@ +#import +#import + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/ios/PassCulture/AppDelegate.m b/ios/PassCulture/AppDelegate.m new file mode 100644 index 00000000000..d74d2485b7e --- /dev/null +++ b/ios/PassCulture/AppDelegate.m @@ -0,0 +1,58 @@ +#import "AppDelegate.h" + +#import +#import +#import +#import // @codepush + +#ifdef FB_SONARKIT_ENABLED +#import +#import +#import +#import +#import +#import +static void InitializeFlipper(UIApplication *application) { + FlipperClient *client = [FlipperClient sharedClient]; + SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; + [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; + [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; + [client addPlugin:[FlipperKitReactPlugin new]]; + [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; + [client start]; +} +#endif + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + #ifdef FB_SONARKIT_ENABLED + InitializeFlipper(application); + #endif + + RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; + RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge + moduleName:@"PassCulture" + initialProperties:nil]; + + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; +#else + return [CodePush bundleURL]; // @codepush +#endif +} + +@end diff --git a/ios/PassCulture/Images.xcassets/AppIcon.appiconset/Contents.json b/ios/PassCulture/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..118c98f7461 --- /dev/null +++ b/ios/PassCulture/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ios/PassCulture/Images.xcassets/Contents.json b/ios/PassCulture/Images.xcassets/Contents.json new file mode 100644 index 00000000000..2d92bd53fdb --- /dev/null +++ b/ios/PassCulture/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/ios/PassCulture/Info.plist b/ios/PassCulture/Info.plist new file mode 100644 index 00000000000..7591030b4a9 --- /dev/null +++ b/ios/PassCulture/Info.plist @@ -0,0 +1,57 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + PassCulture + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(IOS_APP_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(BUILD) + CodePushDeploymentKey + $(CODEPUSH_KEY_IOS) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + SplashScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/ios/PassCulture/main.m b/ios/PassCulture/main.m new file mode 100644 index 00000000000..b1df44b953e --- /dev/null +++ b/ios/PassCulture/main.m @@ -0,0 +1,9 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 00000000000..0096f399744 --- /dev/null +++ b/ios/Podfile @@ -0,0 +1,21 @@ +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, '10.0' + +target 'PassCulture' do + config = use_native_modules! + + use_react_native!(:path => config["reactNativePath"]) + + # Third party deps podspec link, to include if 0.60 autolinking not supported + + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable these next few lines. + use_flipper! + post_install do |installer| + flipper_post_install(installer) + end +end diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 00000000000..361e974f591 --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,536 @@ +PODS: + - Base64 (1.1.2) + - boost-for-react-native (1.63.0) + - CocoaAsyncSocket (7.6.4) + - CocoaLibEvent (1.0.0) + - CodePush (6.3.0): + - Base64 (~> 1.1) + - JWT (~> 3.0.0-beta.12) + - React + - SSZipArchive (~> 2.2.2) + - DoubleConversion (1.1.6) + - FBLazyVector (0.63.3) + - FBReactNativeSpec (0.63.3): + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.3) + - RCTTypeSafety (= 0.63.3) + - React-Core (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - Flipper (0.54.0): + - Flipper-Folly (~> 2.2) + - Flipper-RSocket (~> 1.1) + - Flipper-DoubleConversion (1.1.7) + - Flipper-Folly (2.2.0): + - boost-for-react-native + - CocoaLibEvent (~> 1.0) + - Flipper-DoubleConversion + - Flipper-Glog + - OpenSSL-Universal (= 1.0.2.19) + - Flipper-Glog (0.3.6) + - Flipper-PeerTalk (0.0.4) + - Flipper-RSocket (1.1.0): + - Flipper-Folly (~> 2.2) + - FlipperKit (0.54.0): + - FlipperKit/Core (= 0.54.0) + - FlipperKit/Core (0.54.0): + - Flipper (~> 0.54.0) + - FlipperKit/CppBridge + - FlipperKit/FBCxxFollyDynamicConvert + - FlipperKit/FBDefines + - FlipperKit/FKPortForwarding + - FlipperKit/CppBridge (0.54.0): + - Flipper (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.54.0): + - Flipper-Folly (~> 2.2) + - FlipperKit/FBDefines (0.54.0) + - FlipperKit/FKPortForwarding (0.54.0): + - CocoaAsyncSocket (~> 7.6) + - Flipper-PeerTalk (~> 0.0.4) + - FlipperKit/FlipperKitHighlightOverlay (0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutTextSearchable + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitReactPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.54.0): + - FlipperKit/Core + - FlipperKit/FlipperKitNetworkPlugin + - Folly (2020.01.13.00): + - boost-for-react-native + - DoubleConversion + - Folly/Default (= 2020.01.13.00) + - glog + - Folly/Default (2020.01.13.00): + - boost-for-react-native + - DoubleConversion + - glog + - glog (0.3.5) + - JWT (3.0.0-beta.12): + - Base64 (~> 1.1.2) + - OpenSSL-Universal (1.0.2.19): + - OpenSSL-Universal/Static (= 1.0.2.19) + - OpenSSL-Universal/Static (1.0.2.19) + - RCTRequired (0.63.3) + - RCTTypeSafety (0.63.3): + - FBLazyVector (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTRequired (= 0.63.3) + - React-Core (= 0.63.3) + - React (0.63.3): + - React-Core (= 0.63.3) + - React-Core/DevSupport (= 0.63.3) + - React-Core/RCTWebSocket (= 0.63.3) + - React-RCTActionSheet (= 0.63.3) + - React-RCTAnimation (= 0.63.3) + - React-RCTBlob (= 0.63.3) + - React-RCTImage (= 0.63.3) + - React-RCTLinking (= 0.63.3) + - React-RCTNetwork (= 0.63.3) + - React-RCTSettings (= 0.63.3) + - React-RCTText (= 0.63.3) + - React-RCTVibration (= 0.63.3) + - React-callinvoker (0.63.3) + - React-Core (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.3) + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/CoreModulesHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/Default (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/DevSupport (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.3) + - React-Core/RCTWebSocket (= 0.63.3) + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - React-jsinspector (= 0.63.3) + - Yoga + - React-Core/RCTActionSheetHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTAnimationHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTBlobHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTImageHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTLinkingHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTNetworkHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTSettingsHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTTextHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTVibrationHeaders (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-Core/RCTWebSocket (0.63.3): + - Folly (= 2020.01.13.00) + - glog + - React-Core/Default (= 0.63.3) + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsiexecutor (= 0.63.3) + - Yoga + - React-CoreModules (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.3) + - React-Core/CoreModulesHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - React-RCTImage (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-cxxreact (0.63.3): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-callinvoker (= 0.63.3) + - React-jsinspector (= 0.63.3) + - React-jsi (0.63.3): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-jsi/Default (= 0.63.3) + - React-jsi/Default (0.63.3): + - boost-for-react-native (= 1.63.0) + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-jsiexecutor (0.63.3): + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - React-jsinspector (0.63.3) + - react-native-config (0.13.0): + - React + - react-native-safe-area-context (3.1.8): + - React-Core + - react-native-sensitive-info (5.5.8): + - React + - React-RCTActionSheet (0.63.3): + - React-Core/RCTActionSheetHeaders (= 0.63.3) + - React-RCTAnimation (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.3) + - React-Core/RCTAnimationHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTBlob (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - React-Core/RCTBlobHeaders (= 0.63.3) + - React-Core/RCTWebSocket (= 0.63.3) + - React-jsi (= 0.63.3) + - React-RCTNetwork (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTImage (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.3) + - React-Core/RCTImageHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - React-RCTNetwork (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTLinking (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - React-Core/RCTLinkingHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTNetwork (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.3) + - React-Core/RCTNetworkHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTSettings (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - RCTTypeSafety (= 0.63.3) + - React-Core/RCTSettingsHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - React-RCTText (0.63.3): + - React-Core/RCTTextHeaders (= 0.63.3) + - React-RCTVibration (0.63.3): + - FBReactNativeSpec (= 0.63.3) + - Folly (= 2020.01.13.00) + - React-Core/RCTVibrationHeaders (= 0.63.3) + - React-jsi (= 0.63.3) + - ReactCommon/turbomodule/core (= 0.63.3) + - ReactCommon/turbomodule/core (0.63.3): + - DoubleConversion + - Folly (= 2020.01.13.00) + - glog + - React-callinvoker (= 0.63.3) + - React-Core (= 0.63.3) + - React-cxxreact (= 0.63.3) + - React-jsi (= 0.63.3) + - RNCAsyncStorage (1.12.0): + - React + - RNCMaskedView (0.1.10): + - React + - RNGestureHandler (1.8.0): + - React + - RNLocalize (1.4.1): + - React + - RNReanimated (1.13.1): + - React + - RNScreens (2.11.0): + - React + - SSZipArchive (2.2.3) + - Yoga (1.14.0) + - YogaKit (1.18.1): + - Yoga (~> 1.14) + +DEPENDENCIES: + - CodePush (from `../node_modules/react-native-code-push`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`) + - Flipper (~> 0.54.0) + - Flipper-DoubleConversion (= 1.1.7) + - Flipper-Folly (~> 2.2) + - Flipper-Glog (= 0.3.6) + - Flipper-PeerTalk (~> 0.0.4) + - Flipper-RSocket (~> 1.1) + - FlipperKit (~> 0.54.0) + - FlipperKit/Core (~> 0.54.0) + - FlipperKit/CppBridge (~> 0.54.0) + - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0) + - FlipperKit/FBDefines (~> 0.54.0) + - FlipperKit/FKPortForwarding (~> 0.54.0) + - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0) + - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0) + - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0) + - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0) + - FlipperKit/FlipperKitReactPlugin (~> 0.54.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0) + - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0) + - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/DevSupport (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - "react-native-config (from `../node_modules/@bam.tech/react-native-config`)" + - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - react-native-sensitive-info (from `../node_modules/react-native-sensitive-info`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)" + - "RNCMaskedView (from `../node_modules/@react-native-community/masked-view`)" + - RNGestureHandler (from `../node_modules/react-native-gesture-handler`) + - RNLocalize (from `../node_modules/react-native-localize`) + - RNReanimated (from `../node_modules/react-native-reanimated`) + - RNScreens (from `../node_modules/react-native-screens`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - Base64 + - boost-for-react-native + - CocoaAsyncSocket + - CocoaLibEvent + - Flipper + - Flipper-DoubleConversion + - Flipper-Folly + - Flipper-Glog + - Flipper-PeerTalk + - Flipper-RSocket + - FlipperKit + - JWT + - OpenSSL-Universal + - SSZipArchive + - YogaKit + +EXTERNAL SOURCES: + CodePush: + :path: "../node_modules/react-native-code-push" + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + FBReactNativeSpec: + :path: "../node_modules/react-native/Libraries/FBReactNativeSpec" + Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + RCTRequired: + :path: "../node_modules/react-native/Libraries/RCTRequired" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector" + react-native-config: + :path: "../node_modules/@bam.tech/react-native-config" + react-native-safe-area-context: + :path: "../node_modules/react-native-safe-area-context" + react-native-sensitive-info: + :path: "../node_modules/react-native-sensitive-info" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + RNCAsyncStorage: + :path: "../node_modules/@react-native-community/async-storage" + RNCMaskedView: + :path: "../node_modules/@react-native-community/masked-view" + RNGestureHandler: + :path: "../node_modules/react-native-gesture-handler" + RNLocalize: + :path: "../node_modules/react-native-localize" + RNReanimated: + :path: "../node_modules/react-native-reanimated" + RNScreens: + :path: "../node_modules/react-native-screens" + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + Base64: cecfb41a004124895a7bcee567a89bae5a89d49b + boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845 + CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f + CodePush: 69186fd1143f7e5e5c6f65383cf14f4927e28213 + DoubleConversion: cde416483dac037923206447da6e1454df403714 + FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d + FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f + Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365 + Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41 + Flipper-Folly: c12092ea368353b58e992843a990a3225d4533c3 + Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6 + Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7 + FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d + Folly: b73c3869541e86821df3c387eb0af5f65addfab4 + glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3 + JWT: 9b5c05abbcc1a0e69c3c91e1655b3387fc7e581d + OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355 + RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047 + RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab + React: f36e90f3ceb976546e97df3403e37d226f79d0e3 + React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd + React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6 + React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1 + React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99 + React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d + React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451 + React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2 + react-native-config: 056581a0891d0269428219c45c696dd91c6f9eee + react-native-safe-area-context: 79fea126c6830c85f65947c223a5e3058a666937 + react-native-sensitive-info: 400c6e3d27945e15c8cddb5b4b92233c85ee06f8 + React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa + React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2 + React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40 + React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387 + React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18 + React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda + React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773 + React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746 + React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454 + ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3 + RNCAsyncStorage: 2a692bcb9b69b76a2f1a95f33db057129700af64 + RNCMaskedView: 5a8ec07677aa885546a0d98da336457e2bea557f + RNGestureHandler: 7a5833d0f788dbd107fbb913e09aa0c1ff333c39 + RNLocalize: 49634a6a605dcdeb319e9c42a5c2f1aec508694b + RNReanimated: dd8c286ab5dd4ba36d3a7fef8bff7e08711b5476 + RNScreens: 0e91da98ab26d5d04c7b59a9b6bd694124caf88c + SSZipArchive: 62d4947b08730e4cda640473b0066d209ff033c9 + Yoga: 7d13633d129fd179e01b8953d38d47be90db185a + YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + +PODFILE CHECKSUM: d5d9bf561fb3934d6d26aeaccc075addb22b6b51 + +COCOAPODS: 1.8.4 diff --git a/ios/SplashScreen.storyboard b/ios/SplashScreen.storyboard new file mode 100644 index 00000000000..fe859580cb6 --- /dev/null +++ b/ios/SplashScreen.storyboard @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000000..2a047f2f940 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,17 @@ +module.exports = { + preset: 'react-native', + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + setupFiles: ['./jest/jest.setup.js', './node_modules/react-native-gesture-handler/jestSetup.js'], + transform: { + '^.+\\.(js)$': '/node_modules/babel-jest', + }, + transformIgnorePatterns: [ + 'node_modules/(?!(jest-)?react-native|@react-navigation|react-navigation|@react-native-community/masked-view|@react-native-community/async-storage/(?!(lib)))', + ], + testRegex: '(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$', + testPathIgnorePatterns: ['\\.snap$', '/node_modules/'], + cacheDirectory: '.jest/cache', + collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'], + coveragePathIgnorePatterns: ['/node_modules/', '/src/environment', '/src/locales'], + collectCoverage: true, +}; diff --git a/jest/jest.setup.js b/jest/jest.setup.js new file mode 100644 index 00000000000..2af446e8fbf --- /dev/null +++ b/jest/jest.setup.js @@ -0,0 +1,2 @@ +/* eslint-disable no-undef */ +import 'cross-fetch/polyfill'; diff --git a/jest/wrapWithProvidersAndRender.tsx b/jest/wrapWithProvidersAndRender.tsx new file mode 100644 index 00000000000..f9b276b3b5b --- /dev/null +++ b/jest/wrapWithProvidersAndRender.tsx @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { I18nProvider } from '@lingui/react'; +import React from 'react'; +import { render, RenderAPI } from 'react-native-testing-library'; +import { i18n } from '../src/lib/i18n'; +import { Provider as ReduxProvider } from 'react-redux'; +import { Store, createStore } from 'redux'; +import { rootReducer, RootState } from '../src/redux/rootReducer'; + +interface WrapWithProvidersAndRenderParams { + Component: React.FunctionComponent; + props?: Record; + testInitialReduxState?: Partial; +} + +export const wrapWithProvidersAndRender = ({ + Component, + props = {}, + testInitialReduxState, +}: WrapWithProvidersAndRenderParams): { component: RenderAPI; store: Store } => { + // @ts-ignore + const store = createStore(rootReducer, testInitialReduxState); + + const WrappedComponent = ( + + + + + + ); + + return { component: render(WrappedComponent), store }; +}; diff --git a/metro.config.js b/metro.config.js new file mode 100644 index 00000000000..13a964217f2 --- /dev/null +++ b/metro.config.js @@ -0,0 +1,17 @@ +/** + * Metro configuration for React Native + * https://github.com/facebook/react-native + * + * @format + */ + +module.exports = { + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: false, + }, + }), + }, +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000000..ae8d4db7ae7 --- /dev/null +++ b/package.json @@ -0,0 +1,166 @@ +{ + "name": "PassCulture", + "version": "0.0.1", + "private": true, + "engines": { + "node": ">=12.13.0 <13.0.0" + }, + "scripts": { + "start": "react-native start", + "test": "yarn test:lint && yarn test:types && yarn test:unit", + "test:lint": "eslint . --ext .js,.ts,.tsx --cache", + "test:types": "tsc --noEmit", + "test:unit": "jest", + "plop": "plop", + "postinstall": "patch-package && jetify && yarn translations:compile", + "translations:add-locale": "lingui add-locale", + "translations:extract": "lingui extract", + "translations:compile": "lingui compile" + }, + "dependencies": { + "@bam.tech/react-native-config": "^0.13.0", + "@lingui/react": "^2.9.2", + "@react-native-community/async-storage": "^1.7.1", + "@react-native-community/masked-view": "^0.1.6", + "@react-navigation/native": "^5.7.5", + "@react-navigation/stack": "^5.0.6", + "@storybook/addon-actions": "^6.0.22", + "@storybook/addon-links": "^6.0.22", + "@storybook/addons": "^6.0.22", + "@storybook/react-native": "^5.3.18", + "babel-core": "^6.26.3", + "i18n-js": "^3.5.1", + "jsc-android": "^241213.1.0", + "patch-package": "^6.2.0", + "react": "16.13.1", + "react-native": "0.63.3", + "react-native-code-push": "^6.2.0", + "react-native-gesture-handler": "^1.6.0", + "react-native-localize": "^1.3.2", + "react-native-reanimated": "^1.7.0", + "react-native-safe-area-context": "^3.1.8", + "react-native-screens": "^2.0.0-beta.8", + "react-native-sensitive-info": "^5.5.5", + "react-redux": "^7.1.3", + "recompose": "^0.30.0", + "redux": "^4.0.5", + "redux-devtools-extension": "^2.13.8", + "redux-persist": "^6.0.0", + "redux-persist-sensitive-storage": "^1.0.0", + "redux-saga": "^1.1.3", + "typesafe-actions": "^5.1.0" + }, + "devDependencies": { + "@babel/core": "^7.8.4", + "@babel/runtime": "^7.8.4", + "@lingui/cli": "^2.9.2", + "@lingui/macro": "^2.9.2", + "@react-native-community/eslint-config": "^2.0.0", + "@types/i18n-js": "^3.0.1", + "@types/jest": "^26.0.14", + "@types/lingui__core": "^2.7.1", + "@types/lingui__macro": "^2.7.4", + "@types/lingui__react": "^2.8.3", + "@types/react": "^16.9.19", + "@types/react-native": "^0.63.23", + "@types/react-redux": "^7.1.7", + "@types/react-test-renderer": "^16.9.2", + "@types/recompose": "^0.30.7", + "@typescript-eslint/eslint-plugin": "^4.3.0", + "@typescript-eslint/parser": "^4.3.0", + "babel-jest": "^25.1.0", + "babel-loader": "^8.1.0", + "babel-plugin-macros": "^2.8.0", + "cross-fetch": "^3.0.4", + "eslint": "7.10.0", + "eslint-config-prettier": "^6.9.0", + "eslint-plugin-prettier": "^3.1.2", + "eslint-plugin-react": "^7.18.0", + "eslint-plugin-react-native": "^3.8.1", + "jest": "^25.1.0", + "jetifier": "^1.6.5", + "metro-react-native-babel-preset": "0.63.0", + "plop": "^2.6.0", + "prettier": "^2.1.2", + "react-dom": "16.11.0", + "react-native-testing-library": "^6.0.0", + "react-test-renderer": "16.13.1", + "redux-mock-store": "^1.5.4", + "ts-jest": "26.4.1", + "typescript": "^4.0.3" + }, + "jest": { + "preset": "react-native" + }, + "make-dependencies": { + "@react-navigation": { + "dependencies": [ + "@react-navigation/native", + "@react-navigation/stack", + "react-native-gesture-handler", + "react-native-screens" + ], + "devDependencies": [ + "@types/react-navigation" + ] + }, + "@redux": { + "dependencies": [ + "react-redux", + "redux", + "redux-devtools-extension", + "redux-persist", + "redux-saga", + "recompose", + "typesafe-actions" + ], + "devDependencies": [ + "@types/react-redux", + "@types/redux", + "@types/recompose", + "@types/redux-devtools-extension", + "@types/redux-persist", + "@types/redux-saga", + "redux-mock-store" + ] + }, + "@codepush": { + "dependencies": [ + "react-native-code-push" + ] + }, + "@eslint": { + "devDependencies": [ + "eslint", + "eslint-config-prettier", + "eslint-plugin-prettier", + "eslint-plugin-react", + "eslint-plugin-react-native" + ] + }, + "@translations": { + "dependencies": [ + "jsc-android", + "babel-core", + "babel-plugin-macros", + "@lingui/react", + "@lingui/core", + "@lingui/macros", + "react-native-localize" + ], + "devDependencies": [ + "@types/lingui__react", + "@types/lingui__core", + "@types/lingui__macros" + ] + }, + "@storybook": { + "dependencies": [ + "@storybook/addon-actions", + "@storybook/addon-links", + "@storybook/addons", + "@storybook/react-native" + ] + } + } +} diff --git a/plop_templates/atom/atom.template b/plop_templates/atom/atom.template new file mode 100644 index 00000000000..4a18e31b273 --- /dev/null +++ b/plop_templates/atom/atom.template @@ -0,0 +1,5 @@ +import React, { FunctionComponent } from 'react'; + +interface Props {} + +export const {{ AtomName }}: FunctionComponent = () => null; diff --git a/plop_templates/atom/index.template b/plop_templates/atom/index.template new file mode 100644 index 00000000000..172e8d3b43c --- /dev/null +++ b/plop_templates/atom/index.template @@ -0,0 +1 @@ +export { {{ AtomName }} } from './{{ AtomName }}.atom'; diff --git a/plop_templates/atom/test.template b/plop_templates/atom/test.template new file mode 100644 index 00000000000..69026fcd15a --- /dev/null +++ b/plop_templates/atom/test.template @@ -0,0 +1,10 @@ +import React from 'react'; +import { render } from 'react-native-testing-library'; +import { {{ AtomName }} } from '../{{ AtomName }}.atom'; + +describe('{{ AtomName }} atom', () => { + it('renders correctly', () => { + const component = render(<{{ AtomName }} />); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/plop_templates/module/actions.template b/plop_templates/module/actions.template new file mode 100644 index 00000000000..245016dc9f3 --- /dev/null +++ b/plop_templates/module/actions.template @@ -0,0 +1,9 @@ +import { action, ActionType } from 'typesafe-actions'; + +export const setValue = (value: string) => action('SET_VALUE', { value }); + +const actions = { + setValue, +}; + +export type {{ ModuleName }}Action = ActionType; diff --git a/plop_templates/module/index.template b/plop_templates/module/index.template new file mode 100644 index 00000000000..d157cc01c4e --- /dev/null +++ b/plop_templates/module/index.template @@ -0,0 +1,2 @@ +export * from './actions'; +export { {{camelCase ModuleName}}Reducer } from './reducer'; diff --git a/plop_templates/module/reducer.template b/plop_templates/module/reducer.template new file mode 100644 index 00000000000..f9b1b98744c --- /dev/null +++ b/plop_templates/module/reducer.template @@ -0,0 +1,15 @@ +import { createReducer } from 'typesafe-actions'; +import { {{ModuleName}}Action } from './actions'; + +export interface {{ModuleName}}State { + value?: string; +} + +export const initialState = {}; + +export const {{camelCase ModuleName}}Reducer = createReducer<{{ModuleName}}State, {{ModuleName}}Action>(initialState, { + SET_VALUE: (state, action) => ({ + ...state, + value: action.payload.value, + }), +}); diff --git a/plop_templates/module/rootReducer/RootReducer.template b/plop_templates/module/rootReducer/RootReducer.template new file mode 100644 index 00000000000..d8a7c32b6d6 --- /dev/null +++ b/plop_templates/module/rootReducer/RootReducer.template @@ -0,0 +1,2 @@ +$1 {{camelCase ModuleName}}: localStoragePersist('{{camelCase ModuleName}}', {{camelCase ModuleName}}Reducer, []), +}); \ No newline at end of file diff --git a/plop_templates/module/rootReducer/RootReducerType.template b/plop_templates/module/rootReducer/RootReducerType.template new file mode 100644 index 00000000000..6dc294e90b4 --- /dev/null +++ b/plop_templates/module/rootReducer/RootReducerType.template @@ -0,0 +1,2 @@ +$1 {{camelCase ModuleName}}: Reducer<{{ModuleName}}State, {{ModuleName}}Action>; +}; \ No newline at end of file diff --git a/plop_templates/module/rootReducer/RootState.template b/plop_templates/module/rootReducer/RootState.template new file mode 100644 index 00000000000..2cdbe81b90f --- /dev/null +++ b/plop_templates/module/rootReducer/RootState.template @@ -0,0 +1,2 @@ +$1 {{camelCase ModuleName}}: {{ModuleName}}State; +} \ No newline at end of file diff --git a/plop_templates/module/rootReducer/import.template b/plop_templates/module/rootReducer/import.template new file mode 100644 index 00000000000..3f6eb3dd531 --- /dev/null +++ b/plop_templates/module/rootReducer/import.template @@ -0,0 +1,3 @@ +$1 +import { {{camelCase ModuleName}}Reducer, {{ModuleName}}State } from './{{ModuleName}}/reducer'; +$2import { {{ModuleName}}Action } from './{{ModuleName}}'; diff --git a/plop_templates/module/selectors.template b/plop_templates/module/selectors.template new file mode 100644 index 00000000000..2246de15156 --- /dev/null +++ b/plop_templates/module/selectors.template @@ -0,0 +1,5 @@ +import { RootState } from '../rootReducer'; + +export const valueSelector = (state: RootState): string | undefined => { + return state.{{camelCase ModuleName}}.value; +}; diff --git a/plop_templates/page/RootNavigator/RootNavigator.template b/plop_templates/page/RootNavigator/RootNavigator.template new file mode 100644 index 00000000000..7c20d5f9b43 --- /dev/null +++ b/plop_templates/page/RootNavigator/RootNavigator.template @@ -0,0 +1,2 @@ + + $1 \ No newline at end of file diff --git a/plop_templates/page/RootNavigator/RootStackParamList.template b/plop_templates/page/RootNavigator/RootStackParamList.template new file mode 100644 index 00000000000..1613974bd52 --- /dev/null +++ b/plop_templates/page/RootNavigator/RootStackParamList.template @@ -0,0 +1,2 @@ +$1 {{PageName}}: undefined; +}; \ No newline at end of file diff --git a/plop_templates/page/RootNavigator/import.template b/plop_templates/page/RootNavigator/import.template new file mode 100644 index 00000000000..41fa712a3e0 --- /dev/null +++ b/plop_templates/page/RootNavigator/import.template @@ -0,0 +1 @@ +import { {{PageName}},$1 \ No newline at end of file diff --git a/plop_templates/page/index.template b/plop_templates/page/index.template new file mode 100644 index 00000000000..bab8e46a50b --- /dev/null +++ b/plop_templates/page/index.template @@ -0,0 +1 @@ +export { {{ PageName }} } from './{{ PageName }}.component'; diff --git a/plop_templates/page/page.template b/plop_templates/page/page.template new file mode 100644 index 00000000000..961b1345061 --- /dev/null +++ b/plop_templates/page/page.template @@ -0,0 +1,17 @@ +import React, { FunctionComponent } from 'react'; +import { StyleSheet, View } from 'react-native'; +import { StackNavigationProp } from '@react-navigation/stack'; + +import { RootStackParamList } from '../../navigation'; + +type {{PageName}}ScreenNavigationProp = StackNavigationProp; + +type Props = { + navigation: {{PageName}}ScreenNavigationProp; +}; + +const styles = StyleSheet.create({ + container: { flex: 1 }, +}); + +export const {{PageName}}: FunctionComponent = () => ; diff --git a/plop_templates/page/test.template b/plop_templates/page/test.template new file mode 100644 index 00000000000..95466012286 --- /dev/null +++ b/plop_templates/page/test.template @@ -0,0 +1,14 @@ +import React from 'react'; +import { render } from 'react-native-testing-library'; +import { {{ PageName }} } from '../{{ PageName }}.component'; + +describe('{{ PageName }} page', () => { + const navigation = { + navigate: jest.fn(), + } as any; // eslint-disable-line @typescript-eslint/no-explicit-any + + it('renders correctly', () => { + const {{camelCase PageName}} = render(<{{PageName}} navigation={navigation} />); + expect({{camelCase PageName}}).toMatchSnapshot(); + }); +}); diff --git a/plopfile.ts b/plopfile.ts new file mode 100644 index 00000000000..f7542b145be --- /dev/null +++ b/plopfile.ts @@ -0,0 +1,155 @@ +import { NodePlopAPI } from 'plop'; + +export default function (plop: NodePlopAPI) { + // Atom generator + plop.setGenerator('atom', { + description: 'add a new atom', + prompts: [ + { + type: 'input', + name: 'AtomName', + message: 'Atom name?', + validate: (input) => { + if (input[0] === input[0].toLowerCase()) return 'Please capitalize'; + return true; + }, + }, + ], + actions: [ + { + type: 'add', + path: 'src/atoms/{{AtomName}}/{{AtomName}}.atom.tsx', + templateFile: 'plop_templates/atom/atom.template', + }, + { + type: 'add', + path: 'src/atoms/{{AtomName}}/index.ts', + templateFile: 'plop_templates/atom/index.template', + }, + { + type: 'add', + path: 'src/atoms/{{AtomName}}/__tests__/{{AtomName}}.atom.test.tsx', + templateFile: 'plop_templates/atom/test.template', + }, + ], + }); + + // Page generator + plop.setGenerator('page', { + description: 'add a new page', + prompts: [ + { + type: 'input', + name: 'PageName', + message: 'Page name?', + validate: (input) => { + if (input[0] === input[0].toLowerCase()) return 'Please capitalize'; + return true; + }, + }, + ], + actions: [ + { + type: 'add', + path: 'src/pages/{{PageName}}/{{PageName}}.component.tsx', + templateFile: 'plop_templates/page/page.template', + }, + { + type: 'add', + path: 'src/pages/{{PageName}}/index.ts', + templateFile: 'plop_templates/page/index.template', + }, + { + type: 'add', + path: 'src/pages/{{PageName}}/__tests__/{{PageName}}.component.test.tsx', + templateFile: 'plop_templates/page/test.template', + }, + { + type: 'modify', + path: 'src/pages/index.ts', + pattern: /((export \* from '\.\/\w+';\n?)+)/, + template: `$1export * from './{{PageName}}';\n`, + }, + { + type: 'modify', + path: 'src/navigation/RootNavigator.tsx', + pattern: /(export type RootStackParamList = {\n( *\w*:.*;\n)+)};/, + templateFile: 'plop_templates/page/RootNavigator/RootStackParamList.template', + }, + { + type: 'modify', + path: 'src/navigation/RootNavigator.tsx', + pattern: /(\n {6}<\/RootStack\.Navigator>)/, + templateFile: 'plop_templates/page/RootNavigator/RootNavigator.template', + }, + { + type: 'modify', + path: 'src/navigation/RootNavigator.tsx', + pattern: /import {([\w\n ,]*} from '..\/pages';)/, + templateFile: 'plop_templates/page/RootNavigator/import.template', + }, + ], + }); + + // Redux module generator + plop.setGenerator('module', { + description: 'add a new Redux module', + prompts: [ + { + type: 'input', + name: 'ModuleName', + message: 'Module name?', + validate: (input) => { + if (input[0] === input[0].toLowerCase()) return 'Please capitalize'; + return true; + }, + }, + ], + actions: [ + { + type: 'add', + path: 'src/redux/{{ModuleName}}/index.ts', + templateFile: 'plop_templates/module/index.template', + }, + { + type: 'add', + path: 'src/redux/{{ModuleName}}/selectors.ts', + templateFile: 'plop_templates/module/selectors.template', + }, + { + type: 'add', + path: 'src/redux/{{ModuleName}}/reducer.ts', + templateFile: 'plop_templates/module/reducer.template', + }, + { + type: 'add', + path: 'src/redux/{{ModuleName}}/actions.ts', + templateFile: 'plop_templates/module/actions.template', + }, + { + type: 'modify', + path: 'src/redux/rootReducer.ts', + pattern: /(import.*from.*\/reducer.*)\n((import.*Action.*from '\.\/.*\n)+)/, + templateFile: 'plop_templates/module/rootReducer/import.template', + }, + { + type: 'modify', + path: 'src/redux/rootReducer.ts', + pattern: /(export interface RootState {\n( *\w+: \w+State;\n?)+)}/, + templateFile: 'plop_templates/module/rootReducer/RootState.template', + }, + { + type: 'modify', + path: 'src/redux/rootReducer.ts', + pattern: /(export type RootReducer = {\n( *\w+: Reducer<\w+State, \w+Action>;\n?)+)};/, + templateFile: 'plop_templates/module/rootReducer/RootReducerType.template', + }, + { + type: 'modify', + path: 'src/redux/rootReducer.ts', + pattern: /(export const rootReducer = combineReducers\({\n( *\w+: (localStoragePersist|sensitivePersist)\('\w+', \w+Reducer(, \[\])?\),(\n)?)+)}\);/, + templateFile: 'plop_templates/module/rootReducer/RootReducer.template', + }, + ], + }); +} diff --git a/react-native.config.js b/react-native.config.js new file mode 100644 index 00000000000..05eb78cf868 --- /dev/null +++ b/react-native.config.js @@ -0,0 +1,15 @@ +module.exports = { + dependencies: { + // disable autolinking for these unsupported libraries + // @codepush + 'react-native-code-push': { + platforms: { + android: { + packageImportPath: 'import com.microsoft.codepush.react.CodePush;', + packageInstance: + 'new CodePush(BuildConfig.CODEPUSH_KEY, this.getApplication(), BuildConfig.DEBUG)', + }, + }, + }, + }, +}; diff --git a/rn-cli.config.js b/rn-cli.config.js new file mode 100644 index 00000000000..f42e993c5b2 --- /dev/null +++ b/rn-cli.config.js @@ -0,0 +1,8 @@ +module.exports = { + getTransformModulePath() { + return require.resolve('react-native-typescript-transformer'); + }, + getSourceExts() { + return ['ts', 'tsx']; + }, +}; diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100644 index 00000000000..50ba6594454 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,116 @@ +#! /bin/bash +set -e +RED='\033[0;31m' +BLUE='\033[0;34m' +CYAN='\033[0;36m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +NO_COLOR='\033[0m' + +PROJECT_DIR="$( dirname "${BASH_SOURCE[0]}" )/.." +CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD` + +invalid_option_val() { + echo >&2 "Invalid option value \"$OPTARG\""; print_usage; exit 1; +} + +error(){ + echo -e >&2 "${RED}$1${NO_COLOR}" + exit 1 +} + +success(){ + echo -e "✅ ${GREEN}$1${NO_COLOR}" +} + +warn(){ + echo -e "⚠️ ${YELLOW}$1${NO_COLOR}" + + if [ $DEV -eq 0 ]; then + exit 1 + fi +} + +print_usage(){ + echo "Usage : ./scripts/deploy [-e ] [-t hard|soft] [-o ios|android] [-h] + +Options: + -e Environement to deploy, default staging + -t Deployment type for feature envs, default soft + -o OS to deploy, default ios and android + -h Display this usage + " +} + +DEV=0 +APP_ENV="staging" +APP_OS="ios and android" + +DEPLOY_TYPE="soft" + + +check_environment(){ + CURRENT_BRANCH=`git rev-parse --abbrev-ref HEAD` + + if [ "$CURRENT_BRANCH" != "master" ] + then + warn "Wrong branch, checkout master to deploy to $APP_ENV." + else + success "Deploying to $APP_ENV." + fi +} + + +while getopts ":e:o:t:h:d" opt; do + case $opt in + e) APP_ENV="$OPTARG" ;; + o) if [[ $OPTARG =~ ^(ios|android)$ ]]; then APP_OS="$OPTARG"; else invalid_option_val; fi;; + t) if [[ $OPTARG =~ ^(hard|soft)$ ]]; then DEPLOY_TYPE="$OPTARG"; else invalid_option_val; fi;; + h) print_usage; exit;; + d) DEV=1 ;; + \?) error "Invalid option -$OPTARG"; exit 1;; + esac +done + +[[ -z $(git status -s) ]] || warn 'Please make sure you deploy with no changes or untracked files. You can run *git stash --include-untracked*.' + +check_environment $APP_ENV + +if [ $DEPLOY_TYPE == "hard" ]; then + echo -e "${BLUE}* * * * *" + echo -e "👷 Hard-Deploy" + echo -e "* * * * *${NO_COLOR}" + if [[ $APP_OS != "android" ]]; then + echo -e "${GREEN}- - - - -" + echo -e "Fastlane 🍎 iOS $APP_ENV" + echo -e "- - - - -${NO_COLOR}" + bundle exec fastlane ios deploy --env=$APP_ENV + fi + if [[ $APP_OS != "ios" ]]; then + echo -e "${YELLOW}- - - - -" + echo "Fastlane 🤖 Android $APP_ENV" + echo -e "- - - - -${NO_COLOR}" + bundle exec fastlane android deploy --env=$APP_ENV + fi +fi + +if [ $DEPLOY_TYPE == "soft" ]; then + echo -e "${CYAN}* * * * *" + echo -e "🍦 Soft-Deploy" + echo -e "* * * * *${NO_COLOR}" + + if [[ $APP_OS != "android" ]]; then + echo -e "${GREEN}- - - - -" + echo -e "Codepush 🍎 iOS ${APP_ENV}" + echo -e "- - - - -${NO_COLOR}" + bundle exec fastlane ios deploy codepush: --env=$APP_ENV + fi + if [[ $APP_OS != "ios" ]]; then + echo -e "${YELLOW}- - - - -" + echo -e "Codepush 🤖 Android ${APP_ENV}" + echo -e "- - - - -${NO_COLOR}" + bundle exec fastlane android deploy codepush: --env=$APP_ENV + fi +fi + +success "📦 Deploy succeeded." diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 00000000000..677e625ffc2 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,40 @@ +import React, { FunctionComponent } from 'react'; +import CodePush from 'react-native-code-push'; // @codepush +import { RootNavigator } from './navigation'; +import { withRedux } from './hoc/withRedux'; // @redux +import { env } from './environment'; +import 'react-native-gesture-handler'; // @react-navigation +import { I18nProvider } from '@lingui/react'; //@translations +import { i18n } from './lib/i18n'; //@translations + +const codePushOptionsManual = { + updateDialog: true, + installMode: CodePush.InstallMode.IMMEDIATE, + checkFrequency: CodePush.CheckFrequency.MANUAL, +}; + +const codePushOptionsAuto = { + installMode: CodePush.InstallMode.ON_NEXT_RESTART, + checkFrequency: CodePush.CheckFrequency.ON_APP_START, +}; + +const AppContainer = withRedux(RootNavigator); + +let App = undefined; + +const AppComponent: FunctionComponent = () => { + return ( + + + + ); +}; + +// @codepush +App = env.FEATURE_FLAG_CODE_PUSH + ? CodePush(env.FEATURE_FLAG_CODE_PUSH_MANUAL ? codePushOptionsManual : codePushOptionsAuto)( + AppComponent + ) + : AppComponent; + +export { App }; diff --git a/src/__tests__/App.test.tsx b/src/__tests__/App.test.tsx new file mode 100644 index 00000000000..5d650da290c --- /dev/null +++ b/src/__tests__/App.test.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import TestRenderer from 'react-test-renderer'; +import { App } from '../App'; +import { tick } from './utils.test'; + +describe('App', () => { + it('instantiate the application', async () => { + TestRenderer.create(); + await TestRenderer.act(async () => { + await tick(); + }); + }); +}); diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts new file mode 100644 index 00000000000..536e0c25f67 --- /dev/null +++ b/src/__tests__/utils.test.ts @@ -0,0 +1,19 @@ +export function tick() { + return new Promise((resolve) => + setTimeout(() => { + resolve(); + }, 0) + ); +} + +describe('Utils', () => { + describe('tick', () => { + it('awaits next tick', async () => { + setTimeout(() => { + expect(true).toBeTruthy(); + }, 0); + await tick(); + expect.assertions(1); + }); + }); +}); diff --git a/src/atoms/CheatCodesButton/CheatCodesButton.component.tsx b/src/atoms/CheatCodesButton/CheatCodesButton.component.tsx new file mode 100644 index 00000000000..c29ea37f17f --- /dev/null +++ b/src/atoms/CheatCodesButton/CheatCodesButton.component.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { Button } from 'react-native'; +import { StackNavigationProp } from '@react-navigation/stack'; + +import { RootStackParamList } from '../../navigation'; + +type LoginScreenNavigationProp = StackNavigationProp; +type Props = { + navigation: LoginScreenNavigationProp; +}; +export const CheatCodesButton = (props: Props) => ( + + +); diff --git a/src/pages/CheatCodes/__tests__/CheatCodes.component.test.tsx b/src/pages/CheatCodes/__tests__/CheatCodes.component.test.tsx new file mode 100644 index 00000000000..7e4ba683239 --- /dev/null +++ b/src/pages/CheatCodes/__tests__/CheatCodes.component.test.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { render, act, fireEvent } from 'react-native-testing-library'; +import { CheatCodes } from '../CheatCodes.component'; +import { env } from '../../../environment'; + +jest.mock('../../../environment', () => ({ + env: { + FEATURE_FLAG_CODE_PUSH: true, + }, +})); + +describe('CheatCodes component', () => { + const navigation = { + dispatch: jest.fn(), + } as any; // eslint-disable-line @typescript-eslint/no-explicit-any + + it('should have a button to go to the Login', async () => { + const cheatCodes = render(); + expect(cheatCodes).toMatchSnapshot(); + }); + + it('should have a button to go to the Login', async () => { + const cheatCodes = render(); + + cheatCodes.getByText('Check update'); + const Button = cheatCodes.getByTestId('enLanguageButton'); + act(() => { + fireEvent.press(Button); + }); + + expect(navigation.dispatch).toHaveBeenCalled(); + }); + + it('should have a button to go to the Login', async () => { + env.FEATURE_FLAG_CODE_PUSH = false; + const cheatCodes = render(); + expect(() => cheatCodes.getByText('Check update')).toThrowError(); + }); +}); diff --git a/src/pages/CheatCodes/__tests__/__snapshots__/CheatCodes.component.test.tsx.snap b/src/pages/CheatCodes/__tests__/__snapshots__/CheatCodes.component.test.tsx.snap new file mode 100644 index 00000000000..367e5ca5862 --- /dev/null +++ b/src/pages/CheatCodes/__tests__/__snapshots__/CheatCodes.component.test.tsx.snap @@ -0,0 +1,212 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`CheatCodes component should have a button to go to the Login 1`] = ` + + + + + Crash the app + + + + + + + + FR + + + + + + + EN + + + + + + + Check update + + + + + + Go to storybook + + + + +`; diff --git a/src/pages/CheatCodes/components/ChangeLanguage.tsx b/src/pages/CheatCodes/components/ChangeLanguage.tsx new file mode 100644 index 00000000000..4e208b23101 --- /dev/null +++ b/src/pages/CheatCodes/components/ChangeLanguage.tsx @@ -0,0 +1,37 @@ +import React, { useState } from 'react'; +import { Button, View } from 'react-native'; +import { i18n } from '../../../lib/i18n'; + +interface Props { + onLanguageChange?: () => void; +} + +export const ChangeLanguage = (props: Props) => { + const [language, setLanguage] = useState(i18n.language); + + const changeLanguage = (value: string) => () => { + i18n.activate(value); + setLanguage(value); + + if (props.onLanguageChange) { + props.onLanguageChange(); + } + }; + + return ( + +