diff --git a/.buckconfig b/.buckconfig deleted file mode 100644 index 934256cb29d..00000000000 --- a/.buckconfig +++ /dev/null @@ -1,6 +0,0 @@ - -[android] - target = Google Inc.:Google APIs:23 - -[maven_repositories] - central = https://repo1.maven.org/maven2 diff --git a/.env.local b/.env.local index 9e3fd6931e9..09bc595fd18 100644 --- a/.env.local +++ b/.env.local @@ -20,8 +20,6 @@ FETCH_PAYMENT_MANAGER_TIMEOUT_MS=16000 FETCH_MAX_RETRIES=3 # number of workers to fetch message TOT_MESSAGE_FETCH_WORKERS=5 -# number of workers to fetch service -TOT_SERVICE_FETCH_WORKERS=5 # shuffle pin pad to proceed with the payment SHUFFLE_PINPAD_ON_PAYMENT=NO # Repository of app content @@ -41,8 +39,6 @@ PLAYGROUNDS_ENABLED=YES BONUS_API_URL_PREFIX='http://127.0.0.1:3000/bonus' BONUS_API_SIT_BASEURL='https://api-io.dev.cstar.pagopa.it' BONUS_API_UAT_BASEURL='https://api-io.uat.cstar.pagopa.it' -# local services web url -LOCAL_SERVICE_WEB_URL='http://127.0.0.1:3000/services_web_view' # EU Covid Certificate EU_COVID_CERT_ENABLED=YES # Zendesk configuration diff --git a/.env.production b/.env.production index f5c7a007cc5..4c4075ebff7 100644 --- a/.env.production +++ b/.env.production @@ -20,8 +20,6 @@ FETCH_PAYMENT_MANAGER_TIMEOUT_MS=16000 FETCH_MAX_RETRIES=3 # number of workers to fetch message TOT_MESSAGE_FETCH_WORKERS=5 -# number of workers to fetch service -TOT_SERVICE_FETCH_WORKERS=5 # shuffle pin pad to proceed with the payment SHUFFLE_PINPAD_ON_PAYMENT=NO # Repository of app content @@ -41,8 +39,6 @@ PLAYGROUNDS_ENABLED=YES BONUS_API_URL_PREFIX=https://api-io.cstar.pagopa.it BONUS_API_SIT_BASEURL='https://api-io.dev.cstar.pagopa.it' BONUS_API_UAT_BASEURL='https://api-io.uat.cstar.pagopa.it' -# local services web url -LOCAL_SERVICE_WEB_URL='https://io.italia.it/app-content/enti-servizi.html' # EU Covid Certificate EU_COVID_CERT_ENABLED=YES # Zendesk configuration diff --git a/.eslintrc.js b/.eslintrc.js index 0356c78ae9a..7e52c7daf82 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -8,7 +8,8 @@ module.exports = { "plugin:react-hooks/recommended", "plugin:react-native-a11y/all", "plugin:sonarjs/recommended", - "prettier" + "prettier", + "@react-native" ], parser: "@typescript-eslint/parser", parserOptions: { @@ -26,9 +27,11 @@ module.exports = { "import", "functional", "sonarjs", - "@jambit/typed-redux-saga" + "@jambit/typed-redux-saga", + "@stylistic/eslint-plugin-js" ], rules: { + "comma-dangle": ["error", "never"], "no-case-declarations": "off", "no-inner-declarations": "off", "prefer-const": "error", @@ -47,7 +50,9 @@ module.exports = { "no-console": "error", "no-caller": "error", "no-bitwise": "error", + "no-void": "off", "no-duplicate-imports": "error", + quotes: "off", eqeqeq: ["error", "smart"], "max-classes-per-file": ["error", 1], "guard-for-in": "error", @@ -94,6 +99,14 @@ module.exports = { "react/display-name": "off", "react/jsx-key": "error", "react/jsx-no-bind": ["error", { allowArrowFunctions: true }], + "react/no-unstable-nested-components": [ + "off", + { + allowAsProps: true + } + ], + "react/no-direct-mutation-state": "off", + "react/require-render-return": "off", "functional/no-let": "error", "functional/immutable-data": "error", "sonarjs/no-small-switch": "off", @@ -107,7 +120,23 @@ module.exports = { "off" /* Error when you launch the lint command */, "react-native/no-single-element-style-arrays": "warn", /* Too much verbose. It also requires a lot of effort in the main repo */ - "react-native-a11y/has-accessibility-hint": "off" + "react-native-a11y/has-accessibility-hint": "off", + "no-restricted-imports": [ + "error", + { + "paths": [ + { + name: "i18n-js", + message: 'Importing I18n from "i18n-js" is not allowed. Import it from "ts/i18n.ts" instead.', + }, + { + name: "@pagopa/ts-commons", + importNames: ["pot"], + message: 'Importing { pot } from "@pagopa/ts-commons" is not allowed. Use \'import * as pot from "@pagopa/ts-commons/lib/pot"\' instead.', + } + ], + } + ] }, env: { "react-native/react-native": true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50536ccf973..2496a0798fa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,7 +32,6 @@ jobs: - id: build-release-android run: | ./scripts/android-release.sh ./android/app - yarn bundle:android-release cd android && bundle exec fastlane alpha bundle exec fastlane promote_internal_to_alpha shell: bash diff --git a/.gitignore b/.gitignore index 2a5b814922d..937e84b7133 100644 --- a/.gitignore +++ b/.gitignore @@ -104,4 +104,10 @@ android/app/google-services.json GeneratedDotEnv.m # XState Typegen -**/*.typegen.* \ No newline at end of file +**/*.typegen.* + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* + +# yarn cache dir +.yarn/cache \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index c1f2c0a75a2..379cbe942e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,50 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [2.64.0-rc.1](https://github.com/pagopa/io-app/compare/2.63.0-rc.5...2.64.0-rc.1) (2024-07-11) + + +### Chores + +* **release:** 2.64.0-rc.0 ([01fbf43](https://github.com/pagopa/io-app/commit/01fbf43fd34302b5f153964c802ef5b6c6a633d9)) + +## [2.64.0-rc.0](https://github.com/pagopa/io-app/compare/2.63.0-rc.5...2.64.0-rc.0) (2024-07-10) + + +### Features + +* [[IOCOM-1413](https://pagopa.atlassian.net/browse/IOCOM-1413)] Archiving/Restoring of user messages, new Messages Home ([#5935](https://github.com/pagopa/io-app/issues/5935)) ([b41b36e](https://github.com/pagopa/io-app/commit/b41b36e579136268a4274f4fdcb23480147a40eb)) +* [[IOCOM-1417](https://pagopa.atlassian.net/browse/IOCOM-1417)] Security suggestions bottom sheet, new messages' home ([#5895](https://github.com/pagopa/io-app/issues/5895)) ([a09ddad](https://github.com/pagopa/io-app/commit/a09ddade55840322c77c876e611d1ab5ad11dc03)) +* [[IOCOM-840](https://pagopa.atlassian.net/browse/IOCOM-840)] Preconditions bottom sheet, new DS ([#5912](https://github.com/pagopa/io-app/issues/5912)) ([153b621](https://github.com/pagopa/io-app/commit/153b6212e7cadfb8a322535e206b69f26378798a)) +* [[IOPID-1528](https://pagopa.atlassian.net/browse/IOPID-1528)] Add new DS on profile calendar preferences ([#5915](https://github.com/pagopa/io-app/issues/5915)) ([a95129e](https://github.com/pagopa/io-app/commit/a95129ee996e4231403f9ea1d41d5f719fa2ae6d)) +* [[IOPID-1534](https://pagopa.atlassian.net/browse/IOPID-1534)] Add new DS on Privacy and ToS screen ([#5921](https://github.com/pagopa/io-app/issues/5921)) ([78cf67d](https://github.com/pagopa/io-app/commit/78cf67d7eb8c3f78958f71c1c7161548a4e6efb3)) +* [[IOPID-1550](https://pagopa.atlassian.net/browse/IOPID-1550)] - Download User Data new DS integration ([#5923](https://github.com/pagopa/io-app/issues/5923)) ([69c798e](https://github.com/pagopa/io-app/commit/69c798ee762230bc0435cbb27bd27da5dd8b04a5)) +* [[IOPID-1551](https://pagopa.atlassian.net/browse/IOPID-1551)] - Integrate new DS in account deletion flow ([#5916](https://github.com/pagopa/io-app/issues/5916)) ([d607088](https://github.com/pagopa/io-app/commit/d607088c4ec1dade076056859cd9f77fdc4b2392)) +* [[IOPID-1938](https://pagopa.atlassian.net/browse/IOPID-1938)] `withThirdPartyRefreshApiCall` ([#5901](https://github.com/pagopa/io-app/issues/5901)) ([dc2185a](https://github.com/pagopa/io-app/commit/dc2185a092c335ae8af31a88bcf0e6c355fc638e)) +* [[IOPLT-551](https://pagopa.atlassian.net/browse/IOPLT-551)] Upgrade react-native to 0.72 ([#5864](https://github.com/pagopa/io-app/issues/5864)) ([c8751b7](https://github.com/pagopa/io-app/commit/c8751b7389e2840a088f04d65098bda475cf404d)) +* [[PE-617](https://pagopa.atlassian.net/browse/PE-617)] Change labels in CGN Partner detail screen ([#5931](https://github.com/pagopa/io-app/issues/5931)) ([e91ba28](https://github.com/pagopa/io-app/commit/e91ba286f0e7cfdba3ebb27472b37f9332d16ec2)) +* **IT Wallet:** [[SIW-1286](https://pagopa.atlassian.net/browse/SIW-1286)] New IT Wallet discovery screen ([#5909](https://github.com/pagopa/io-app/issues/5909)) ([1f3964b](https://github.com/pagopa/io-app/commit/1f3964b18b5a0a51aa6d9718306e1cad6175b938)) + + +### Bug Fixes + +* [IOPID-1552, IOPID-1553, IOPID-1528] remediation english copy ([#5928](https://github.com/pagopa/io-app/issues/5928)) ([e274190](https://github.com/pagopa/io-app/commit/e274190dcd21c815b611587fae4c9c8f8807a9bd)) + + +### Chores + +* [[IOBP-727](https://pagopa.atlassian.net/browse/IOBP-727)] Change label into transaction details error ([#5941](https://github.com/pagopa/io-app/issues/5941)) ([01207a8](https://github.com/pagopa/io-app/commit/01207a8ba055a8489cf8e4514a48922f16bb1c89)) +* [[IOCOM-1561](https://pagopa.atlassian.net/browse/IOCOM-1561)] FIMS history saga ([#5920](https://github.com/pagopa/io-app/issues/5920)) ([8542d6d](https://github.com/pagopa/io-app/commit/8542d6dc2f9abbd81d7dcaddd7b95bea68e02fe3)) +* [[IOCOM-1578](https://pagopa.atlassian.net/browse/IOCOM-1578)] Updated types for FIMS consents, latest Http Client version ([#5927](https://github.com/pagopa/io-app/issues/5927)) ([bda8018](https://github.com/pagopa/io-app/commit/bda801850e8dd20b1df46caaa00c8ff4bf3df819)) +* [[IOPAE-1100](https://pagopa.atlassian.net/browse/IOPAE-1100),[IOPAE-1282](https://pagopa.atlassian.net/browse/IOPAE-1282),[IOPAE-1285](https://pagopa.atlassian.net/browse/IOPAE-1285)] Remove old implementation of services ([#5898](https://github.com/pagopa/io-app/issues/5898)) ([03a24e6](https://github.com/pagopa/io-app/commit/03a24e66294d03986a2d855aa1f8b368e78520f3)) +* [[IOPAE-1191](https://pagopa.atlassian.net/browse/IOPAE-1191)] Removal of `UserMetadata` ([#5942](https://github.com/pagopa/io-app/issues/5942)) ([0036f49](https://github.com/pagopa/io-app/commit/0036f4929d547ffed8d50b2472e87f7e36440500)) +* [[IOPAE-1284](https://pagopa.atlassian.net/browse/IOPAE-1284)] Update services Mixpanel analytics events ([#5907](https://github.com/pagopa/io-app/issues/5907)) ([fafd081](https://github.com/pagopa/io-app/commit/fafd0815542bebc35bdad74f922ea89d3133f338)) +* [[IOPLT-599](https://pagopa.atlassian.net/browse/IOPLT-599)] Replace legacy StatusContent with Alert component for SectionStatus advices ([#5924](https://github.com/pagopa/io-app/issues/5924)) ([bb96fd2](https://github.com/pagopa/io-app/commit/bb96fd21d2e136542b6b8b5b316c221cc90190d4)) +* **Cross:** [[IOAPPX-317](https://pagopa.atlassian.net/browse/IOAPPX-317)] Refactor `DeveloperTestEnvironmentSection` to use `FlatList` ([#5845](https://github.com/pagopa/io-app/issues/5845)) ([e02827d](https://github.com/pagopa/io-app/commit/e02827ded2d5188f99e6ccf6aed5d6eed4073335)) +* **Cross:** [[IOAPPX-325](https://pagopa.atlassian.net/browse/IOAPPX-325)] Fix wrong behavior of `FooterActions`' sticky example on Android devices ([#5875](https://github.com/pagopa/io-app/issues/5875)) ([c225aa0](https://github.com/pagopa/io-app/commit/c225aa0f269a5e90f33e740be6349aa1188a6829)) +* **IT Wallet:** [[SIW-1287](https://pagopa.atlassian.net/browse/SIW-1287)] Update credential preview screens ([#5910](https://github.com/pagopa/io-app/issues/5910)) ([1a3d4bc](https://github.com/pagopa/io-app/commit/1a3d4bc5f20a210fe3c0db6639171dc280872cfe)) +* **IT Wallet:** [[SIW-1312](https://pagopa.atlassian.net/browse/SIW-1312)] IT Wallet playgrounds refactoring ([#5929](https://github.com/pagopa/io-app/issues/5929)) ([0d172f3](https://github.com/pagopa/io-app/commit/0d172f3dc699ee7dd7e50bf92aa6a3ad291b5d90)) + ## [2.63.0-rc.5](https://github.com/pagopa/io-app/compare/2.63.0-rc.4...2.63.0-rc.5) (2024-07-01) diff --git a/Gemfile b/Gemfile index 956c32566e2..dad4449e116 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ source "https://rubygems.org" # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version ruby '>=2.6.10' -gem "cocoapods", ">= 1.15.2" +# Cocoapods 1.15 introduced a bug which break the build. We will remove the upper +# bound in the template on Cocoapods with next React Native release. +gem 'cocoapods', '>= 1.13', '< 1.15' gem "fastlane", "~> 2.212.2" gem 'activesupport', '>= 6.1.7.3', '< 7.1.0' diff --git a/Gemfile.lock b/Gemfile.lock index 0079aaf3314..3f7badcd45f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,10 +36,10 @@ GEM babosa (1.0.4) base64 (0.2.0) claide (1.1.0) - cocoapods (1.15.2) + cocoapods (1.14.3) addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.15.2) + cocoapods-core (= 1.14.3) cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 2.1, < 3.0) cocoapods-plugins (>= 1.0.0, < 2.0) @@ -54,7 +54,7 @@ GEM nap (~> 1.0) ruby-macho (>= 2.3.0, < 3.0) xcodeproj (>= 1.23.0, < 2.0) - cocoapods-core (1.15.2) + cocoapods-core (1.14.3) activesupport (>= 5.0, < 8) addressable (~> 2.8) algoliasearch (~> 1.0) @@ -273,7 +273,7 @@ PLATFORMS DEPENDENCIES activesupport (>= 6.1.7.3, < 7.1.0) - cocoapods (>= 1.15.2) + cocoapods (>= 1.13, < 1.15) fastlane (~> 2.212.2) RUBY VERSION diff --git a/android/app/BUCK b/android/app/BUCK deleted file mode 100644 index 5dd140b1e14..00000000000 --- a/android/app/BUCK +++ /dev/null @@ -1,55 +0,0 @@ -# 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 = "it.pagopa.io.app", -) - -android_resource( - name = "res", - package = "it.pagopa.io.app", - 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 index 9856500b695..25a78fb5916 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -4,81 +4,13 @@ project.ext.envConfigFiles = [ ] apply plugin: "com.android.application" -apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" +apply plugin: "com.facebook.react" -import com.android.build.OutputFile -import org.apache.tools.ant.taskdefs.condition.Os +apply from: project(':react-native-config').projectDir.getPath() + "/dotenv.gradle" /** - * 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. - * entryFile: "index.android.js", - * - * // 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: [] - * ] + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. */ // usage: ./gradlew assembleDebug -PbundleInDebug=true @@ -89,39 +21,74 @@ if (project.hasProperty(bundleInDebugPropertyName)) { bundleDebug = project.getProperty(bundleInDebugPropertyName) } -project.ext.react = [ - enableHermes: true, - bundleInDebug: bundleDebug -] - -apply from: "../../node_modules/react-native/react.gradle" - -/** - * 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 = true + react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '..' + // root = file("../") + // The folder where the react-native NPM package is. Default is ../node_modules/react-native + // reactNativeDir = file("../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen + // codegenDir = file("../node_modules/@react-native/codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js + // cliFile = file("../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] +} /** - * Run Proguard to shrink the Java bytecode in release builds. + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. */ def enableProguardInReleaseBuilds = false /** - * Architectures to build native code for. + * The preferred build flavor of JavaScriptCore (JSC) + * + * 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 reactNativeArchitectures() { - def value = project.getProperties().get("reactNativeArchitectures") - return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] -} +def jscFlavor = 'org.webkit:android-jsc:+' android { ndkVersion rootProject.ext.ndkVersion compileSdkVersion rootProject.ext.compileSdkVersion + + namespace "it.pagopa.io.app" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 @@ -145,69 +112,12 @@ android { applicationId "it.pagopa.io.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 100154828 - versionName "2.63.0.5" - buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString() - if (isNewArchitectureEnabled()) { - // We configure the CMake build only if you decide to opt-in for the New Architecture. - externalNativeBuild { - cmake { - arguments "-DPROJECT_BUILD_DIR=$buildDir", - "-DREACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid", - "-DREACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build", - "-DNODE_MODULES_DIR=$rootDir/../node_modules", - "-DANDROID_STL=c++_shared" - } - } - if (!enableSeparateBuildPerCPUArchitecture) { - ndk { - abiFilters (*reactNativeArchitectures()) - } - } - } + versionCode 100154830 + versionName "2.64.0.1" multiDexEnabled true // The resConfigs attribute will remove all not required localized resources while building the application, // including the localized resources from libraries. - if (isNewArchitectureEnabled()) { - // We configure the NDK build only if you decide to opt-in for the New Architecture. - externalNativeBuild { - cmake { - path "$projectDir/src/main/jni/CMakeLists.txt" - } - } - def reactAndroidProjectDir = project(':ReactAndroid').projectDir - def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) { - dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck") - from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") - into("$buildDir/react-ndk/exported") - } - def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) { - dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck") - from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib") - into("$buildDir/react-ndk/exported") - } - afterEvaluate { - // If you wish to add a custom TurboModule or component locally, - // you should uncomment this line. - // preBuild.dependsOn("generateCodegenArtifactsFromSchema") - preDebugBuild.dependsOn(packageReactNdkDebugLibs) - preReleaseBuild.dependsOn(packageReactNdkReleaseLibs) - // Due to a bug inside AGP, we have to explicitly set a dependency - // between configureCMakeDebug* tasks and the preBuild tasks. - // This can be removed once this is solved: https://issuetracker.google.com/issues/207403732 - configureCMakeRelWithDebInfo.dependsOn(preReleaseBuild) - configureCMakeDebug.dependsOn(preDebugBuild) - reactNativeArchitectures().each { architecture -> - tasks.findByName("configureCMakeDebug[${architecture}]")?.configure { - dependsOn("preDebugBuild") - } - tasks.findByName("configureCMakeRelWithDebInfo[${architecture}]")?.configure { - dependsOn("preReleaseBuild") - } - } - } - } } signingConfigs { release { @@ -219,14 +129,6 @@ android { } } } - splits { - abi { - reset() - enable enableSeparateBuildPerCPUArchitecture - universalApk true // If true, also generate a universal APK - include (*reactNativeArchitectures()) - } - } buildTypes { debug { signingConfig signingConfigs.debug @@ -242,19 +144,6 @@ android { signingConfig signingConfigs.release } } - // 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: - // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits - 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 = - defaultConfig.versionCode * 1000 + versionCodes.get(abi) - } - } - } // The Android App Bundle read this section to create different bundles bundle { @@ -275,58 +164,31 @@ android { } } -def jscFlavor = 'org.webkit:android-jsc:+' -def enableHermes = project.ext.react.get("enableHermes", false) - dependencies { implementation project(':jail-monkey') implementation project(':react-native-linear-gradient') implementation project(':react-native-share') - implementation fileTree(dir: "libs", include: ["*.jar"]) - //noinspection GradleDynamicVersion - implementation "com.facebook.react:react-native:+" - implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" - debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { - exclude group:'com.facebook.fbjni' - } + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") 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' - } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") implementation project(':react-native-cie') - implementation ("com.squareup.okhttp3:okhttp:4.9.2"){ - force = true - } + implementation ("com.squareup.okhttp3:okhttp:4.9.2") implementation 'com.squareup.okhttp3:logging-interceptor:4.9.2' - if (enableHermes) { - //noinspection GradleDynamicVersion - implementation("com.facebook.react:hermes-engine:+") { // From node_modules - exclude group:'com.facebook.fbjni' - } + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") } else { implementation jscFlavor } - if (isNewArchitectureEnabled()) { - // If new architecture is enabled, we let you build RN from source - // Otherwise we fallback to a prebuilt .aar bundled in the NPM package. - // This will be applied to all the imported transtitive dependency. - configurations.all { - resolutionStrategy.dependencySubstitution { - substitute(module("com.facebook.react:react-native")) - .using(project(":ReactAndroid")) - .because("On New Architecture we're building React Native from source") - substitute(module("com.facebook.react:hermes-engine")) - .using(project(":ReactAndroid:hermes-engine")) - .because("On New Architecture we're building Hermes from source") - } - } - } + implementation project(':react-native-fingerprint-scanner') implementation project(':react-native-art') implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.41" @@ -336,21 +198,6 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:2.1.4" } -// 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.implementation - into 'libs' -} - // Add the following line to the bottom of the file: apply plugin: 'com.google.gms.google-services' // Google Play services Gradle plugin apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) - -def isNewArchitectureEnabled() { - // To opt-in for the New Architecture, you can either: - // - Set `newArchEnabled` to true inside the `gradle.properties` file - // - Invoke gradle with `-newArchEnabled=true` - // - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true` - return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true" -} diff --git a/android/app/build_defs.bzl b/android/app/build_defs.bzl deleted file mode 100644 index fff270f8d1d..00000000000 --- a/android/app/build_defs.bzl +++ /dev/null @@ -1,19 +0,0 @@ -"""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/src/debug/java/it/pagopa/io/app/ReactNativeFlipper.java b/android/app/src/debug/java/it/pagopa/io/app/ReactNativeFlipper.java index 433b3f89a8f..4aa790ac460 100644 --- a/android/app/src/debug/java/it/pagopa/io/app/ReactNativeFlipper.java +++ b/android/app/src/debug/java/it/pagopa/io/app/ReactNativeFlipper.java @@ -23,12 +23,16 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.modules.network.NetworkingModule; import okhttp3.OkHttpClient; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the debug + * flavor of it. Here you can add your own plugins and customize the Flipper setup. + */ 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()); diff --git a/android/app/src/main/java/it/pagopa/io/app/MainActivity.java b/android/app/src/main/java/it/pagopa/io/app/MainActivity.java index 083570b8ae1..a943683a383 100644 --- a/android/app/src/main/java/it/pagopa/io/app/MainActivity.java +++ b/android/app/src/main/java/it/pagopa/io/app/MainActivity.java @@ -8,7 +8,8 @@ import com.facebook.react.ReactActivity; import com.facebook.react.ReactActivityDelegate; -import com.facebook.react.ReactRootView; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactActivityDelegate; import org.devio.rn.splashscreen.SplashScreen; public class MainActivity extends ReactActivity { @@ -35,22 +36,17 @@ protected void onCreate(Bundle savedInstanceState) { } } - public static class MainActivityDelegate extends ReactActivityDelegate { - public MainActivityDelegate(ReactActivity activity, String mainComponentName) { - super(activity, mainComponentName); - } - @Override - protected ReactRootView createRootView() { - ReactRootView reactRootView = new ReactRootView(getContext()); - // If you opted-in for the New Architecture, we enable the Fabric Renderer. - reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED); - return reactRootView; - } - @Override - protected boolean isConcurrentRootEnabled() { - // If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18). - // More on this on https://reactjs.org/blog/2022/03/29/react-v18.html - return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - } + /** + * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link + * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React + * (aka React 18) with two boolean flags. + */ + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new DefaultReactActivityDelegate( + this, + getMainComponentName(), + // If you opted-in for the New Architecture, we enable the Fabric Renderer. + DefaultNewArchitectureEntryPoint.getFabricEnabled()); } } diff --git a/android/app/src/main/java/it/pagopa/io/app/MainApplication.java b/android/app/src/main/java/it/pagopa/io/app/MainApplication.java index 8fbbda48385..fde21027b12 100644 --- a/android/app/src/main/java/it/pagopa/io/app/MainApplication.java +++ b/android/app/src/main/java/it/pagopa/io/app/MainApplication.java @@ -3,20 +3,18 @@ import com.facebook.react.ReactApplication; import com.facebook.react.ReactNativeHost; import com.facebook.react.ReactPackage; -import com.facebook.react.config.ReactFeatureFlags; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactNativeHost; import com.facebook.soloader.SoLoader; -import it.pagopa.io.app.newarchitecture.MainApplicationReactNativeHost; import it.ipzs.cieidsdk.native_bridge.CiePackage; import com.reactnativecommunity.art.ARTPackage; import com.facebook.react.bridge.JSIModulePackage; -import com.swmansion.reanimated.ReanimatedJSIModulePackage; import com.facebook.react.PackageList; import android.app.Application; import android.content.Context; import com.facebook.react.ReactInstanceManager; -import java.lang.reflect.InvocationTargetException; import java.util.List; @@ -24,82 +22,50 @@ public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = - new ReactNativeHost(this) { + new DefaultReactNativeHost(this) { @Override public boolean getUseDeveloperSupport() { return BuildConfig.DEBUG; } - @Override - protected String getJSMainModuleName() { - return "index"; - } - - @Override - protected JSIModulePackage getJSIModulePackage() { - return new ReanimatedJSIModulePackage(); - } + @Override + protected String getJSMainModuleName() { + return "index"; + } - @Override - protected List getPackages() { - List packages = new PackageList(this).getPackages(); - packages.add(new CiePackage()); - packages.add(new ARTPackage()); - return packages; - } - }; + @Override + protected List getPackages() { + List packages = new PackageList(this).getPackages(); + packages.add(new CiePackage()); + packages.add(new ARTPackage()); + return packages; + } - private final ReactNativeHost mNewArchitectureNativeHost = - new MainApplicationReactNativeHost(this); + @Override + protected boolean isNewArchEnabled() { + return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + } + + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED; + } + }; @Override public ReactNativeHost getReactNativeHost() { - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - return mNewArchitectureNativeHost; - } else { - return mReactNativeHost; - } + return mReactNativeHost; } @Override public void onCreate() { super.onCreate(); - // If you opted-in for the New Architecture, we enable the TurboModule system - ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; SoLoader.init(this, /* native exopackage */ false); - initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + DefaultNewArchitectureEntryPoint.load(); + } + ReactNativeFlipper.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("it.pagopa.io.app.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/java/it/pagopa/io/app/newarchitecture/MainApplicationReactNativeHost.java b/android/app/src/main/java/it/pagopa/io/app/newarchitecture/MainApplicationReactNativeHost.java deleted file mode 100644 index c1e9f90fa72..00000000000 --- a/android/app/src/main/java/it/pagopa/io/app/newarchitecture/MainApplicationReactNativeHost.java +++ /dev/null @@ -1,103 +0,0 @@ -package it.pagopa.io.app.newarchitecture; -import android.app.Application; -import androidx.annotation.NonNull; -import com.facebook.react.PackageList; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; -import com.facebook.react.bridge.JSIModulePackage; -import com.facebook.react.bridge.JSIModuleProvider; -import com.facebook.react.bridge.JSIModuleSpec; -import com.facebook.react.bridge.JSIModuleType; -import com.facebook.react.bridge.JavaScriptContextHolder; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.react.bridge.UIManager; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.react.fabric.CoreComponentsRegistry; -import com.facebook.react.fabric.FabricJSIModuleProvider; -import com.facebook.react.fabric.ReactNativeConfig; -import com.facebook.react.uimanager.ViewManagerRegistry; -import it.pagopa.io.app.BuildConfig; -import it.pagopa.io.app.newarchitecture.components.MainComponentsRegistry; -import it.pagopa.io.app.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate; -import java.util.ArrayList; -import java.util.List; -/** - * A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both - * TurboModule delegates and the Fabric Renderer. - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -public class MainApplicationReactNativeHost extends ReactNativeHost { - public MainApplicationReactNativeHost(Application application) { - super(application); - } - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - @Override - protected List getPackages() { - List packages = new PackageList(this).getPackages(); - // Packages that cannot be autolinked yet can be added manually here, for example: - // packages.add(new MyReactNativePackage()); - // TurboModules must also be loaded here providing a valid TurboReactPackage implementation: - // packages.add(new TurboReactPackage() { ... }); - // If you have custom Fabric Components, their ViewManagers should also be loaded here - // inside a ReactPackage. - return packages; - } - @Override - protected String getJSMainModuleName() { - return "index"; - } - @NonNull - @Override - protected ReactPackageTurboModuleManagerDelegate.Builder - getReactPackageTurboModuleManagerDelegateBuilder() { - // Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary - // for the new architecture and to use TurboModules correctly. - return new MainApplicationTurboModuleManagerDelegate.Builder(); - } - @Override - protected JSIModulePackage getJSIModulePackage() { - return new JSIModulePackage() { - @Override - public List getJSIModules( - final ReactApplicationContext reactApplicationContext, - final JavaScriptContextHolder jsContext) { - final List specs = new ArrayList<>(); - // Here we provide a new JSIModuleSpec that will be responsible of providing the - // custom Fabric Components. - specs.add( - new JSIModuleSpec() { - @Override - public JSIModuleType getJSIModuleType() { - return JSIModuleType.UIManager; - } - @Override - public JSIModuleProvider getJSIModuleProvider() { - final ComponentFactory componentFactory = new ComponentFactory(); - CoreComponentsRegistry.register(componentFactory); - // Here we register a Components Registry. - // The one that is generated with the template contains no components - // and just provides you the one from React Native core. - MainComponentsRegistry.register(componentFactory); - final ReactInstanceManager reactInstanceManager = getReactInstanceManager(); - ViewManagerRegistry viewManagerRegistry = - new ViewManagerRegistry( - reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)); - return new FabricJSIModuleProvider( - reactApplicationContext, - componentFactory, - ReactNativeConfig.DEFAULT_CONFIG, - viewManagerRegistry); - } - }); - return specs; - } - }; - } -} \ No newline at end of file diff --git a/android/app/src/main/java/it/pagopa/io/app/newarchitecture/components/MainComponentsRegistry.java b/android/app/src/main/java/it/pagopa/io/app/newarchitecture/components/MainComponentsRegistry.java deleted file mode 100644 index 2601fdd73e9..00000000000 --- a/android/app/src/main/java/it/pagopa/io/app/newarchitecture/components/MainComponentsRegistry.java +++ /dev/null @@ -1,30 +0,0 @@ -package it.pagopa.io.app.newarchitecture.components; -import com.facebook.jni.HybridData; -import com.facebook.proguard.annotations.DoNotStrip; -import com.facebook.react.fabric.ComponentFactory; -import com.facebook.soloader.SoLoader; -/** - * Class responsible to load the custom Fabric Components. This class has native methods and needs a - * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ - * folder for you). - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -@DoNotStrip -public class MainComponentsRegistry { - static { - SoLoader.loadLibrary("fabricjni"); - } - @DoNotStrip private final HybridData mHybridData; - @DoNotStrip - private native HybridData initHybrid(ComponentFactory componentFactory); - @DoNotStrip - private MainComponentsRegistry(ComponentFactory componentFactory) { - mHybridData = initHybrid(componentFactory); - } - @DoNotStrip - public static MainComponentsRegistry register(ComponentFactory componentFactory) { - return new MainComponentsRegistry(componentFactory); - } -} \ No newline at end of file diff --git a/android/app/src/main/java/it/pagopa/io/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java b/android/app/src/main/java/it/pagopa/io/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java deleted file mode 100644 index b20346eee87..00000000000 --- a/android/app/src/main/java/it/pagopa/io/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate.java +++ /dev/null @@ -1,40 +0,0 @@ -package it.pagopa.io.app.newarchitecture.modules; -import com.facebook.jni.HybridData; -import com.facebook.react.ReactPackage; -import com.facebook.react.ReactPackageTurboModuleManagerDelegate; -import com.facebook.react.bridge.ReactApplicationContext; -import com.facebook.soloader.SoLoader; -import java.util.List; -/** - * Class responsible to load the TurboModules. This class has native methods and needs a - * corresponding C++ implementation/header file to work correctly (already placed inside the jni/ - * folder for you). - * - *

Please note that this class is used ONLY if you opt-in for the New Architecture (see the - * `newArchEnabled` property). Is ignored otherwise. - */ -public class MainApplicationTurboModuleManagerDelegate - extends ReactPackageTurboModuleManagerDelegate { - private static volatile boolean sIsSoLibraryLoaded; - protected MainApplicationTurboModuleManagerDelegate( - ReactApplicationContext reactApplicationContext, List packages) { - super(reactApplicationContext, packages); - } - protected native HybridData initHybrid(); - native boolean canCreateTurboModule(String moduleName); - public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder { - protected MainApplicationTurboModuleManagerDelegate build( - ReactApplicationContext context, List packages) { - return new MainApplicationTurboModuleManagerDelegate(context, packages); - } - } - @Override - protected synchronized void maybeLoadOtherSoLibraries() { - if (!sIsSoLibraryLoaded) { - // If you change the name of your application .so file in the Android.mk file, - // make sure you update the name here as well. - SoLoader.loadLibrary("rndiffapp_appmodules"); - sIsSoLibraryLoaded = true; - } - } -} \ No newline at end of file diff --git a/android/app/src/main/jni/CMakeLists.txt b/android/app/src/main/jni/CMakeLists.txt deleted file mode 100644 index 0dd7f25118c..00000000000 --- a/android/app/src/main/jni/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -cmake_minimum_required(VERSION 3.13) - -# Define the library name here. -project(ioapp_appmodules) - -# This file includes all the necessary to let you build your application with the New Architecture. -include(${REACT_ANDROID_DIR}/cmake-utils/ReactNative-application.cmake) diff --git a/android/app/src/main/jni/MainApplicationModuleProvider.cpp b/android/app/src/main/jni/MainApplicationModuleProvider.cpp deleted file mode 100644 index 1e04d15b34f..00000000000 --- a/android/app/src/main/jni/MainApplicationModuleProvider.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "MainApplicationModuleProvider.h" -#include -#include -namespace facebook { -namespace react { -std::shared_ptr MainApplicationModuleProvider( - const std::string &moduleName, - const JavaTurboModule::InitParams ¶ms) { - // Here you can provide your own module provider for TurboModules coming from - // either your application or from external libraries. The approach to follow - // is similar to the following (for a library called `samplelibrary`: - // - // auto module = samplelibrary_ModuleProvider(moduleName, params); - // if (module != nullptr) { - // return module; - // } - // return rncore_ModuleProvider(moduleName, params); - // Module providers autolinked by RN CLI - auto rncli_module = rncli_ModuleProvider(moduleName, params); - if (rncli_module != nullptr) { - return rncli_module; - } - - return rncore_ModuleProvider(moduleName, params); -} -} // namespace react -} // namespace facebook diff --git a/android/app/src/main/jni/MainApplicationModuleProvider.h b/android/app/src/main/jni/MainApplicationModuleProvider.h deleted file mode 100644 index 4515c40bb83..00000000000 --- a/android/app/src/main/jni/MainApplicationModuleProvider.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include -#include -#include -namespace facebook { -namespace react { -std::shared_ptr MainApplicationModuleProvider( - const std::string &moduleName, - const JavaTurboModule::InitParams ¶ms); -} // namespace react -} // namespace facebook \ No newline at end of file diff --git a/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp b/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp deleted file mode 100644 index 8439888b532..00000000000 --- a/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "MainApplicationTurboModuleManagerDelegate.h" -#include "MainApplicationModuleProvider.h" -namespace facebook { -namespace react { -jni::local_ref -MainApplicationTurboModuleManagerDelegate::initHybrid( - jni::alias_ref) { - return makeCxxInstance(); -} -void MainApplicationTurboModuleManagerDelegate::registerNatives() { - registerHybrid({ - makeNativeMethod( - "initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid), - makeNativeMethod( - "canCreateTurboModule", - MainApplicationTurboModuleManagerDelegate::canCreateTurboModule), - }); -} -std::shared_ptr -MainApplicationTurboModuleManagerDelegate::getTurboModule( - const std::string &name, - const std::shared_ptr &jsInvoker) { - // Not implemented yet: provide pure-C++ NativeModules here. - return nullptr; -} -std::shared_ptr -MainApplicationTurboModuleManagerDelegate::getTurboModule( - const std::string &name, - const JavaTurboModule::InitParams ¶ms) { - return MainApplicationModuleProvider(name, params); -} -bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule( - const std::string n&ame) { - return getTurboModule(name, nullptr) != nullptr || - getTurboModule(name, {.moduleName = name}) != nullptr; -} -} // namespace react -} // namespace facebook \ No newline at end of file diff --git a/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h b/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h deleted file mode 100644 index c31481973d3..00000000000 --- a/android/app/src/main/jni/MainApplicationTurboModuleManagerDelegate.h +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include -#include -namespace facebook { -namespace react { -class MainApplicationTurboModuleManagerDelegate - : public jni::HybridClass< - MainApplicationTurboModuleManagerDelegate, - TurboModuleManagerDelegate> { - public: - // Adapt it to the package you used for your Java class. - static constexpr auto kJavaDescriptor = - "Lit/pagopa/io/app/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;"; - static jni::local_ref initHybrid(jni::alias_ref); - static void registerNatives(); - std::shared_ptr getTurboModule( - const std::string &name, - const std::shared_ptr &jsInvoker) override; - std::shared_ptr getTurboModule( - const std::string &name, - const JavaTurboModule::InitParams ¶ms) override; - /** - * Test-only method. Allows user to verify whether a TurboModule can be - * created by instances of this class. - */ - bool canCreateTurboModule(const std::string &name); -}; -} // namespace react -} // namespace facebook diff --git a/android/app/src/main/jni/MainComponentsRegistry.cpp b/android/app/src/main/jni/MainComponentsRegistry.cpp deleted file mode 100644 index c634fa5bb2e..00000000000 --- a/android/app/src/main/jni/MainComponentsRegistry.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "MainComponentsRegistry.h" -#include -#include -#include -#include -#include - -namespace facebook { -namespace react { -MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {} -std::shared_ptr -MainComponentsRegistry::sharedProviderRegistry() { - auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry(); - - // Autolinked providers registered by RN CLI - rncli_registerProviders(providerRegistry); - // Custom Fabric Components go here. You can register custom - // components coming from your App or from 3rd party libraries here. - // - // providerRegistry->add(concreteComponentDescriptorProvider< - // AocViewerComponentDescriptor>()); - return providerRegistry; -} -jni::local_ref -MainComponentsRegistry::initHybrid( - jni::alias_ref, - ComponentFactory *delegate) { - auto instance = makeCxxInstance(delegate); - auto buildRegistryFunction = - [](EventDispatcher::Weak const &eventDispatcher, - ContextContainer::Shared const &contextContainer) - -> ComponentDescriptorRegistry::Shared { - auto registry = MainComponentsRegistry::sharedProviderRegistry() - ->createComponentDescriptorRegistry( - {eventDispatcher, contextContainer}); - auto mutableRegistry = - std::const_pointer_cast(registry); - mutableRegistry->setFallbackComponentDescriptor( - std::make_shared( - ComponentDescriptorParameters{ - eventDispatcher, contextContainer, nullptr})); - return registry; - }; - delegate->buildRegistryFunction = buildRegistryFunction; - return instance; -} -void MainComponentsRegistry::registerNatives() { - registerHybrid({ - makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid), - }); -} -} // namespace react -} // namespace facebook \ No newline at end of file diff --git a/android/app/src/main/jni/MainComponentsRegistry.h b/android/app/src/main/jni/MainComponentsRegistry.h deleted file mode 100644 index 6d701b038bc..00000000000 --- a/android/app/src/main/jni/MainComponentsRegistry.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include -#include -#include -#include -namespace facebook { -namespace react { -class MainComponentsRegistry - : public facebook::jni::HybridClass { - public: - // Adapt it to the package you used for your Java class. - constexpr static auto kJavaDescriptor = - "Lit/pagopa/io/app/newarchitecture/components/MainComponentsRegistry;"; - static void registerNatives(); - MainComponentsRegistry(ComponentFactory *delegate); - private: - static std::shared_ptr - sharedProviderRegistry(); - static jni::local_ref initHybrid( - jni::alias_ref, - ComponentFactory *delegate); -}; -} // namespace react -} // namespace facebook \ No newline at end of file diff --git a/android/app/src/main/jni/OnLoad.cpp b/android/app/src/main/jni/OnLoad.cpp deleted file mode 100644 index 6f2082d2a2c..00000000000 --- a/android/app/src/main/jni/OnLoad.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include "MainApplicationTurboModuleManagerDelegate.h" -#include "MainComponentsRegistry.h" -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) { - return facebook::jni::initialize(vm, [] { - facebook::react::MainApplicationTurboModuleManagerDelegate:: - registerNatives(); - facebook::react::MainComponentsRegistry::registerNatives(); - }); -} \ No newline at end of file diff --git a/android/app/src/release/java/it/pagopa/io/app/ReactNativeFlipper.java b/android/app/src/release/java/it/pagopa/io/app/ReactNativeFlipper.java new file mode 100644 index 00000000000..8219f1aa95a --- /dev/null +++ b/android/app/src/release/java/it/pagopa/io/app/ReactNativeFlipper.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package it.pagopa.io.app; + +import android.content.Context; +import com.facebook.react.ReactInstanceManager; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the release + * flavor of it so it's empty as we don't want to load Flipper. + */ +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + // Do nothing as we don't want to initialize Flipper on Release. + } +} \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle index 200c0cdea90..2e6f02499f1 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,55 +3,30 @@ buildscript { ext { firebaseMessagingVersion = "23.2.1" - kotlin_version = "1.7.0" buildToolsVersion = "33.0.2" + kotlinVersion = "1.7.0" minSdkVersion = 23 compileSdkVersion = 33 targetSdkVersion = 33 supportLibVersion = "28.0.0" - - if (System.properties['os.arch'] == "aarch64") { - // For M1 Users we need to use the NDK 24 which added support for aarch64 - ndkVersion = "24.0.8215888" - } else { - // Otherwise we default to the side-by-side NDK version from AGP. - ndkVersion = "21.4.7075529" - } + // We use NDK 24 which has both M1 support and is the side-by-side NDK version from AGP. + ndkVersion = "24.0.8215888" } repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' + classpath 'com.android.tools.build:gradle' classpath 'com.facebook.react:react-native-gradle-plugin' - classpath 'de.undercouch:gradle-download-task:5.0.1' classpath 'com.google.gms:google-services:4.3.3' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } allprojects { repositories { - 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") - } - mavenCentral { - // We don't want to fetch react-native from Maven Central as there are - // older versions over there. - content { - excludeGroup "com.facebook.react" - } - } - google() jcenter() maven { url 'https://www.jitpack.io' } maven { url 'https://zendesk.jfrog.io/zendesk/repo' } diff --git a/android/fastlane/Fastfile b/android/fastlane/Fastfile index b7994f9129c..bb7a0f0a90b 100644 --- a/android/fastlane/Fastfile +++ b/android/fastlane/Fastfile @@ -30,7 +30,7 @@ platform :android do gradle( task: "assemble", build_type: "Debug", - flags: "-x bundleReleaseJsAndAssets --no-daemon" + # flags: "-x bundleReleaseJsAndAssets --no-daemon" ) end @@ -42,7 +42,7 @@ platform :android do gradle( task: "bundle", build_type: "Release", - flags: "-x bundleReleaseJsAndAssets --no-daemon" + # flags: "-x bundleReleaseJsAndAssets --no-daemon" ) end diff --git a/android/gradle.properties b/android/gradle.properties index 8088bf34d62..f00ad1d2900 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -26,7 +26,7 @@ android.useAndroidX=true android.enableJetifier=true # Version of flipper SDK to use with React Native -FLIPPER_VERSION=0.154.0 +FLIPPER_VERSION=0.182.0 # Use this property to specify which architecture you want to build. # You can also override it from the CLI using @@ -39,3 +39,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 # to write custom TurboModules/Fabric components OR use libraries that # are providing them. newArchEnabled=false + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4..943f0cbfa75 100644 Binary files a/android/gradle/wrapper/gradle-wrapper.jar 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 index 8fad3f5a98b..6ec1567a0f8 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/gradlew b/android/gradlew index a59a43c1889..510d795ac83 100755 --- a/android/gradlew +++ b/android/gradlew @@ -54,7 +54,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -77,11 +77,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # 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"' @@ -140,12 +139,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -199,6 +202,12 @@ set -- \ -classpath "$CLASSPATH" \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/android/gradlew.bat b/android/gradlew.bat index e61892156e8..4b4ef2d7626 100644 --- a/android/gradlew.bat +++ b/android/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -74,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 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 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/android/settings.gradle b/android/settings.gradle index 3cc2df04f20..2f63f80bcde 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -53,12 +53,6 @@ project(':react-native-community-netinfo').projectDir = new File(rootProject.pro include ':react-native-screen-brightness' project(':react-native-screen-brightness').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-screen-brightness/android') include ':app' -includeBuild('../node_modules/react-native-gradle-plugin') -if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") { - include(":ReactAndroid") - project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid') - include(":ReactAndroid:hermes-engine") - project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine') -} +includeBuild('../node_modules/@react-native/gradle-plugin') include ':react-native-art' project(':react-native-art').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/art/android') diff --git a/img/services/icon-loading-services.png b/img/services/icon-loading-services.png deleted file mode 100644 index b0f6e89ba41..00000000000 Binary files a/img/services/icon-loading-services.png and /dev/null differ diff --git a/img/services/icon-loading-services@2x.png b/img/services/icon-loading-services@2x.png deleted file mode 100644 index 32bdeb58d24..00000000000 Binary files a/img/services/icon-loading-services@2x.png and /dev/null differ diff --git a/img/services/icon-loading-services@3x.png b/img/services/icon-loading-services@3x.png deleted file mode 100644 index 95102e0e653..00000000000 Binary files a/img/services/icon-loading-services@3x.png and /dev/null differ diff --git a/ios/ItaliaApp.xcodeproj/project.pbxproj b/ios/ItaliaApp.xcodeproj/project.pbxproj index 26c0e1868f8..9f213527512 100644 --- a/ios/ItaliaApp.xcodeproj/project.pbxproj +++ b/ios/ItaliaApp.xcodeproj/project.pbxproj @@ -467,7 +467,7 @@ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSDKConfigurationsSDK/SDKConfigurations.framework/SDKConfigurations", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSupportProvidersSDK/SupportProvidersSDK.framework/SupportProvidersSDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSupportSDK/SupportSDK.framework/SupportSDK", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -500,11 +500,13 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ItaliaApp-ItaliaAppTests/Pods-ItaliaApp-ItaliaAppTests-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Alamofire.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", ); runOnlyForDeploymentPostprocessing = 0; @@ -552,11 +554,13 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-ItaliaApp/Pods-ItaliaApp-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle", ); name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Alamofire.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle", ); runOnlyForDeploymentPostprocessing = 0; @@ -607,7 +611,7 @@ "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSDKConfigurationsSDK/SDKConfigurations.framework/SDKConfigurations", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSupportProvidersSDK/SupportProvidersSDK.framework/SupportProvidersSDK", "${PODS_XCFRAMEWORKS_BUILD_DIR}/ZendeskSupportSDK/SupportSDK.framework/SupportSDK", - "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/hermes.framework/hermes", + "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( @@ -744,7 +748,7 @@ CODE_SIGN_ENTITLEMENTS = ItaliaApp/ItaliaApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = M2X5YQ4BJ7; ENABLE_BITCODE = NO; @@ -785,7 +789,7 @@ CODE_SIGN_ENTITLEMENTS = ItaliaApp/ItaliaApp.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 5; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = M2X5YQ4BJ7; ENABLE_BITCODE = NO; @@ -872,11 +876,9 @@ ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_CFLAGS = "$(inherited)"; + OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; }; @@ -935,11 +937,9 @@ "\"$(SDKROOT)/usr/lib/swift\"", ); MTL_ENABLE_DEBUG_INFO = NO; - OTHER_LDFLAGS = ( - "$(inherited)", - "-Wl", - "-ld_classic", - ); + OTHER_CFLAGS = "$(inherited)"; + OTHER_CPLUSPLUSFLAGS = "$(inherited)"; + OTHER_LDFLAGS = "$(inherited)"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; VALIDATE_PRODUCT = YES; diff --git a/ios/ItaliaApp/AppDelegate.h b/ios/ItaliaApp/AppDelegate.h index a0c8478e82e..de3d0a1d168 100644 --- a/ios/ItaliaApp/AppDelegate.h +++ b/ios/ItaliaApp/AppDelegate.h @@ -1,9 +1,7 @@ -#import +#import #import #import // react-native-push-notification-ios -@interface AppDelegate : UIResponder // UNUserNotificationCenterDelegate react-native-push-notification-ios - -@property (nonatomic, strong) UIWindow *window; +@interface AppDelegate : RCTAppDelegate // UNUserNotificationCenterDelegate react-native-push-notification-ios @end diff --git a/ios/ItaliaApp/AppDelegate.mm b/ios/ItaliaApp/AppDelegate.mm index fa6708ea9ba..00a641313b0 100644 --- a/ios/ItaliaApp/AppDelegate.mm +++ b/ios/ItaliaApp/AppDelegate.mm @@ -1,59 +1,22 @@ #import "AppDelegate.h" #import -#import #import -#import -#import #import // react-native-push-notification-ios #import // react-native-push-notification-ios -#if RCT_NEW_ARCH_ENABLED -#import -#import -#import -#import -#import -#import -#import -static NSString *const kRNConcurrentRoot = @"concurrentRoot"; -@interface AppDelegate () { - RCTTurboModuleManager *_turboModuleManager; - RCTSurfacePresenterBridgeAdapter *_bridgeAdapter; - std::shared_ptr _reactNativeConfig; - facebook::react::ContextContainer::Shared _contextContainer; -} -@end -#endif @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - RCTAppSetupPrepareApp(application); - RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; -#if RCT_NEW_ARCH_ENABLED - _contextContainer = std::make_shared(); - _reactNativeConfig = std::make_shared(); - _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); - _bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer]; - bridge.surfacePresenter = _bridgeAdapter.surfacePresenter; -#endif - NSDictionary *initProps = [self prepareInitialProps]; - UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"IO", initProps); - if (@available(iOS 13.0, *)) { - rootView.backgroundColor = [UIColor systemBackgroundColor]; - } else { - rootView.backgroundColor = [UIColor whiteColor]; - } - self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; - UIViewController *rootViewController = [UIViewController new]; - rootViewController.view = rootView; - self.window.rootViewController = rootViewController; - [self.window makeKeyAndVisible]; - +{ + self.moduleName = @"IO"; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + // react-native-push-notification-ios UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; - return YES; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } // react-native-push-notification-ios - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken @@ -83,24 +46,7 @@ -(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNoti { completionHandler(UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge); } -/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. -/// -/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html -/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). -/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`. -- (BOOL)concurrentRootEnabled -{ - // Switch this bool to turn on and off the concurrent root - return true; -} -- (NSDictionary *)prepareInitialProps -{ - NSMutableDictionary *initProps = [NSMutableDictionary new]; -#ifdef RCT_NEW_ARCH_ENABLED - initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); -#endif - return initProps; -} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG @@ -109,36 +55,6 @@ - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; #endif } -#if RCT_NEW_ARCH_ENABLED -#pragma mark - RCTCxxBridgeDelegate -- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge -{ - _turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge - delegate:self - jsInvoker:bridge.jsCallInvoker]; - return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); -} -#pragma mark RCTTurboModuleManagerDelegate -- (Class)getModuleClassFromName:(const char *)name -{ - return RCTCoreModulesClassProvider(name); -} -- (std::shared_ptr)getTurboModule:(const std::string &)name - jsInvoker:(std::shared_ptr)jsInvoker -{ - return nullptr; -} -- (std::shared_ptr)getTurboModule:(const std::string &)name - initParams: - (const facebook::react::ObjCTurboModule::InitParams &)params -{ - return nullptr; -} -- (id)getModuleInstanceFromClass:(Class)moduleClass -{ - return RCTAppSetupDefaultModuleFromClass(moduleClass); -} -#endif // Add this inside `@implementation AppDelegate` above `@end`: - (BOOL)application:(UIApplication *)application diff --git a/ios/ItaliaApp/Info.plist b/ios/ItaliaApp/Info.plist index 31394924c4e..cecdf5a4fab 100644 --- a/ios/ItaliaApp/Info.plist +++ b/ios/ItaliaApp/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.63.0 + 2.64.0 CFBundleSignature ???? CFBundleURLTypes @@ -34,7 +34,7 @@ CFBundleVersion - 5 + 1 ITSAppUsesNonExemptEncryption LSApplicationQueriesSchemes diff --git a/ios/ItaliaAppTests/Info.plist b/ios/ItaliaAppTests/Info.plist index 6856e3f24c3..f87483357f5 100644 --- a/ios/ItaliaAppTests/Info.plist +++ b/ios/ItaliaAppTests/Info.plist @@ -15,10 +15,10 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.63.0 + 2.64.0 CFBundleSignature ???? CFBundleVersion - 5 + 1 \ No newline at end of file diff --git a/ios/Podfile b/ios/Podfile index 6d1f0c91841..04ed3180739 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,9 +1,29 @@ -require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' -require_relative '../node_modules/react-native-permissions/scripts/setup' +def node_require(script) + # Resolve script with node to allow for hoisting + require Pod::Executable.execute_command('node', ['-p', + "require.resolve( + '#{script}', + {paths: [process.argv[1]]}, + )", __dir__]).strip +end + +# Use it to require both react-native's and this package's scripts: +node_require('react-native/scripts/react_native_pods.rb') +node_require('react-native-permissions/scripts/setup.rb') + +min_ios_versions_supported = ['14.0', min_ios_version_supported] +index_of_max = min_ios_versions_supported.each_with_index.max_by { |number, _| number.to_f }[1] -platform :ios, '14.0' -install! 'cocoapods', :deterministic_uuids => false +platform :ios, min_ios_versions_supported[index_of_max] + +prepare_react_native_project! + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end # Required by react-native-permissions # https://github.com/zoontek/react-native-permissions?tab=readme-ov-file#ios @@ -16,6 +36,21 @@ production = ENV["PRODUCTION"] == "1" # we are in a CI environment or not. IS_CI = ENV['CI'] +# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. +# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded +# +# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` +# ```js +# module.exports = { +# dependencies: { +# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), +# ``` +flipper_config = IS_CI ? FlipperConfiguration.disabled : FlipperConfiguration.enabled + + +# Flags change depending on the env values. +flags = get_default_flags() + target 'ItaliaApp' do config = use_native_modules! @@ -23,11 +58,13 @@ target 'ItaliaApp' do pod 'ReactNativeART', :podspec => '../node_modules/@react-native-community/art/ReactNativeART.podspec' use_react_native!( :path => config[:reactNativePath], - # Hermes is now enabled by default. Disable by setting this flag to false. - # Upcoming versions of React Native may rely on get_default_flags(), but - # we make it explicit here to aid in the React Native upgrade process. - :hermes_enabled => true, - :fabric_enabled => false, + :hermes_enabled => flags[:hermes_enabled], + :fabric_enabled => flags[:fabric_enabled], + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable these next few lines. + :flipper_configuration => flipper_config, # An absolute path to your application root. :app_path => "#{Pod::Config.instance.installation_root}/.." ) @@ -36,20 +73,12 @@ target 'ItaliaApp' do inherit! :complete # Pods for testing end - # Enables Flipper. - # - # Note that if you have use_frameworks! enabled, Flipper will not work and - # you should disable these next few lines. - if not IS_CI - use_flipper!({ 'Flipper' => '0.154.0' }) - end post_install do |installer| + # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( installer, - # Set `mac_catalyst_enabled` to `true` in order to apply patches - # necessary for Mac Catalyst builds - :mac_catalyst_enabled => false + config[:reactNativePath], ) __apply_Xcode_12_5_M1_post_install_workaround(installer) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 789fbecf305..566620a501f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -5,15 +5,15 @@ PODS: - React - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - - FBLazyVector (0.70.15) - - FBReactNativeSpec (0.70.15): + - FBLazyVector (0.72.14) + - FBReactNativeSpec (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTRequired (= 0.70.15) - - RCTTypeSafety (= 0.70.15) - - React-Core (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - Flipper (0.154.0): + - RCTRequired (= 0.72.14) + - RCTTypeSafety (= 0.72.14) + - React-Core (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - Flipper (0.182.0): - Flipper-Folly (~> 2.6) - Flipper-Boost-iOSX (1.76.0.1.11) - Flipper-DoubleConversion (3.2.0.1) @@ -27,50 +27,48 @@ PODS: - OpenSSL-Universal (= 1.1.1100) - Flipper-Glog (0.5.0.5) - Flipper-PeerTalk (0.0.4) - - Flipper-RSocket (1.4.3): - - Flipper-Folly (~> 2.6) - - FlipperKit (0.154.0): - - FlipperKit/Core (= 0.154.0) - - FlipperKit/Core (0.154.0): - - Flipper (~> 0.154.0) + - FlipperKit (0.182.0): + - FlipperKit/Core (= 0.182.0) + - FlipperKit/Core (0.182.0): + - Flipper (~> 0.182.0) - FlipperKit/CppBridge - FlipperKit/FBCxxFollyDynamicConvert - FlipperKit/FBDefines - FlipperKit/FKPortForwarding - SocketRocket (~> 0.6.0) - - FlipperKit/CppBridge (0.154.0): - - Flipper (~> 0.154.0) - - FlipperKit/FBCxxFollyDynamicConvert (0.154.0): + - FlipperKit/CppBridge (0.182.0): + - Flipper (~> 0.182.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.182.0): - Flipper-Folly (~> 2.6) - - FlipperKit/FBDefines (0.154.0) - - FlipperKit/FKPortForwarding (0.154.0): + - FlipperKit/FBDefines (0.182.0) + - FlipperKit/FKPortForwarding (0.182.0): - CocoaAsyncSocket (~> 7.6) - Flipper-PeerTalk (~> 0.0.4) - - FlipperKit/FlipperKitHighlightOverlay (0.154.0) - - FlipperKit/FlipperKitLayoutHelpers (0.154.0): + - FlipperKit/FlipperKitHighlightOverlay (0.182.0) + - FlipperKit/FlipperKitLayoutHelpers (0.182.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutTextSearchable - - FlipperKit/FlipperKitLayoutIOSDescriptors (0.154.0): + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.182.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutPlugin (0.154.0): + - FlipperKit/FlipperKitLayoutPlugin (0.182.0): - FlipperKit/Core - FlipperKit/FlipperKitHighlightOverlay - FlipperKit/FlipperKitLayoutHelpers - FlipperKit/FlipperKitLayoutIOSDescriptors - FlipperKit/FlipperKitLayoutTextSearchable - YogaKit (~> 1.18) - - FlipperKit/FlipperKitLayoutTextSearchable (0.154.0) - - FlipperKit/FlipperKitNetworkPlugin (0.154.0): + - FlipperKit/FlipperKitLayoutTextSearchable (0.182.0) + - FlipperKit/FlipperKitNetworkPlugin (0.182.0): - FlipperKit/Core - - FlipperKit/FlipperKitReactPlugin (0.154.0): + - FlipperKit/FlipperKitReactPlugin (0.182.0): - FlipperKit/Core - - FlipperKit/FlipperKitUserDefaultsPlugin (0.154.0): + - FlipperKit/FlipperKitUserDefaultsPlugin (0.182.0): - FlipperKit/Core - - FlipperKit/SKIOSNetworkPlugin (0.154.0): + - FlipperKit/SKIOSNetworkPlugin (0.182.0): - FlipperKit/Core - FlipperKit/FlipperKitNetworkPlugin - fmt (6.2.1) @@ -105,9 +103,11 @@ PODS: - GoogleUtilitiesComponents (1.1.0): - GoogleUtilities/Logger - GTMSessionFetcher/Core (2.3.0) - - hermes-engine (0.70.15) - - jail-monkey (2.3.2): - - React + - hermes-engine (0.72.14): + - hermes-engine/Pre-built (= 0.72.14) + - hermes-engine/Pre-built (0.72.14) + - jail-monkey (2.8.0): + - React-Core - JOSESwift (2.4.0) - libevent (2.1.12) - Mixpanel-swift (4.2.0): @@ -144,13 +144,16 @@ PODS: - React-Core - pagopa-io-react-native-http-client (1.0.5): - Alamofire (~> 5.9.1) + - RCT-Folly (= 2021.07.22.00) - React-Core - pagopa-io-react-native-jwt (1.2.0): - JOSESwift (~> 2.3) + - RCT-Folly (= 2021.07.22.00) - React-Core - pagopa-io-react-native-login-utils (1.0.1): - React-Core - pagopa-react-native-zendesk (0.3.29): + - RCT-Folly (= 2021.07.22.00) - React-Core - ZendeskAnswerBotSDK - ZendeskChatSDK @@ -174,214 +177,288 @@ PODS: - fmt (~> 6.2.1) - glog - libevent - - RCTRequired (0.70.15) - - RCTTypeSafety (0.70.15): - - FBLazyVector (= 0.70.15) - - RCTRequired (= 0.70.15) - - React-Core (= 0.70.15) - - React (0.70.15): - - React-Core (= 0.70.15) - - React-Core/DevSupport (= 0.70.15) - - React-Core/RCTWebSocket (= 0.70.15) - - React-RCTActionSheet (= 0.70.15) - - React-RCTAnimation (= 0.70.15) - - React-RCTBlob (= 0.70.15) - - React-RCTImage (= 0.70.15) - - React-RCTLinking (= 0.70.15) - - React-RCTNetwork (= 0.70.15) - - React-RCTSettings (= 0.70.15) - - React-RCTText (= 0.70.15) - - React-RCTVibration (= 0.70.15) - - React-bridging (0.70.15): - - RCT-Folly (= 2021.07.22.00) - - React-jsi (= 0.70.15) - - React-callinvoker (0.70.15) - - React-Codegen (0.70.15): - - FBReactNativeSpec (= 0.70.15) - - RCT-Folly (= 2021.07.22.00) - - RCTRequired (= 0.70.15) - - RCTTypeSafety (= 0.70.15) - - React-Core (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-Core (0.70.15): + - RCTRequired (0.72.14) + - RCTTypeSafety (0.72.14): + - FBLazyVector (= 0.72.14) + - RCTRequired (= 0.72.14) + - React-Core (= 0.72.14) + - React (0.72.14): + - React-Core (= 0.72.14) + - React-Core/DevSupport (= 0.72.14) + - React-Core/RCTWebSocket (= 0.72.14) + - React-RCTActionSheet (= 0.72.14) + - React-RCTAnimation (= 0.72.14) + - React-RCTBlob (= 0.72.14) + - React-RCTImage (= 0.72.14) + - React-RCTLinking (= 0.72.14) + - React-RCTNetwork (= 0.72.14) + - React-RCTSettings (= 0.72.14) + - React-RCTText (= 0.72.14) + - React-RCTVibration (= 0.72.14) + - React-callinvoker (0.72.14) + - React-Codegen (0.72.14): + - DoubleConversion + - FBReactNativeSpec + - glog + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-jsi + - React-jsiexecutor + - React-NativeModulesApple + - React-rncore + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-Core (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.70.15) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-Core/Default (= 0.72.14) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/CoreModulesHeaders (0.70.15): + - React-Core/CoreModulesHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/Default (0.70.15): + - React-Core/Default (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/DevSupport (0.70.15): + - React-Core/DevSupport (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.70.15) - - React-Core/RCTWebSocket (= 0.70.15) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-jsinspector (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-Core/Default (= 0.72.14) + - React-Core/RCTWebSocket (= 0.72.14) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-jsinspector (= 0.72.14) + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTActionSheetHeaders (0.70.15): + - React-Core/RCTActionSheetHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTAnimationHeaders (0.70.15): + - React-Core/RCTAnimationHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTBlobHeaders (0.70.15): + - React-Core/RCTBlobHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTImageHeaders (0.70.15): + - React-Core/RCTImageHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTLinkingHeaders (0.70.15): + - React-Core/RCTLinkingHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTNetworkHeaders (0.70.15): + - React-Core/RCTNetworkHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTSettingsHeaders (0.70.15): + - React-Core/RCTSettingsHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTTextHeaders (0.70.15): + - React-Core/RCTTextHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTVibrationHeaders (0.70.15): + - React-Core/RCTVibrationHeaders (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - React-Core/Default - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-Core/RCTWebSocket (0.70.15): + - React-Core/RCTWebSocket (0.72.14): - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Core/Default (= 0.70.15) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-Core/Default (= 0.72.14) + - React-cxxreact + - React-hermes + - React-jsi + - React-jsiexecutor + - React-perflogger + - React-runtimeexecutor + - React-utils + - SocketRocket (= 0.6.1) - Yoga - - React-CoreModules (0.70.15): + - React-CoreModules (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.70.15) - - React-Codegen (= 0.70.15) - - React-Core/CoreModulesHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - React-RCTImage (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-cxxreact (0.70.15): + - RCTTypeSafety (= 0.72.14) + - React-Codegen (= 0.72.14) + - React-Core/CoreModulesHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - React-RCTBlob + - React-RCTImage (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - SocketRocket (= 0.6.1) + - React-cxxreact (0.72.14): - boost (= 1.76.0) - DoubleConversion - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-callinvoker (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsinspector (= 0.70.15) - - React-logger (= 0.70.15) - - React-perflogger (= 0.70.15) - - React-runtimeexecutor (= 0.70.15) - - React-hermes (0.70.15): + - React-callinvoker (= 0.72.14) + - React-debug (= 0.72.14) + - React-jsi (= 0.72.14) + - React-jsinspector (= 0.72.14) + - React-logger (= 0.72.14) + - React-perflogger (= 0.72.14) + - React-runtimeexecutor (= 0.72.14) + - React-debug (0.72.14) + - React-hermes (0.72.14): - DoubleConversion - glog - hermes-engine - RCT-Folly (= 2021.07.22.00) - RCT-Folly/Futures (= 2021.07.22.00) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-jsiexecutor (= 0.70.15) - - React-jsinspector (= 0.70.15) - - React-perflogger (= 0.70.15) - - React-jsi (0.70.15): - - boost (= 1.76.0) - - DoubleConversion - - glog - - RCT-Folly (= 2021.07.22.00) - - React-jsi/Default (= 0.70.15) - - React-jsi/Default (0.70.15): + - React-cxxreact (= 0.72.14) + - React-jsi + - React-jsiexecutor (= 0.72.14) + - React-jsinspector (= 0.72.14) + - React-perflogger (= 0.72.14) + - React-jsi (0.72.14): - boost (= 1.76.0) - DoubleConversion - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-jsiexecutor (0.70.15): + - React-jsiexecutor (0.72.14): - DoubleConversion - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-perflogger (= 0.70.15) - - React-jsinspector (0.70.15) - - React-logger (0.70.15): + - React-cxxreact (= 0.72.14) + - React-jsi (= 0.72.14) + - React-perflogger (= 0.72.14) + - React-jsinspector (0.72.14) + - React-logger (0.72.14): - glog - react-native-background-timer (2.1.1): - React @@ -401,19 +478,20 @@ PODS: - React - react-native-flipper (0.154.0): - React-Core - - react-native-get-random-values (1.7.0): + - react-native-get-random-values (1.11.0): - React-Core - react-native-image-picker (4.10.3): - React-Core - react-native-pager-view (6.2.3): + - RCT-Folly (= 2021.07.22.00) - React-Core - - react-native-pdf (6.4.0): + - react-native-pdf (6.7.4): - React-Core - react-native-pdf-thumbnail (1.2.1): - React-Core - react-native-render-html (6.3.1): - React-Core - - react-native-safe-area-context (3.3.2): + - react-native-safe-area-context (4.10.4): - React-Core - react-native-screen-brightness (2.0.0-alpha): - React @@ -424,80 +502,125 @@ PODS: - react-native-view-shot (3.1.2): - React - react-native-webview (13.10.3): + - RCT-Folly (= 2021.07.22.00) + - React-Core + - React-NativeModulesApple (0.72.14): + - hermes-engine + - React-callinvoker - React-Core - - React-perflogger (0.70.15) - - React-RCTActionSheet (0.70.15): - - React-Core/RCTActionSheetHeaders (= 0.70.15) - - React-RCTAnimation (0.70.15): + - React-cxxreact + - React-jsi + - React-runtimeexecutor + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-perflogger (0.72.14) + - React-RCTActionSheet (0.72.14): + - React-Core/RCTActionSheetHeaders (= 0.72.14) + - React-RCTAnimation (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.70.15) - - React-Codegen (= 0.70.15) - - React-Core/RCTAnimationHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTBlob (0.70.15): + - RCTTypeSafety (= 0.72.14) + - React-Codegen (= 0.72.14) + - React-Core/RCTAnimationHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTAppDelegate (0.72.14): + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-CoreModules + - React-hermes + - React-NativeModulesApple + - React-RCTImage + - React-RCTNetwork + - React-runtimescheduler + - ReactCommon/turbomodule/core + - React-RCTBlob (0.72.14): + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 0.70.15) - - React-Core/RCTBlobHeaders (= 0.70.15) - - React-Core/RCTWebSocket (= 0.70.15) - - React-jsi (= 0.70.15) - - React-RCTNetwork (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTImage (0.70.15): + - React-Codegen (= 0.72.14) + - React-Core/RCTBlobHeaders (= 0.72.14) + - React-Core/RCTWebSocket (= 0.72.14) + - React-jsi (= 0.72.14) + - React-RCTNetwork (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTImage (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.70.15) - - React-Codegen (= 0.70.15) - - React-Core/RCTImageHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - React-RCTNetwork (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTLinking (0.70.15): - - React-Codegen (= 0.70.15) - - React-Core/RCTLinkingHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTNetwork (0.70.15): + - RCTTypeSafety (= 0.72.14) + - React-Codegen (= 0.72.14) + - React-Core/RCTImageHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - React-RCTNetwork (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTLinking (0.72.14): + - React-Codegen (= 0.72.14) + - React-Core/RCTLinkingHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTNetwork (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.70.15) - - React-Codegen (= 0.70.15) - - React-Core/RCTNetworkHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTSettings (0.70.15): + - RCTTypeSafety (= 0.72.14) + - React-Codegen (= 0.72.14) + - React-Core/RCTNetworkHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTSettings (0.72.14): - RCT-Folly (= 2021.07.22.00) - - RCTTypeSafety (= 0.70.15) - - React-Codegen (= 0.70.15) - - React-Core/RCTSettingsHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-RCTText (0.70.15): - - React-Core/RCTTextHeaders (= 0.70.15) - - React-RCTVibration (0.70.15): + - RCTTypeSafety (= 0.72.14) + - React-Codegen (= 0.72.14) + - React-Core/RCTSettingsHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-RCTText (0.72.14): + - React-Core/RCTTextHeaders (= 0.72.14) + - React-RCTVibration (0.72.14): - RCT-Folly (= 2021.07.22.00) - - React-Codegen (= 0.70.15) - - React-Core/RCTVibrationHeaders (= 0.70.15) - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (= 0.70.15) - - React-runtimeexecutor (0.70.15): - - React-jsi (= 0.70.15) - - ReactCommon/turbomodule/core (0.70.15): + - React-Codegen (= 0.72.14) + - React-Core/RCTVibrationHeaders (= 0.72.14) + - React-jsi (= 0.72.14) + - ReactCommon/turbomodule/core (= 0.72.14) + - React-rncore (0.72.14) + - React-runtimeexecutor (0.72.14): + - React-jsi (= 0.72.14) + - React-runtimescheduler (0.72.14): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker + - React-debug + - React-jsi + - React-runtimeexecutor + - React-utils (0.72.14): + - glog + - RCT-Folly (= 2021.07.22.00) + - React-debug + - ReactCommon/turbomodule/bridging (0.72.14): - DoubleConversion - glog + - hermes-engine - RCT-Folly (= 2021.07.22.00) - - React-bridging (= 0.70.15) - - React-callinvoker (= 0.70.15) - - React-Core (= 0.70.15) - - React-cxxreact (= 0.70.15) - - React-jsi (= 0.70.15) - - React-logger (= 0.70.15) - - React-perflogger (= 0.70.15) + - React-callinvoker (= 0.72.14) + - React-cxxreact (= 0.72.14) + - React-jsi (= 0.72.14) + - React-logger (= 0.72.14) + - React-perflogger (= 0.72.14) + - ReactCommon/turbomodule/core (0.72.14): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.72.14) + - React-cxxreact (= 0.72.14) + - React-jsi (= 0.72.14) + - React-logger (= 0.72.14) + - React-perflogger (= 0.72.14) - ReactNativeART (1.2.0): - React - ReactNativeExceptionHandler (2.10.8): - React - RNCalendarEvents (2.2.0): - React - - RNCAsyncStorage (1.17.10): + - RNCAsyncStorage (1.23.1): - React-Core - RNCClipboard (1.10.0): - React-Core @@ -510,6 +633,7 @@ PODS: - RNFS (2.18.0): - React - RNGestureHandler (2.15.0): + - RCT-Folly (= 2021.07.22.00) - React-Core - RNI18n (2.0.15): - React @@ -522,35 +646,14 @@ PODS: - ZXingObjC - RNReactNativeHapticFeedback (2.0.2): - React-Core - - RNReanimated (2.10.0): - - DoubleConversion - - FBLazyVector - - FBReactNativeSpec - - glog - - RCT-Folly - - RCTRequired - - RCTTypeSafety - - React-callinvoker + - RNReanimated (3.12.0): + - RCT-Folly (= 2021.07.22.00) - React-Core - - React-Core/DevSupport - - React-Core/RCTWebSocket - - React-CoreModules - - React-cxxreact - - React-jsi - - React-jsiexecutor - - React-jsinspector - - React-RCTActionSheet - - React-RCTAnimation - - React-RCTBlob - - React-RCTImage - - React-RCTLinking - - React-RCTNetwork - - React-RCTSettings - - React-RCTText - ReactCommon/turbomodule/core - - Yoga - - RNScreens (2.18.1): + - RNScreens (3.31.1): + - RCT-Folly (= 2021.07.22.00) - React-Core + - React-RCTImage - RNShare (7.3.9): - React-Core - RNSVG (15.1.0): @@ -559,7 +662,7 @@ PODS: - vision-camera-code-scanner (0.2.0): - GoogleMLKit/BarcodeScanning - React-Core - - VisionCamera (2.15.4): + - VisionCamera (2.16.7): - React - React-callinvoker - React-Core @@ -598,29 +701,28 @@ DEPENDENCIES: - 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/React/FBReactNativeSpec`) - - Flipper (= 0.154.0) + - Flipper (= 0.182.0) - Flipper-Boost-iOSX (= 1.76.0.1.11) - Flipper-DoubleConversion (= 3.2.0.1) - Flipper-Fmt (= 7.1.7) - Flipper-Folly (= 2.6.10) - Flipper-Glog (= 0.5.0.5) - Flipper-PeerTalk (= 0.0.4) - - Flipper-RSocket (= 1.4.3) - - FlipperKit (= 0.154.0) - - FlipperKit/Core (= 0.154.0) - - FlipperKit/CppBridge (= 0.154.0) - - FlipperKit/FBCxxFollyDynamicConvert (= 0.154.0) - - FlipperKit/FBDefines (= 0.154.0) - - FlipperKit/FKPortForwarding (= 0.154.0) - - FlipperKit/FlipperKitHighlightOverlay (= 0.154.0) - - FlipperKit/FlipperKitLayoutPlugin (= 0.154.0) - - FlipperKit/FlipperKitLayoutTextSearchable (= 0.154.0) - - FlipperKit/FlipperKitNetworkPlugin (= 0.154.0) - - FlipperKit/FlipperKitReactPlugin (= 0.154.0) - - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.154.0) - - FlipperKit/SKIOSNetworkPlugin (= 0.154.0) + - FlipperKit (= 0.182.0) + - FlipperKit/Core (= 0.182.0) + - FlipperKit/CppBridge (= 0.182.0) + - FlipperKit/FBCxxFollyDynamicConvert (= 0.182.0) + - FlipperKit/FBDefines (= 0.182.0) + - FlipperKit/FKPortForwarding (= 0.182.0) + - FlipperKit/FlipperKitHighlightOverlay (= 0.182.0) + - FlipperKit/FlipperKitLayoutPlugin (= 0.182.0) + - FlipperKit/FlipperKitLayoutTextSearchable (= 0.182.0) + - FlipperKit/FlipperKitNetworkPlugin (= 0.182.0) + - FlipperKit/FlipperKitReactPlugin (= 0.182.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.182.0) + - FlipperKit/SKIOSNetworkPlugin (= 0.182.0) - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - - hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - jail-monkey (from `../node_modules/jail-monkey`) - libevent (~> 2.1.12) - MixpanelReactNative (from `../node_modules/mixpanel-react-native`) @@ -634,13 +736,14 @@ DEPENDENCIES: - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) - React (from `../node_modules/react-native/`) - - React-bridging (from `../node_modules/react-native/ReactCommon`) - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) - React-Codegen (from `build/generated/ios`) - 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-debug (from `../node_modules/react-native/ReactCommon/react/debug`) - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) @@ -666,9 +769,11 @@ DEPENDENCIES: - react-native-splash-screen (from `../node_modules/react-native-splash-screen`) - react-native-view-shot (from `../node_modules/react-native-view-shot`) - react-native-webview (from `../node_modules/react-native-webview`) + - React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`) - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) - 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`) @@ -676,7 +781,10 @@ DEPENDENCIES: - 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`) + - React-rncore (from `../node_modules/react-native/ReactCommon`) - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) + - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) - "ReactNativeART (from `../node_modules/@react-native-community/art/ReactNativeART.podspec`)" - ReactNativeExceptionHandler (from `../node_modules/react-native-exception-handler`) @@ -712,7 +820,6 @@ SPEC REPOS: - Flipper-Folly - Flipper-Glog - Flipper-PeerTalk - - Flipper-RSocket - FlipperKit - fmt - GoogleDataTransport @@ -760,7 +867,7 @@ EXTERNAL SOURCES: glog: :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" hermes-engine: - :podspec: "../node_modules/react-native/sdks/hermes/hermes-engine.podspec" + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" jail-monkey: :path: "../node_modules/jail-monkey" MixpanelReactNative: @@ -783,8 +890,6 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/TypeSafety" React: :path: "../node_modules/react-native/" - React-bridging: - :path: "../node_modules/react-native/ReactCommon" React-callinvoker: :path: "../node_modules/react-native/ReactCommon/callinvoker" React-Codegen: @@ -795,6 +900,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/React/CoreModules" React-cxxreact: :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-debug: + :path: "../node_modules/react-native/ReactCommon/react/debug" React-hermes: :path: "../node_modules/react-native/ReactCommon/hermes" React-jsi: @@ -845,12 +952,16 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-view-shot" react-native-webview: :path: "../node_modules/react-native-webview" + React-NativeModulesApple: + :path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios" React-perflogger: :path: "../node_modules/react-native/ReactCommon/reactperflogger" React-RCTActionSheet: :path: "../node_modules/react-native/Libraries/ActionSheetIOS" React-RCTAnimation: :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../node_modules/react-native/Libraries/AppDelegate" React-RCTBlob: :path: "../node_modules/react-native/Libraries/Blob" React-RCTImage: @@ -865,8 +976,14 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/Libraries/Text" React-RCTVibration: :path: "../node_modules/react-native/Libraries/Vibration" + React-rncore: + :path: "../node_modules/react-native/ReactCommon" React-runtimeexecutor: :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + React-runtimescheduler: + :path: "../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler" + React-utils: + :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCommon: :path: "../node_modules/react-native/ReactCommon" ReactNativeART: @@ -916,21 +1033,20 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c - boost: 9fa78656d705f55b1220151d997e57e2a3f2cde0 + boost: 7dcd2de282d72e344012f7d6564d024930a6a440 BVLinearGradient: e3aad03778a456d77928f594a649e96995f1c872 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 - FBLazyVector: 9cf707e46f9bd90816b7c91b2c1c8b8a2f549527 - FBReactNativeSpec: 5ce1ea97a4309ded19af6c21f13f63ee3cabfed2 - Flipper: 53851f5b89559bb6e251572589dc166d1f8d6e2e + FBLazyVector: d98eefb42c5a64cb28ef966bd9096c76770d8f24 + FBReactNativeSpec: 53d4eb00e8e1b6e987a3dd5906d2afe131cc54c8 + Flipper: 6edb735e6c3e332975d1b17956bcc584eccf5818 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 - Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 - FlipperKit: 51cf8b6f5b0931e251c57d4d60a15a1c2ba546aa + FlipperKit: 2efad7007d6745a3f95e4034d547be637f89d3f6 fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b GoogleDataTransport: 57c22343ab29bc686febbf7cbb13bad167c2d8fe @@ -939,8 +1055,8 @@ SPEC CHECKSUMS: GoogleUtilities: 0759d1a57ebb953965c2dfe0ba4c82e95ccc2e34 GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 - hermes-engine: 2592781da1571e4375dfd897f9462638c2d0ceb9 - jail-monkey: d7c5048b2336f22ee9c9e0efa145f1f917338ea9 + hermes-engine: b213bace5f31766ad1434d2d9b2cbf04cf92a2b6 + jail-monkey: a71b35d482a70ecba844a90f002994012cf12a5d JOSESwift: 7ff178bb9173ff42c6e990929a9f2fa702a34f69 libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 Mixpanel-swift: e5dd85295923e6a875acf17ccbab8d2ecb10ea65 @@ -952,26 +1068,26 @@ SPEC CHECKSUMS: nanopb: d4d75c12cd1316f4a64e3c6963f879ecd4b5e0d5 OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c pagopa-io-react-native-crypto: 6aa9f33e4bf64ef420ad97c720c1ad0f876cd470 - pagopa-io-react-native-http-client: 8ed571ea31ec9e6216688f63056e5c92bcbe484d - pagopa-io-react-native-jwt: f89a378bbc5ebfd93c24ec5993e97545a4815157 + pagopa-io-react-native-http-client: cbdfb83c92432efb0de22b7c3c85cffa391870f8 + pagopa-io-react-native-jwt: 662d4e722715996758b079774abea1996b057467 pagopa-io-react-native-login-utils: 9fb43fd59dcc864a24343209e74bd7429659456a - pagopa-react-native-zendesk: e4a63ee0745a567b641110f7ff78e457086ab7a3 + pagopa-react-native-zendesk: 60a26f8a8072234789c34bb7c8a769c156eb57dc PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 - RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda - RCTRequired: 2a96ea90ffddd10cc43115bd93803692e09b5d9a - RCTTypeSafety: 02c99baddcf0b3393bf58e6d9b792e83a37716b4 - React: 45e3210df90d25ec6da7fc286943b377b63a92ec - React-bridging: e3a18265bbd59003562e29429985e0923a5b6286 - React-callinvoker: 344ff205a470c3c99b4daf0a2dff9bc29045d6c5 - React-Codegen: 2b1765b0e1a38b8b3601178ca27c1e9216e81632 - React-Core: 93efb81ef85fafee7f83f7ef6ecf546b2e1ee2c0 - React-CoreModules: 4eb535b1650b718cb3680767c1b9a1cacf649cbc - React-cxxreact: 283248db3101de28d6cf0fe438a2dc95537ee472 - React-hermes: 5439b771de0b04930c97888cc4c28852aa37389c - React-jsi: 560bdf0bc36d5c137ac962c0eb4b60b50c304d77 - React-jsiexecutor: d2eebcd5a432f90be3baa5d1309f47d05478ea61 - React-jsinspector: bfedded1f4f562d29c2d4a8bb795c9a150a739e4 - React-logger: 31f198387a04172be49fe38e41a082560a81aeeb + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCTRequired: 264adaca1d8b1a9c078761891898d4142df05313 + RCTTypeSafety: 279a89da7058a69899778a127be73fab38b84499 + React: 725b4e11f6ffb43d6f9b14e82879073623db4071 + React-callinvoker: c2ba5e7e1187d0f37705b9dcaaf9bbf24d3fe9dc + React-Codegen: 590303f222cffc109dc0d122854fa8281fa536b7 + React-Core: 89fe417d4957766ef20b4bec921a0721e914d988 + React-CoreModules: 57ea4ca8627be90b1a29916e0640d879e5684305 + React-cxxreact: 8bcd7336084fbaaad0304935896f2cb62659894d + React-debug: d360c17c84e514b9143e78217072183d4fcfb9c0 + React-hermes: d2e7945d1b480a2e732d64303564d9870edf22e9 + React-jsi: 53bff70353449aa006546c00024736de3ed66219 + React-jsiexecutor: e9a70be9304ef2e66eeebac35710f958b076dc14 + React-jsinspector: 275d9f80210f15f0af9a4b7fd5683fab9738e28e + React-logger: 8da4802de77a0eb62512396ad6bb1769904c2f0e react-native-background-timer: 1b6e6b4e10f1b74c367a1fdc3c72b67c619b222b react-native-blob-util: a5d3561045ed98cfb2fb80cbbff600fae0e8edee react-native-cameraroll: 2f08db1ecc9b73dbc01f89335d6d5179fac2894c @@ -980,53 +1096,58 @@ SPEC CHECKSUMS: react-native-document-picker: 3599b238843369026201d2ef466df53f77ae0452 react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe react-native-flipper: 97d537d855e0e7f6ac26a065e01bf1aecc8ba41c - react-native-get-random-values: 237bffb1c7e05fb142092681531810a29ba53015 + react-native-get-random-values: 21325b2244dfa6b58878f51f9aa42821e7ba3d06 react-native-image-picker: 60f4246eb5bb7187fc15638a8c1f13abd3820695 - react-native-pager-view: c29d484f19c49ff19525a94105e4ab2c4d4ae273 - react-native-pdf: a6a5a3f0bdf340eb2eed6c96034424d2cc3f84b0 + react-native-pager-view: 948dc00b6545d82b53e5f99cafef4a8521c60dd4 + react-native-pdf: 79aa75e39a80c1d45ffe58aa500f3cf08f267a2e react-native-pdf-thumbnail: a042fffdab7a49f0f9df0e11da0a90beebfd4241 react-native-render-html: 96c979fe7452a0a41559685d2f83b12b93edac8c - react-native-safe-area-context: 584dc04881deb49474363f3be89e4ca0e854c057 + react-native-safe-area-context: 399a5859f6acbdf67f671c69b53113f535f3b5b0 react-native-screen-brightness: 9eefe6db96a5d757e63cdfce8e48d7c9039f2af2 react-native-slider: e99fc201cefe81270fc9d81714a7a0f5e566b168 react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865 react-native-view-shot: 4475fde003fe8a210053d1f98fb9e06c1d834e1c - react-native-webview: bbe76e4df57707c717f3c417ec0441f2a1848bba - React-perflogger: 010e98d3335e5185a8f7496babca50d82a042e84 - React-RCTActionSheet: 0f585d684b540a5bbfc62b0a1fbc5292cff2aefc - React-RCTAnimation: eb0e5b020333f9cc652d85f27a47086fbf56fffd - React-RCTBlob: 4af18ad2a64515c3ede9b829e8532f1508e00894 - React-RCTImage: 08787efa5378ad0e7344943eed1b898619cf956a - React-RCTLinking: ea7ec6fbfdb04df7895c39f15f0e7479acc43bca - React-RCTNetwork: 926b436b6afada9905d969a8e3713cf204905a00 - React-RCTSettings: cc083c9b6e126b7e6ea1128e64837d8b78ceb219 - React-RCTText: c36ddf2bda5131b325e1c2763700f0a63a963e1d - React-RCTVibration: 12a2a859fa22368d2fc3ca7594504fd130b91a18 - React-runtimeexecutor: 04332dda2f2335ea4ddaf9255de069d3269f4e8b - ReactCommon: 200471e0841cf2f7cde1fa2ef3d3c199ed970c07 + react-native-webview: 9946090a1cd4add775cf91327f93bc910bf89286 + React-NativeModulesApple: 3107f777453f953906d9ba9dc5f8cbd91a6ef913 + React-perflogger: daabc494c6328efc1784a4b49b8b74fca305d11c + React-RCTActionSheet: 0e0e64a7cf6c07f1de73d1f0a92d26a70262b256 + React-RCTAnimation: faef65b19e73029c4828167985b0a7c01c62756d + React-RCTAppDelegate: b24e761d235760656226364bb259e3f1508559c2 + React-RCTBlob: 9e9784a84b824b6d7e2cce05a8087c8c3a47a559 + React-RCTImage: 15e211cbb629210ab9c1fa37e07e7100362b12ed + React-RCTLinking: 50d5faf19b02541cefb78ee5d505029283c8ef95 + React-RCTNetwork: dfa9fb4ad2ae459b9193a14204b1d9da907d15a7 + React-RCTSettings: 37611fa97d44a9c5a7ea844cfb953d3513f7ace0 + React-RCTText: 39ed334f64484d07b85a8159cf117814ff069ff6 + React-RCTVibration: 62462803b5fe0842e0be6d9ef86dd74e0df4a614 + React-rncore: 25ad3a3c1e0f4edf77913b9af3af9f497b7f99a5 + React-runtimeexecutor: e5c2f0a1493d72c61b97465ccfedc339157b3179 + React-runtimescheduler: f284b4fdad43fe811041129099f1339b54783135 + React-utils: 22a77b05da25ce49c744faa82e73856dcae1734e + ReactCommon: ff94462e007c568d8cdebc32e3c97af86ec93bb5 ReactNativeART: 78edc68dd4a1e675338cd0cd113319cf3a65f2ab ReactNativeExceptionHandler: 8025d98049c25f186835a3af732dd7c9974d6dce RNCalendarEvents: 7e65eb4a94f53c1744d1e275f7fafcfaa619f7a3 - RNCAsyncStorage: 0c357f3156fcb16c8589ede67cc036330b6698ca + RNCAsyncStorage: 826b603ae9c0f88b5ac4e956801f755109fa4d5c RNCClipboard: f1736c75ab85b627a4d57587edb4b60999c4dd80 RNCPushNotificationIOS: 61a7c72bd1ebad3568025957d001e0f0e7b32191 RNDeviceInfo: 5795b418ed3451ebcaf39384e6cf51f60cb931c9 RNFlashList: ade81b4e928ebd585dd492014d40fb8d0e848aab RNFS: 3ab21fa6c56d65566d1fb26c2228e2b6132e5e32 - RNGestureHandler: 0cba6c7c51a90cd793cf2475cf7fdca613ede300 + RNGestureHandler: fc754e30bb46d093b46b47824e1a04e722fd8a3d RNI18n: e2f7e76389fcc6e84f2c8733ea89b92502351fd8 RNKeychain: 840f8e6f13be0576202aefcdffd26a4f54bfe7b5 RNPermissions: b3d9d00889e37cc184d365ab04bb7a3f20811b1c RNQrGenerator: 1676221c08bfabec978242989c733810dad20959 RNReactNativeHapticFeedback: 6d24decfa94e037c2ecc312407d2a057b7933f10 - RNReanimated: 60e291d42c77752a0f6d6f358387bdf225a87c6e - RNScreens: f7ad633b2e0190b77b6a7aab7f914fad6f198d8d + RNReanimated: 108a53626a492df35db30b98fc922a7189d1c601 + RNScreens: b8d370282cdeae9df85dd5eab20c88eb5181243b RNShare: 807d6f8231b8ebcf6dd839294b877342eb93d4e5 RNSVG: 50cf2c7018e57cf5d3522d98d0a3a4dd6bf9d093 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 vision-camera-code-scanner: dda884a7f3ec8243a2a6d6489b91860648371bca - VisionCamera: e9a95af10e00aaebe99d648ff4519fd336e16ffe - Yoga: d6134eb3d6e3675afc1d6d65ccb3169b60e21980 + VisionCamera: 8bc0089891097ab368afd7b699cc3e239871235c + Yoga: c32e0be1a17f8f1f0e633a3122f7666441f52c82 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a ZendeskAnswerBotProvidersSDK: a024260282886870a15e7a986bf5286c23fd9311 ZendeskAnswerBotSDK: b9f74105b26fda5f74d6639c0dc8fe37f522a867 @@ -1041,6 +1162,6 @@ SPEC CHECKSUMS: ZendeskSupportSDK: 8cf921c496269bb5f53aac9320f3d002a793991d ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: eb8caf6058660eae1ef9ea3a352628c7e1f0ba76 +PODFILE CHECKSUM: a409230d2dbff899bf9ff74097f2aa21f5a54a2a -COCOAPODS: 1.15.2 +COCOAPODS: 1.14.3 diff --git a/jest.config.js b/jest.config.js index 8a9e795fc66..1da09771056 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ module.exports = { preset: "react-native", transformIgnorePatterns: [ - "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@react-native-camera-roll/camera-roll|@codler|remark|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system)" + "node_modules/(?!(jest-)?@react-native|react-native|react-navigation|@react-navigation|react-navigation-redux-helpers|react-native-device-info|rn-placeholder|jsbarcode|@pagopa/react-native-cie|react-native-share|jail-monkey|@react-native-community/art|@react-native-community/push-notification-ios|@react-native-camera-roll/camera-roll|@codler|remark|unified|bail|is-plain-obj|trough|vfile|unist-util-stringify-position|mdast-util-from-markdown|mdast-util-to-string|micromark|parse-entities|character-entities|mdast-util-to-markdown|zwitch|longest-streak|@pagopa/io-react-native-zendesk|rn-qr-generator|mixpanel-react-native|@pagopa/io-app-design-system|uuid)" ], moduleNameMapper: { "\\.svg": "/ts/__mocks__/svgMock.js" @@ -11,10 +11,7 @@ module.exports = { "./node_modules/react-native-gesture-handler/jestSetup.js" ], globalSetup: "./jestGlobalSetup.js", - setupFilesAfterEnv: [ - "@testing-library/jest-native/extend-expect", - "./jestSetupAfterEnv.js" - ], + setupFilesAfterEnv: ["@testing-library/jest-native/extend-expect"], collectCoverage: true, testPathIgnorePatterns: [".*fiscal-code.test.ts$"] }; diff --git a/jest.config.no.timezone.js b/jest.config.no.timezone.js index f994729c8fb..04721ac3cd3 100644 --- a/jest.config.no.timezone.js +++ b/jest.config.no.timezone.js @@ -13,9 +13,6 @@ module.exports = { "./jestSetup.js", "./node_modules/react-native-gesture-handler/jestSetup.js" ], - setupFilesAfterEnv: [ - "@testing-library/jest-native/extend-expect", - "./jestSetupAfterEnv.js" - ], + setupFilesAfterEnv: ["@testing-library/jest-native/extend-expect"], collectCoverage: true }; diff --git a/jestSetup.js b/jestSetup.js index 5c8befdc1b7..3fa3ff0011b 100644 --- a/jestSetup.js +++ b/jestSetup.js @@ -12,6 +12,7 @@ import { NativeModules } from "react-native"; import mockRNDeviceInfo from "react-native-device-info/jest/react-native-device-info-mock"; import mockRNCameraRoll from "@react-native-camera-roll/camera-roll/src/__mocks__/nativeInterface"; import mockZendesk from "./ts/__mocks__/io-react-native-zendesk.ts"; +import "react-native-get-random-values"; jest.mock("@pagopa/io-react-native-zendesk", () => mockZendesk); jest.mock("@react-native-async-storage/async-storage", () => mockAsyncStorage); @@ -139,3 +140,12 @@ jest.mock("mixpanel-react-native", () => ({ init: jest.fn() })) })); + +jest.mock("react-native", () => { + const RN = jest.requireActual("react-native"); // use original implementation, which comes with mocks out of the box + + // eslint-disable-next-line functional/immutable-data + RN.NativeModules.JailMonkey = jest.requireActual("jail-monkey"); + + return RN; +}); diff --git a/jestSetupAfterEnv.js b/jestSetupAfterEnv.js deleted file mode 100644 index 504999a402d..00000000000 --- a/jestSetupAfterEnv.js +++ /dev/null @@ -1,3 +0,0 @@ -global.beforeEach(() => { - jest.useFakeTimers(); -}); diff --git a/locales/de/index.yml b/locales/de/index.yml index 1ef7f3b3923..369a2ee4958 100644 --- a/locales/de/index.yml +++ b/locales/de/index.yml @@ -338,11 +338,8 @@ profile: answer_3: "Ich habe die App nie benutzt" answer_4: "Keiner der oben genannten Punkte" exportData: - cta: "Kopie deiner Daten anfordern" title: "Zugriff auf deine Daten" description: "Du erhältst eine Kopie der mit deinem IO-Profil verbundenen Daten" - info: - title: "Du kannst deine IO-Profilinformationen jederzeit herunterladen." alert: requestTitle: "Möchtest du wirklich all deine Daten exportieren?" oldRequest: "Wir bearbeiten bereits eine Anfrage zum Datenexport" @@ -451,9 +448,6 @@ profile: content: "Wenn diese Option aktiviert ist, werden bei Push-Benachrichtigungen der Absender und der Betreff von Nachrichten angezeigt, auch wenn der Bildschirm gesperrt ist. Diese Informationen werden vom Hersteller deines Betriebssystems und von den Apps der Drittanbieter, die möglicherweise ausgeführt werden, verarbeitet." cta: "OK, habe verstanden!" error: "Bei deiner Anfrage ist ein Fehler aufgetreten" -userMetadata: - errors: - upsertVersion: "Mit dieser Version können keine Benutzer-Metadaten erstellt oder aktualisiert werden" navigation: messages: "Mitteilungen" profile: "Profil" @@ -1830,8 +1824,6 @@ biometric_recognition: send_email_messages: title: "Mitteilungen mittels E-Mail weiterleiten" services: - accessibility: - edit: "Ändere die Einstellung der Dienste" optIn: preferences: completed: @@ -1862,40 +1854,7 @@ services: title: "Wenn du bestätigst, erhältst du keine Mitteilungen" body: "Bei einer manuellen Konfiguration können dir weder aktuelle noch zukünftige IO-Dienste Mitteilungen senden. Damit Dienste, an denen du interessiert bist, dich kontaktieren können, musst du sie einzeln konfigurieren, indem du den Punkt “In der App kontaktieren” auf jeder Registerkarte des Dienstes aktivierst." title: "Dienste" - subTitle: "Aktiviere oder deaktiviere die Dienste, die dir Mitteilungen senden dürfen" contextualHelpTitle: "Was du in deinen Diensten machen kannst" - serviceIsEnabled: "In der App kontaktieren" - serviceNotEnabled: "Der Dienst ist nicht aktiv" - pushNotifications: "Push-Benachrichtigungen senden" - messageReadStatus: "Lesebestätigungen erhalten" - emailForwarding: "E-Mails senden" - tosAndPrivacy: "Nutzungsbedingungen und Datenschutzbestimmungen" - tosLink: "Nutzungsbedingungen" - privacyLink: "Informationen zum Datenschutz" - otherAppsInfo: "Du findest diesen Dienst auch" - otherAppWeb: "online auf" - otherAppIos: "iOS-App" - otherAppAndroid: "Android-App" - contactsAndInfo: "Kontakte und Informationen" - visitWebsite: "Website" - askForAssistance: "Hilfe anfordern" - contactAddress: "Adresse" - contactPhone: "Telefon" - contactSupport: "Support" - tab: - locals: "Lokal" - national: "National" - all: "Alle" - loading: - title: "Lade die Liste der Dienste" - subtitle: "Warte ein paar Sekunden..." - enableAll: "Schnelleinrichtung verwenden" - disableAll: "Manuelle Konfiguration verwenden" - updatingServiceMode: "Bitte warten..." - disableAllTitle: "Möchtest du wirklich alle Dienste deaktivieren?" - disableAllMsg: "Bedenke, dass du nur von Diensten angeschrieben wirst, die tatsächlich über persönliche Informationen verfügen, die sie dir mitteilen möchten. Du wirst keine Spam-Mitteilungen erhalten. Wenn du alle Dienste deaktivierst, können diese dich nicht mehr über IO kontaktieren (bis du sie wieder aktivierst). Du kannst die Dienste weiterhin über andere Kanäle nutzen (Schalter, Website usw.)." - close: "Schließen" - emptyListMessage: "Zurzeit sind keine Dienste verfügbar, zum Aktualisieren ziehe auf dem Bildschirm nach unten" home: institutions: title: "National" @@ -1925,22 +1884,6 @@ services: title: "Nutzungsbedingungen und Datenschutzbestimmungen" tosLink: "Nutzungsbedingungen" privacyLink: "Datenschutzerklärung" -serviceDetail: - fiscalCode: "Steuernummer Körperschaft" - fiscalCodeAccessibility: "Steuernummer Körperschaft" - fiscalCodeAccessibilityCopy: "Kopiere Steuernummer Körperschaft" - contacts: - title: "Dieser Dienst kann:" - headerTitle: "Details zum Dienst" - onUpdateEnabledChannelsFailure: "Beim Speichern der Einstellungen ist ein temporärer Fehler aufgetreten. Bitte versuche es erneut." - disableTitle: "Möchtest du den Dienst wirklich deaktivieren?" - disableMsg: "Bedenke, dass du nur von Diensten angeschrieben wirst, die tatsächlich über persönliche Informationen verfügen, die sie dir mitteilen möchten. Du wirst keine Spam-Mitteilungen erhalten. Wenn du diesen Dienst deaktivierst, kann dieser dich nicht mehr über IO kontaktieren (bis du ihn wieder aktivierst). Du kannst diesen Dienst weiterhin über andere Kanäle nutzen (Schalter, Website usw.)." - lockedMailAlert: "Die E-Mail-Weiterleitung von Mitteilungen ist global {{enabled}}:" - updatePreferences: "Einstellungen ändern" - goTo: "Geh zu den Einstellungen" - enabled: "aktiviert" - disabled: "deaktiviert" - notValidated: "Um die E-Mail-Weiterleitung zu aktivieren, musst du deine E-Mail-Adresse bestätigen." identification: instructions: unlockCode: "Entsperrcode" @@ -2078,7 +2021,7 @@ bonus: error: noCode: "Die Rabattcodes für diese Ermäßigung sind derzeit abgelaufen, bitte versuche es später noch einmal." title: - deals: "Rabatte und Ermäßigungen" + deals: Opportunità description: "Beschreibung" services: "Dienste" contactInfo: "Adressen" @@ -2090,6 +2033,7 @@ bonus: label: "Besuche die Website des Anbieters" landingPage: "Die Ermäßigung nutzen" discountUrl: "Nutze die Ermäßigung" + website: Vai al sito del partner categories: all: "Alle Anbieter" counting: "und weitere {{count}}" @@ -2109,13 +2053,13 @@ bonus: goToDetail: "Die Karte anzeigen" detail: cta: - buyers: "CGN Ermäßigungen anzeigen" + buyers: Scopri le opportunità CGN otp: "Code generieren" eyca: copy: "EYCA Kartennummer kopieren" - pending: "EYCA Ermäßigungen anzeigen" + pending: Scopri le opportunità EYCA bottomSheet: "Besuche die EYCA Website" - showEycaDiscounts: "EYCA Ermäßigungen anzeigen" + showEycaDiscounts: Scopri le opportunità EYCA information: active: "Die Karte ist aktiv und kann bis zum {{date}} verwendet werden." warning: "Achtung! " @@ -2130,7 +2074,7 @@ bonus: eycaPending: "Wir verknüpfen deine nationale Jugendkarte mit einer EYCA Nummer." eycaError: "Wir hatten Probleme mit den EYCA Systemen." eycaBottomSheetTitle: "EYCA Kartennummer" - eycaDescription: "Bis zum Alter von 31 Jahren ist **deine nationale Jugendkarte Mitglied der EYCA** (European Youth Card Association).\n\nMit der Karte kannst du bei den teilnehmenden Händlern Rabatte und Ermäßigungen bei kulturellen Aktivitäten, beim Einkaufen, im Verkehr, in der Gastronomie und bei der Unterbringung erhalten, und zwar auch in den europäischen Ländern, die an der EYCA teilnehmen." + eycaDescription: "Fino al compimento dei 31 anni, **la tua Carta Giovani Nazionale aderisce al circuito EYCA** (European Youth Card Association).\n\nPuoi usare la Carta presso i partner aderenti, per usufruire delle opportunità disponibili per attività culturali, negozi, trasporti, ristorazione e alloggio anche nei paesi europei aderenti al circuito." badge: active: "Aktiv" revoked: "Widerrufen" @@ -2175,7 +2119,7 @@ bonus: toast: "Die Anfrage zur Deaktivierung war erfolgreich" alert: title: "Karte deaktivieren" - message: "Bist du sicher, dass du deine Nationale Jugendkarte deaktivieren möchtest? Wenn du dies bestätigst, verlierst du die Möglichkeit, die Vorteile der Initiative in Anspruch zu nehmen und die Karte wird aus deinem Konto verschwinden." + message: "Vuoi davvero disattivare la tua Carta Giovani Nazionale? Se confermi, non potrai più accedere alle opportunità e la carta scomparirà dal Portafoglio." otp: error: "Aufgrund eines technischen Problems war es uns nicht möglich, einen Rabattcode zu generieren. Bitte versuche es erneut." code: diff --git a/locales/en/index.yml b/locales/en/index.yml index d9255c956c9..cf8bb248389 100644 --- a/locales/en/index.yml +++ b/locales/en/index.yml @@ -376,12 +376,25 @@ profile: body: "We've taken over your request and we will delete your account within 30 days. You can re-subscribe to the IO App at any time with SPID or CIE." cta: Close exportData: - cta: Request a copy of your data + cta: Ask for a copy of the data title: Access your data + subtitle: You can download your IO profile data at any time. description: Receive a copy of the data associated with your IO profile - info: - title: You can download your IO profile information at any time. - body: !include profile/profile_download_data.md + detail: + bulletList: + title: "Your data are:" + item1: your tax code; + item2: your email address and, if applicable, confirmation of its validation; + item3: the text of the Messages you received; + item4: your notification preferences. + paragraph1: The information will be downloaded in YAML format, a standard format for exchanging data between different applications. + paragraph2: + part1: "You will get " + part2: "a message on IO " + part3: with a link and password that lasts 15 days. + paragraph3: + part1: For more information see the. + link: IO Privacy Policy. alert: requestTitle: Are you sure you want to export all your data? oldRequest: We are already proceeding to export all your data @@ -571,9 +584,6 @@ profile: content: If this option is enabled, push notifications show the sender and subject of messages, even when the screen is locked. This information is processed by your OS manager and by third-party apps that may be running. cta: Ok! error: An error occurred with your request -userMetadata: - errors: - upsertVersion: Can't upsert user metadata with this version navigation: messages: Messages profile: Profile @@ -2004,6 +2014,9 @@ messages: archive: success: "Successfully archived!" failure: "Failed to archive" + generic: + failure: "Operation failed" + success: "Operazione successful!" restore: success: "Successfully unarchived." failure: "Failed to unarchive" @@ -2110,8 +2123,6 @@ send_email_messages: title: Email a preview subtitle: You will get a preview of the message at your email address if the service allows. services: - accessibility: - edit: Edit services setup optIn: preferences: completed: @@ -2142,41 +2153,8 @@ services: title: "If you confirm, you won't receive any message" body: "With manual configuration the services on IO, present and future, will not be able to contact you. To allow the services you are interested in to contact you, you will have to configure them one by one by enabling “Contact you in app” in each service detail page." title: Services - subTitle: Enable or disable the services that are allowed to send you messages contextualHelpTitle: About this section contextualHelpContent: !include services/services_home.md - serviceIsEnabled: Contact you in app - serviceNotEnabled: The service is not enabled - pushNotifications: Send you push notifications - messageReadStatus: Receive read receipts - emailForwarding: Send you e-mail - tosAndPrivacy: Terms of use and Privacy - tosLink: Terms and conditions - privacyLink: Privacy information - otherAppsInfo: You can find this service also - otherAppWeb: Online on - otherAppIos: iOS App - otherAppAndroid: Android App - contactsAndInfo: Contacts and info - visitWebsite: Browse Website - askForAssistance: Get support - contactAddress: Address - contactPhone: Phone - contactSupport: Support - tab: - locals: Local - national: National - all: All - loading: - title: Loading the services list - subtitle: Please wait... - enableAll: Use quick setup - disableAll: Use manual setup - updatingServiceMode: Please wait... - disableAllTitle: "Do you really want to disable all the services?" - disableAllMsg: "Remember, on IO you will receive messages only from services having some personalized information to communicate to you. You will not receive spam messages. If you disable all the services, they can no longer contact you through the app (until you enable it again). You can use the services through other channels (front office, website, etc.)" - close: Close - emptyListMessage: There are no services available at this time, pull down to refresh new: New home: featured: @@ -2230,23 +2208,6 @@ services: title: "Terms of use and Privacy" tosLink: "Terms and conditions" privacyLink: "Privacy information" -serviceDetail: - fiscalCode: "Institution's fiscal code" - fiscalCodeAccessibility: "Institution's fiscal code" - fiscalCodeAccessibilityCopy: "Copy Institution's fiscal code" - contacts: - title: "This service can:" - headerTitle: "Service details" - contextualHelpContent: !include services/service_detail.md - onUpdateEnabledChannelsFailure: "There was a temporary problem while saving preferences, please retry." - disableTitle: "Do you really want to disable the service?" - disableMsg: "Remember, on IO you will receive messages only from services having some personalized information to communicate to you. You will not receive spam messages. If you disable the service, it can no longer contact you by IO (until you enable it again). You will be able to use the service by other channels (front office, website, etc.)" - lockedMailAlert: "The email forwarding of messages is globally {{enabled}}:" - updatePreferences: Update your preferences - goTo: Go to preferences - enabled: enabled - disabled: disabled - notValidated: To enable the email forwarding you must validate your email address. identification: instructions: unlockCode: unlock code @@ -2676,7 +2637,7 @@ bonus: error: noCode: Codes for this discount are temporary over, please try again later title: - deals: Deals and opportunities + deals: Opportunities description: Description services: Services contactInfo: Addresses @@ -2685,10 +2646,10 @@ bonus: discountCode: Discount code conditions: Conditions cta: - label: Navigate to operator website + label: Go to partner's website landingPage: Get the benefit discountUrl: Go to the benefit - website: Go to the website + website: Go to partner's website categories: all: All the operators counting: and other {{count}} @@ -2711,14 +2672,14 @@ bonus: goToDetail: View your Carta Giovani detail: cta: - buyers: Discover the CGN benefits + buyers: Discover CGN opportunities. discover: Discover the benefits otp: Generate code eyca: copy: Copy EYCA's card number - pending: Check EYCA discounts + pending: Discover EYCA opportunities. bottomSheet: Check the EYCA website - showEycaDiscounts: Check EYCA discounts + showEycaDiscounts: Discover EYCA opportunities. information: active: "Your card is active and can be used until {{date}}" warning: "Attention! " @@ -2732,7 +2693,7 @@ bonus: eycaPending: "We're linking your Carta Giovani Nazionale to a EYCA Number." eycaError: "We had some issues with EYCA's systems." eycaBottomSheetTitle: "EYCA card number" - eycaDescription: "Until the Until you turn 31, **your National Youth Card is a member of the EYCA circuit** (European Youth Card Association).\n\nBy using the card at participating merchants, you will be able to obtain discounts and concessions on cultural activities, shops, transport, catering and accommodation even in European countries participating in the circuit." + eycaDescription: "Until 31 years old, your **Carta Giovani Nazionale is a member of the EYCA circuit**(European Youth Card Association).\n\n Use the Card at partners to enjoy cultural activities, shopping, transport, restaurants and accommodation in European countries partecipating in the circuit." eycaNumber: Card number badge: active: Active @@ -2779,7 +2740,7 @@ bonus: toast: "The request to deactivate the card has been successfully executed" alert: title: "Deactivate card" - message: "Are you sure to deactivate your Carta Giovani Nazionale? If you confirm, you will lose the possibility to access the benefits provided by the initiative and the card will disappear from your Wallet." + message: "Do you really want to deactivate your Carta Giovani Nazionale? If you confirm, you will no longer be able to access the opportunities and the card will disappear from your Wallet." otp: error: "Due to a technical problem we were unable to generate a discount code. Please try again." code: @@ -3125,7 +3086,7 @@ features: title: Nessuna ricevuta trovata body: Se stai cercando la ricevuta di un avviso pagoPA che hai pagato in passato, rivolgiti all’ente creditore. banner: - content: Stiamo generando le ricevute di pagamento in formato PDF. + content: "Stiamo generando le ricevute in formato PDF: se in questa lista trovi dei doppioni, ignorali pure!" action: Scopri di più title: Operazioni precedenti bottomSheet: @@ -3133,7 +3094,7 @@ features: noticeHeading: Avvisi pagati di recente noticeBody: "Stiamo aggiornando i nostri sistemi: non appena pronte, troverai le ricevute in formato PDF nella sezione Pagamenti." allNoticesHeading: Tutti gli altri avvisi - allNoticesBody: Per ottenere le ricevute di operazioni eseguite prima del 1° aprile 2023, rivolgiti all’ente creditore. + allNoticesBody: Per ottenere le ricevute in formato PDF di operazioni eseguite prima del 1° aprile 2023, rivolgiti all’ente creditore. multiplePayment: Pagamento multiplo title: Ricevute pagoPA button: Vedi tutte @@ -3886,7 +3847,7 @@ bonusCard: transaction: details: error: - title: É stato riscontrato un errore + title: We couldn't retrieve the receipt title: Dettaglio operazione totalAmount: Totale totalFee: Il totale comprende @@ -3900,8 +3861,8 @@ transaction: paymentMethod: Metodo di pagamento authCode: Codice autorizzativo rrn: RRN - executedBy: Eseguito da - headedTo: Intestato a + executedBy: Eseguita da + headedTo: Metodo intestato a alert: content: Rivolgiti all’Ente Creditore se hai bisogno della ricevuta. operation: diff --git a/locales/en/profile/profile_download_data.md b/locales/en/profile/profile_download_data.md deleted file mode 100644 index 9508aa0e414..00000000000 --- a/locales/en/profile/profile_download_data.md +++ /dev/null @@ -1,34 +0,0 @@ -The list of your information includes: - -- Your fiscal code; -- The email address you indicated and, if necessary, confirmation of its validation; -- The text of the messages you received; -- Your notification preferences. - - -The information will be downloaded in ** YAML ** format, a standard format that in computer science allows the exchange of data between different applications. - -The data download procedure is protected by a password, which will be sent to you together with the download link via message in the app. -After creation, the copy of your data will be accessible for 15 days. - - -**Information on the processing of your data** - -Pursuant to the GDPR, we confirm that we process your personal data within the Io App. -In particular, we process your identification and contact data for identification and authentication purposes, recording preferences and sending messages strictly related to the functioning of the App, as well as assistance and debugging and activities aimed at ensuring security. In addition, as data processors of the Dispensing Bodies, in order to allow you to use the Services, we also process the data contained in the messages in addition to your identification and contact data. - -Some of the data we process are collected by your Spid provider at the time of your registration or, if you have registered via CIE, by the Ministry of the Interior. - - Your data is also processed through third party suppliers, located in countries outside the EEA. We use the guarantees provided for in art. 44 and following of the GDPR. In particular, we use Privacy Shield certified suppliers and, in any case, where necessary, we have bound these suppliers to compliance with the standard contractual conditions approved by the Commission. - -We keep your data for a limited time. In particular, the data relating to the Messages sent on behalf of the Dispensing Bodies are deleted after 3 years from their receipt and your identification data are kept for 10 years from your cancellation. - -You have the right to request the exercise of your right to limit and oppose the treatment and to request the correction of your email address. You can also contact the Guarantor for the protection of personal data. -With reference to the data of the Services offered by the Providers, all rights must be exercised with them. - - -More information is available in our [Privacy Policy](ioit://PROFILE_PRIVACY) which is accessible at all times in the Profile / Privacy and Terms of Use section. -Invia commenti -Cronologia -Salvate -Community \ No newline at end of file diff --git a/locales/it/index.yml b/locales/it/index.yml index 3f5238578d7..f5c88da1f8b 100644 --- a/locales/it/index.yml +++ b/locales/it/index.yml @@ -376,12 +376,25 @@ profile: body: Il tuo profilo verrà eliminato entro 30 giorni. Puoi tornare nuovamente su IO in qualsiasi momento entrando con SPID o CIE. cta: Chiudi exportData: - cta: Richiedi copia dei tuoi dati + cta: Richiedi una copia dei dati title: Accedi ai tuoi dati + subtitle: Puoi scaricare i dati principali del tuo profilo IO in qualsiasi momento. description: Ricevi una copia dei dati associati al tuo profilo IO - info: - title: Puoi scaricare le informazioni relative al tuo profilo IO in qualsiasi momento. - body: !include profile/profile_download_data.md + detail: + bulletList: + title: "I dati sono:" + item1: il tuo codice fiscale; + item2: l’indirizzo email da te indicato e, nel caso, la conferma della sua validazione; + item3: il testo dei Messaggi che hai ricevuto; + item4: le tue preferenze di notifica. + paragraph1: Le informazioni saranno scaricate in formato YAML, un formato standard che in informatica consente lo scambio di dati fra applicazioni diverse. + paragraph2: + part1: "Riceverai un " + part2: "messaggio su IO " + part3: in cui sono presenti un link e una password validi per 15 giorni. + paragraph3: + part1: Per maggiori informazioni consulta l’ + link: Informativa privacy di IO. alert: requestTitle: Vuoi davvero esportare tutti i tuoi dati? oldRequest: Abbiamo già preso in carico una richiesta di esportazione dei dati. @@ -571,9 +584,6 @@ profile: content: "Una notifica push è un messaggio che ricevi sul tuo dispositivo anche quando non usi l'app. Se attivi questa opzione, riceverai notifiche push che ti mostreranno il mittente e l'oggetto del messaggio.\nQueste informazioni sono gestite dal sistema operativo del tuo dispositivo e dalle eventuali app di terze parti che stai utilizzando." cta: Ho capito! error: Si è verificato un errore con la tua richiesta -userMetadata: - errors: - upsertVersion: Impossibile creare o aggiornare i metadata utente con questa versione navigation: messages: Messaggi profile: Profilo @@ -2004,6 +2014,9 @@ messages: archive: success: "Archiviazione riuscita!" failure: "L’archiviazione non è andata a buon fine" + generic: + failure: "L'operazione non è andata a buon fine" + success: "Operazione riuscita!" restore: success: "Ripristino effettuato." failure: "Il ripristino non è andato a buon fine" @@ -2110,8 +2123,6 @@ send_email_messages: title: Invia un’anteprima via email subtitle: Riceverai un'anteprima del messaggio al tuo indirizzo email, se il servizio lo consente. services: - accessibility: - edit: Modifica l'impostazione dei servizi optIn: preferences: completed: @@ -2142,41 +2153,8 @@ services: title: "Se confermi, non riceverai alcun messaggio" body: "Con la configurazione manuale i servizi di IO, presenti o futuri, non possono inviarti messaggi. Per permettere ai servizi che ti interessano di contattarti, dovrai configurarli uno a uno abilitando la voce “Contattarti in app” che trovi in ogni scheda servizio." title: Servizi - subTitle: Attiva o disattiva i servizi da cui puoi ricevere messaggi contextualHelpTitle: Cosa puoi fare nei tuoi Servizi contextualHelpContent: !include services/services_home.md - serviceIsEnabled: Contattarti in app - serviceNotEnabled: Il servizio non è attivo - pushNotifications: Inviarti notifiche push - messageReadStatus: Ricevere conferme di lettura - emailForwarding: Inviarti e-mail - tosAndPrivacy: Termini e Privacy - tosLink: Termini e condizioni d'uso - privacyLink: Informativa sulla privacy - otherAppsInfo: Puoi usare questo servizio anche - otherAppWeb: Online su - otherAppIos: App iOS - otherAppAndroid: App Android - contactsAndInfo: Contatti ed informazioni - visitWebsite: Visita il sito - askForAssistance: Richiedi assistenza - contactAddress: Indirizzo - contactPhone: Telefono - contactSupport: Assistenza - tab: - locals: Locali - national: Nazionali - all: Tutti - loading: - title: Sto caricando l'elenco dei servizi - subtitle: attendi qualche secondo... - enableAll: Usa configurazione rapida - disableAll: Usa configurazione manuale - updatingServiceMode: Attendi... - disableAllTitle: Vuoi davvero disattivare tutti i servizi? - disableAllMsg: "Ricorda che su IO ti scriveranno solo i servizi che effettivamente hanno qualche informazione personalizzata da comunicarti. Non riceverai messaggi di spam. Se disattivi tutti i servizi, questi non potranno più contattarti tramite IO (finché non li riattiverai). Potrai continuare a fruire i servizi attraverso altri canali (sportello, sito, etc.)" - close: Chiudi - emptyListMessage: Non ci sono servizi disponibili al momento, trascina in basso per aggiornare new: Nuovo home: featured: @@ -2230,23 +2208,6 @@ services: title: "Termini e Privacy" tosLink: "Termini e condizioni d'uso" privacyLink: "Informativa sulla privacy" -serviceDetail: - fiscalCode: "C.F. Ente" - fiscalCodeAccessibility: "Codice fiscale Ente" - fiscalCodeAccessibilityCopy: "Copia codice fiscale Ente" - contacts: - title: "Questo servizio può:" - headerTitle: "Dettagli del Servizio" - contextualHelpContent: !include services/service_detail.md - onUpdateEnabledChannelsFailure: "Si è verificato un errore temporaneo nel salvataggio delle preferenze, riprova per piacere." - disableTitle: "Vuoi davvero disattivare il servizio?" - disableMsg: "Ricorda che su IO ti scriveranno solo i servizi che effettivamente hanno qualche informazione personalizzata da comunicarti. Non riceverai messaggi di spam. Se disattivi il servizio, questo non potrà più contattarti tramite IO (finché non lo riattiverai). Potrai continuare a fruire il servizio attraverso altri canali (sportello, sito, etc.)" - lockedMailAlert: "L'inoltro dei messaggi via email è {{enabled}} globalmente:" - updatePreferences: Modifica le preferenze - goTo: Vai alle preferenze - enabled: abilitato - disabled: disabilitato - notValidated: Per poter abilitare le notifiche via email devi validare il tuo indirizzo email." identification: instructions: unlockCode: codice di sblocco @@ -2676,7 +2637,7 @@ bonus: error: noCode: I codici sconto di questa agevolazione sono al momento terminati, riprova più tardi title: - deals: Agevolazioni e opportunità + deals: Opportunità description: Descrizione services: Servizi contactInfo: Contatti e informazioni @@ -2688,7 +2649,7 @@ bonus: label: Vai al sito dell'operatore landingPage: Accedi all’opportunità discountUrl: Vai all'opportunità - website: Vai al sito dell'esercente + website: Vai al sito del partner categories: all: Tutti gli operatori counting: e altre {{count}} @@ -2711,14 +2672,14 @@ bonus: goToDetail: Visualizza la Carta detail: cta: - buyers: Vedi le opportunità CGN + buyers: Scopri le opportunità CGN discover: Scopri le opportunità otp: Genera codice eyca: copy: Copia numero carta EYCA - pending: Vedi le opportunità EYCA + pending: Scopri le opportunità EYCA bottomSheet: Visita il sito di EYCA - showEycaDiscounts: Vedi le opportunità EYCA + showEycaDiscounts: Scopri le opportunità EYCA information: active: "La carta è attiva e può essere usata fino al {{date}}" warning: "Attenzione! " @@ -2733,7 +2694,7 @@ bonus: eycaPending: "Stiamo associando la tua Carta Giovani Nazionale a un Numero EYCA." eycaError: "Abbiamo riscontrato problemi con i sistemi EYCA." eycaBottomSheetTitle: "Numero di carta EYCA" - eycaDescription: "Fino al compimento dei 31 anni, **la tua Carta Giovani Nazionale aderisce al circuito EYCA** (European Youth Card Association).\n\nUsando la carta presso gli esercenti aderenti, potrai ottenere sconti e agevolazioni su attività culturali, negozi, trasporti, ristorazione e alloggio anche nei paesi europei aderenti al circuito." + eycaDescription: "Fino al compimento dei 31 anni, **la tua Carta Giovani Nazionale aderisce al circuito EYCA** (European Youth Card Association).\n\nPuoi usare la Carta presso i partner aderenti, per usufruire delle opportunità disponibili per attività culturali, negozi, trasporti, ristorazione e alloggio anche nei paesi europei aderenti al circuito." badge: active: Attiva revoked: Revocata @@ -2779,7 +2740,7 @@ bonus: toast: "La richiesta di disattivazione è avvenuta correttamente" alert: title: "Disattiva carta" - message: "Sei sicuro di voler disattivare la tua Carta Giovani Nazionale? Se confermi, perderai la possibilità di accedere alle agevolazioni previste dall’iniziativa e la carta scomparirà dal Portafoglio." + message: "Vuoi davvero disattivare la tua Carta Giovani Nazionale? Se confermi, non potrai più accedere alle opportunità e la carta scomparirà dal Portafoglio." otp: error: "Per un problema tecnico non siamo riusciti a generare un codice sconto. Ti chiediamo di riprovare." code: @@ -3125,7 +3086,7 @@ features: title: Nessuna ricevuta trovata body: Se stai cercando la ricevuta di un avviso pagoPA che hai pagato in passato, rivolgiti all’ente creditore. banner: - content: Stiamo generando le ricevute di pagamento in formato PDF. + content: "Stiamo generando le ricevute in formato PDF: se in questa lista trovi dei doppioni, ignorali pure!" action: Scopri di più title: Operazioni precedenti bottomSheet: @@ -3133,7 +3094,7 @@ features: noticeHeading: Avvisi pagati di recente noticeBody: "Stiamo aggiornando i nostri sistemi: non appena pronte, troverai le ricevute in formato PDF nella sezione Pagamenti." allNoticesHeading: Tutti gli altri avvisi - allNoticesBody: Per ottenere le ricevute di operazioni eseguite prima del 1° aprile 2023, rivolgiti all’ente creditore. + allNoticesBody: Per ottenere le ricevute in formato PDF di operazioni eseguite prima del 1° aprile 2023, rivolgiti all’ente creditore. multiplePayment: Pagamento multiplo title: Ricevute pagoPA button: Vedi tutte @@ -3886,7 +3847,7 @@ bonusCard: transaction: details: error: - title: É stato riscontrato un errore + title: Non siamo riusciti a caricare la ricevuta title: Dettaglio operazione totalAmount: Totale totalFee: Il totale comprende @@ -3900,8 +3861,8 @@ transaction: paymentMethod: Metodo di pagamento authCode: Codice autorizzativo rrn: RRN - executedBy: Eseguito da - headedTo: Intestato a + executedBy: Eseguita da + headedTo: Metodo intestato a alert: content: Rivolgiti all’Ente Creditore se hai bisogno della ricevuta. operation: diff --git a/locales/it/profile/profile_download_data.md b/locales/it/profile/profile_download_data.md deleted file mode 100644 index d310fc247d9..00000000000 --- a/locales/it/profile/profile_download_data.md +++ /dev/null @@ -1,30 +0,0 @@ -L’elenco delle tue informazioni comprende: - -- Il tuo codice fiscale; -- L’indirizzo email da te indicato e, nel caso, la conferma della sua validazione; -- Il testo dei Messaggi da te ricevuti; -- Le tue preferenze di notifica. - - -Le informazioni saranno scaricate in formato **YAML**, un formato standard che in informatica consente lo scambio di dati fra applicazioni diverse. - -La procedura di scaricamento dati è protetta da una password, che ti verrà inviata insieme al link per lo scaricamento tramite messaggio in app. -Dopo la creazione, la copia dei tuoi dati sarà accessibile per 15 giorni. - - -**Informazioni sul trattamento dei tuoi dati** - -Ai sensi del GDPR, ti confermiamo che trattiamo i tuoi dati personali all’interno dell’App Io. -In particolare, trattiamo i tuoi dati identificativi e di contatto per finalità di identificazione e autenticazione, registrazione delle preferenze e invio di messaggi strettamente legati al funzionamento dell’App, nonché di assistenza e debug e attività volte ad assicurare la sicurezza. Inoltre, come responsabili del trattamento degli Enti Erogatori, per consentirti di usufruire dei Servizi, trattiamo oltre ai tuoi dati identificativi e di contatto, anche i dati contenuti ndei messaggi. - -Alcuni dei dati da noi trattati sono raccolti dal tuo provider Spid al momento della tua registrazione ovvero, qualora tu ti sia registrato tramite CIE, dal Ministero dell’Interno. - - I tuoi dati sono trattati anche per tramite di fornitori terzi, situati in paesi al di fuori dello SEE. Utilizziamo per i trasferimenti extra UE le garanzie previste dagli art. 44 e seguenti del GDPR. In particolare, utilizziamo fornitori certificati Privacy Shield e, in ogni caso, laddove necessario, abbiamo vincolato tali fornitori al rispetto delle condizioni contrattuali tipo approvate dalla Commissione. - -Conserviamo i tuoi dati per un tempo limitato. In particolare, I dati relativi ai Messaggi inviati per conto degli Enti Erogatori sono cancellati dopo 3 anni dalla loro ricezione e i tuoi dati identificativi sono conservati per 10 anni dalla tua cancellazione. - -Hai diritto a richiedere la esercitare il tuo diritto di limitazione e opposizione del trattamento e a chiedere la rettifica del tuo indirizzo email. Potrai inoltre rivolgerti al Garante per la protezione dei dati personali. -Con riferimento ai dati dei Servizi offerti dagli Enti Erogatori, tutti i diritti dovranno essere esercitati presso di loro. - - -Maggiori informazioni sono disponibili nella nostra [Informativa Privacy](ioit://PROFILE_PRIVACY) che è accessibile in ogni momento nella sezione Profilo/Privacy e Condizioni d’uso. diff --git a/metro.config.js b/metro.config.js index b6ca471aff0..d10178991d0 100644 --- a/metro.config.js +++ b/metro.config.js @@ -1,21 +1,21 @@ -const { getDefaultConfig } = require("metro-config"); +const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config"); -module.exports = (async () => { - const { - resolver: { sourceExts, assetExts } - } = await getDefaultConfig(); - const withE2ESourceExts = process.env.RN_SRC_EXT - ? process.env.RN_SRC_EXT.split(",").concat(sourceExts) - : sourceExts; - return { - transformer: { - babelTransformerPath: require.resolve("react-native-svg-transformer"), - experimentalImportSupport: false, - inlineRequires: true - }, - resolver: { - assetExts: assetExts.filter(ext => ext !== "svg"), - sourceExts: [...withE2ESourceExts, "svg"] - } - }; -})(); +const { + resolver: { sourceExts, assetExts } +} = getDefaultConfig(__dirname); + +const withE2ESourceExts = process.env.RN_SRC_EXT + ? process.env.RN_SRC_EXT.split(",").concat(sourceExts) + : sourceExts; + +const config = { + transformer: { + babelTransformerPath: require.resolve("react-native-svg-transformer") + }, + resolver: { + assetExts: assetExts.filter(ext => ext !== "svg"), + sourceExts: [...withE2ESourceExts, "svg"] + } +}; + +module.exports = mergeConfig(getDefaultConfig(__dirname), config); diff --git a/package.json b/package.json index 135eea4141a..ebe52347d7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "italia-app", - "version": "2.63.0-rc.5", + "version": "2.64.0-rc.1", "io_backend_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_backend.yaml", "io_public_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_public.yaml", "io_content_specs": "https://raw.githubusercontent.com/pagopa/io-services-metadata/1.0.34/definitions.yml", @@ -8,7 +8,6 @@ "io_cgn_merchants_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_cgn_operator_search.yaml", "api_fci": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_io_sign.yaml", "io_eu_covid_cert": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_eucovidcert.yaml", - "io_sicilia_vola_token": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_mit_voucher.yaml", "io_pn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/api_pn.yaml", "io_consumed_pn_specs": "https://raw.githubusercontent.com/pagopa/io-backend/v13.39.1-RELEASE/openapi/consumed/api-piattaforma-notifiche.yaml", "api_cdc": "assets/CdcSwagger.yml", @@ -112,9 +111,9 @@ "@pagopa/io-react-native-login-utils": "^1.0.1", "@pagopa/io-react-native-wallet": "^0.11.1", "@pagopa/io-react-native-zendesk": "^0.3.29", - "@pagopa/react-native-cie": "^1.2.1", + "@pagopa/react-native-cie": "^1.3.0", "@pagopa/ts-commons": "^10.15.0", - "@react-native-async-storage/async-storage": "^1.17.10", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-native-camera-roll/camera-roll": "5.6.1", "@react-native-clipboard/clipboard": "^1.10.0", "@react-native-community/push-notification-ios": "^1.8.0", @@ -127,20 +126,19 @@ "@redux-saga/testing-utils": "^1.1.3", "@shopify/flash-list": "~1.4.3", "@xstate/react": "^3.0.1", + "@xstate5/react": "npm:@xstate/react@4", "async-mutex": "^0.1.3", "buffer": "^4.9.1", "color": "^3.0.0", "date-fns": "^1.29.0", - "deprecated-react-native-prop-types": "^2.3.0", "detox": "^19.9.2", "eslint-plugin-react-native": "^4.0.0", - "fbjs": "^3.0.2", "fp-ts": "^2.12.1", "front-matter": "^4.0.2", "hastscript": "^7.0.2", "hoist-non-react-statics": "^3.0.1", "io-ts": "^2.2.16", - "jail-monkey": "^2.3.2", + "jail-monkey": "^2.8.0", "jwk-thumbprint": "^0.1.4", "lodash": "^4.17.21", "metro-babel-register": "^0.72.1", @@ -148,8 +146,8 @@ "pako": "^2.1.0", "path-browserify": "0.0.0", "pdf-lib": "^1.17.1", - "react": "18.1.0", - "react-native": "0.70.15", + "react": "18.2.0", + "react-native": "0.72.14", "react-native-android-open-settings": "^1.3.0", "react-native-background-timer": "2.1.1", "react-native-barcode-builder": "^2.0.0", @@ -175,24 +173,23 @@ "react-native-markdown-display": "^7.0.2", "react-native-masked-text": "^1.13.0", "react-native-pager-view": "^6.2.3", - "react-native-pdf": "^6.4.0", + "react-native-pdf": "6.7.4", "react-native-pdf-thumbnail": "^1.2.1", "react-native-permissions": "^4.0.0", - "react-native-popup-menu": "^0.15.11", "react-native-push-notification": "^8.1.1", - "react-native-reanimated": "^2.9.1", + "react-native-reanimated": "^3.12.0", "react-native-render-html": "^6.3.1", "react-native-responsive-screen": "^1.4.1", - "react-native-safe-area-context": "^3.3.2", + "react-native-safe-area-context": "^4.10.4", "react-native-screen-brightness": "^2.0.0-alpha", - "react-native-screens": "^2.18.1", + "react-native-screens": "^3.31.1", "react-native-share": "7.3.9", "react-native-splash-screen": "^3.2.0", "react-native-svg": "^15.1.0", "react-native-tab-view": "3.5.2", "react-native-url-polyfill": "^2.0.0", "react-native-view-shot": "3.1.2", - "react-native-vision-camera": "2.15.4", + "react-native-vision-camera": "2.16.7", "react-native-webview": "^13.10.3", "react-native-xml2js": "^1.0.3", "react-redux": "8.1.3", @@ -224,34 +221,39 @@ "vision-camera-code-scanner": "^0.2.0", "xml2js": "^0.5.0", "xss": "1.0.10", - "xstate": "^4.33.6" + "xstate": "^4.33.6", + "xstate5": "npm:xstate@5" }, "devDependencies": { - "@babel/core": "^7.18.8", - "@babel/preset-typescript": "^7.16.7", - "@babel/runtime": "^7.15.3", + "@babel/core": "^7.20.0", + "@babel/preset-env": "^7.20.0", + "@babel/preset-typescript": "^7.23.3", + "@babel/runtime": "^7.20.0", "@jambit/eslint-plugin-typed-redux-saga": "^0.4.0", "@pagopa/openapi-codegen-ts": "^13.2.0", - "@react-native-community/eslint-config": "^3.0.1", + "@react-native-community/eslint-config": "^3.2.0", + "@react-native/eslint-config": "^0.72.2", + "@react-native/metro-config": "^0.72.12", + "@stylistic/eslint-plugin-js": "^2.1.0", "@testing-library/jest-native": "^3.4.3", "@testing-library/react-native": "^8.0.0", + "@tsconfig/react-native": "^3.0.0", "@types/chalk": "^2.2.0", "@types/color": "^3.0.0", "@types/fs-extra": "^5.0.4", "@types/hoist-non-react-statics": "^3.0.1", - "@types/jest": "^25.1.0", + "@types/jest": "^29.2.1", "@types/js-yaml": "^3.12.1", "@types/lodash": "^4.14.157", "@types/node": "^10.1.0", "@types/node-fetch": "^2.1.7", "@types/pako": "^2.0.0", "@types/prettier": "^2.7.3", - "@types/react": "16.9.43", - "@types/react-native": "0.70.19", + "@types/react": "^18.0.24", "@types/react-native-background-timer": "^2.0.0", "@types/react-native-i18n": "^2.0.0", "@types/react-native-push-notification": "^8.1.1", - "@types/react-test-renderer": "16.0.3", + "@types/react-test-renderer": "^18.0.0", "@types/redux-logger": "^3.0.6", "@types/redux-mock-store": "^1.0.2", "@types/semver": "^6.2.0", @@ -264,12 +266,12 @@ "@typescript-eslint/parser": "^7.13.0", "@xstate/cli": "^0.3.3", "abortcontroller-polyfill": "1.7.3", - "babel-jest": "^26.6.3", + "babel-jest": "^29.2.1", "babel-plugin-macros": "^3.1.0", "babel-preset-react-native": "^4.0.1", "chalk": "^2.4.1", "danger": "^10.3.0", - "eslint": "^8.6.0", + "eslint": "^8.19.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-functional": "^4.1.1", "eslint-plugin-import": "^2.25.4", @@ -279,12 +281,12 @@ "eslint-plugin-sonarjs": "^0.11.0", "fs-extra": "^7.0.0", "husky": "^8.0.0", - "jest": "^26.6.3", + "jest": "^29.2.1", "jest-circus": "^26.6.3", "jest-junit": "^13.0.0", "js-yaml": "^3.13.1", "lint-staged": "^13.2.0", - "metro-react-native-babel-preset": "^0.73.0", + "metro-react-native-babel-preset": "^0.76.9", "mockdate": "^3.0.2", "mockttp": "2.4.0", "node-fetch": "^2.6.7", @@ -294,9 +296,9 @@ "postinstall-postinstall": "^1.0.0", "prettier": "2.8.8", "react-native-bundle-visualizer": "^2.2.1", - "react-native-get-random-values": "^1.7.0", + "react-native-get-random-values": "^1.11.0", "react-native-svg-transformer": "^0.14.3", - "react-test-renderer": "18.1.0", + "react-test-renderer": "18.2.0", "redux-mock-store": "^1.5.4", "redux-saga-test-plan": "4.0.3", "rn-nodeify": "^10.0.1", @@ -304,17 +306,24 @@ "ts-node": "^7.0.1", "typescript": "^5.4.5" }, + "engines": { + "node": ">=16" + }, "resolutions": { - "@types/react": "16.7.18", - "@types/prop-types": "15.5.5" + "@types/react": "^18.0.24", + "@babel/preset-typescript": "^7.23.3", + "@babel/plugin-transform-typescript": "^7.23.6", + "@babel/plugin-syntax-typescript": "^7.23.3", + "@babel/types": "^7.23.6", + "@xstate5/react/xstate": "^5.13.0" }, "react-native": { "path": "path-browserify", "crypto": "react-native-crypto" }, "browser": { - "path": "path-browserify", - "crypto": "react-native-crypto" + "crypto": "react-native-crypto", + "path": "path-browserify" }, "repository": { "type": "git", diff --git a/patches/@types+react-native+0.70.19.patch b/patches/@types+react-native+0.70.19.patch deleted file mode 100644 index ea89767f364..00000000000 --- a/patches/@types+react-native+0.70.19.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/node_modules/@types/react-native/index.d.ts b/node_modules/@types/react-native/index.d.ts -index 12831dd..aedaba3 100644 ---- a/node_modules/@types/react-native/index.d.ts -+++ b/node_modules/@types/react-native/index.d.ts -@@ -8548,6 +8548,10 @@ export interface UIManagerStatic { - * commandArgs - Args of the native method that we can pass from JS to native. - */ - dispatchViewManagerCommand: (reactTag: number | null, commandID: number | string, commandArgs?: any[]) => void; -+ -+ //Added missing accesibility definition -+ sendAccessibilityEvent(reactTag?: number, eventType: number): void; -+ AccessibilityEventTypes: { typeViewFocused : number; } - } - - export interface SwitchPropsIOS extends ViewProps { diff --git a/patches/patches.md b/patches/patches.md index 220e7284499..fe13f43f815 100644 --- a/patches/patches.md +++ b/patches/patches.md @@ -159,11 +159,10 @@ Created on **16/01/2023** This was fine as long as each event was originally created and handled using this library only but initially another library was used, react-native-add-calendar-event, which treated event's Id as long -### react-native+0.70.15 +### react-native-reanimated+3.12.0.patch -Created on **24/05/2024** +Created on **16/01/2023** #### Reason: -- This patch implements the readAsArrayBuffer function in react-native by allowing code-gen to autogenerate - API call responses that have a Blob. this is a known issue that the meta team have not yet provided direct support, - as already indicated in [this issue](https://github.com/facebook/react-native/issues/34402) + +- Patch to fix a crash on android due to wrong file definition, to be removed in a future update diff --git a/patches/react-native+0.70.15.patch b/patches/react-native+0.72.14.patch similarity index 61% rename from patches/react-native+0.70.15.patch rename to patches/react-native+0.72.14.patch index d7ecae4316c..b8b7d9b874f 100644 --- a/patches/react-native+0.70.15.patch +++ b/patches/react-native+0.72.14.patch @@ -1,85 +1,34 @@ +diff --git a/node_modules/react-native/.DS_Store b/node_modules/react-native/.DS_Store +new file mode 100644 +index 0000000..b1221b4 +Binary files /dev/null and b/node_modules/react-native/.DS_Store differ diff --git a/node_modules/react-native/Libraries/.DS_Store b/node_modules/react-native/Libraries/.DS_Store new file mode 100644 -index 0000000..e69de29 -diff --git a/node_modules/react-native/Libraries/Blob/FileReader.js b/node_modules/react-native/Libraries/Blob/FileReader.js -index c0b7fea..e1b961d 100644 ---- a/node_modules/react-native/Libraries/Blob/FileReader.js -+++ b/node_modules/react-native/Libraries/Blob/FileReader.js -@@ -9,6 +9,7 @@ - */ - - const Blob = require('./Blob'); -+const { toByteArray } = require('base64-js'); - const EventTarget = require('event-target-shim'); - - import NativeFileReaderModule from './NativeFileReaderModule'; -@@ -43,8 +44,8 @@ class FileReader extends (EventTarget(...READER_EVENTS): any) { - DONE: number = DONE; - - _readyState: ReadyState; -- _error: ?Error; -- _result: ?ReaderResult; -+ _error: ? Error; -+ _result: ? ReaderResult; - _aborted: boolean = false; - - constructor() { -@@ -60,21 +61,48 @@ class FileReader extends (EventTarget(...READER_EVENTS): any) { - - _setReadyState(newState: ReadyState) { - this._readyState = newState; -- this.dispatchEvent({type: 'readystatechange'}); -+ this.dispatchEvent({ type: 'readystatechange' }); - if (newState === DONE) { - if (this._aborted) { -- this.dispatchEvent({type: 'abort'}); -+ this.dispatchEvent({ type: 'abort' }); - } else if (this._error) { -- this.dispatchEvent({type: 'error'}); -+ this.dispatchEvent({ type: 'error' }); - } else { -- this.dispatchEvent({type: 'load'}); -+ this.dispatchEvent({ type: 'load' }); - } -- this.dispatchEvent({type: 'loadend'}); -+ this.dispatchEvent({ type: 'loadend' }); - } - } - -- readAsArrayBuffer() { -- throw new Error('FileReader.readAsArrayBuffer is not implemented'); -+ readAsArrayBuffer(blob: ?Blob) { -+ this._aborted = false; -+ -+ if (blob == null) { -+ throw new TypeError( -+ "Failed to execute 'readAsArrayBuffer' on 'FileReader': parameter 1 is not of type 'Blob'", -+ ); -+ } -+ -+ NativeFileReaderModule.readAsDataURL(blob.data).then( -+ (text: string) => { -+ if (this._aborted) { -+ return; -+ } -+ -+ const base64 = text.split(',')[1]; -+ const typedArray = toByteArray(base64); -+ -+ this._result = typedArray.buffer; -+ this._setReadyState(DONE); -+ }, -+ error => { -+ if (this._aborted) { -+ return; -+ } -+ this._error = error; -+ this._setReadyState(DONE); -+ }, -+ ) - } +index 0000000..a1f9425 +Binary files /dev/null and b/node_modules/react-native/Libraries/.DS_Store differ +diff --git a/node_modules/react-native/Libraries/ReactNative/UIManager.d.ts b/node_modules/react-native/Libraries/ReactNative/UIManager.d.ts +index 1f0b346..12603ec 100644 +--- a/node_modules/react-native/Libraries/ReactNative/UIManager.d.ts ++++ b/node_modules/react-native/Libraries/ReactNative/UIManager.d.ts +@@ -151,6 +151,10 @@ export interface UIManagerStatic { + commandID: number | string, + commandArgs?: Array, + ) => void; ++ ++ //Added missing accesibility definition ++ sendAccessibilityEvent(reactTag: number | null, eventType: number): void; ++ AccessibilityEventTypes: { typeViewFocused : number; } + } - readAsDataURL(blob: ?Blob) { + export const UIManager: UIManagerStatic; +diff --git a/node_modules/react-native/React/.DS_Store b/node_modules/react-native/React/.DS_Store +new file mode 100644 +index 0000000..a7392ec +Binary files /dev/null and b/node_modules/react-native/React/.DS_Store differ +diff --git a/node_modules/react-native/React/AccessibilityResources/.DS_Store b/node_modules/react-native/React/AccessibilityResources/.DS_Store +new file mode 100644 +index 0000000..e6a50bd +Binary files /dev/null and b/node_modules/react-native/React/AccessibilityResources/.DS_Store differ diff --git a/node_modules/react-native/React/AccessibilityResources/it.lproj/Localizable.strings b/node_modules/react-native/React/AccessibilityResources/it.lproj/Localizable.strings new file mode 100644 index 0000000..b0bc264 @@ -113,53 +62,12 @@ index 0000000..b0bc264 +"collapsed"="compresso"; +"mixed"="misto"; \ No newline at end of file -diff --git a/node_modules/react-native/index.js b/node_modules/react-native/index.js -index d59ba34..d0554fd 100644 ---- a/node_modules/react-native/index.js -+++ b/node_modules/react-native/index.js -@@ -435,32 +435,16 @@ module.exports = { - }, - // Deprecated Prop Types - get ColorPropType(): $FlowFixMe { -- invariant( -- false, -- 'ColorPropType has been removed from React Native. Migrate to ' + -- "ColorPropType exported from 'deprecated-react-native-prop-types'.", -- ); -+ return require("deprecated-react-native-prop-types").ColorPropType; - }, - get EdgeInsetsPropType(): $FlowFixMe { -- invariant( -- false, -- 'EdgeInsetsPropType has been removed from React Native. Migrate to ' + -- "EdgeInsetsPropType exported from 'deprecated-react-native-prop-types'.", -- ); -+ return require("deprecated-react-native-prop-types").EdgeInsetsPropType; - }, - get PointPropType(): $FlowFixMe { -- invariant( -- false, -- 'PointPropType has been removed from React Native. Migrate to ' + -- "PointPropType exported from 'deprecated-react-native-prop-types'.", -- ); -+ return require("deprecated-react-native-prop-types").PointPropType; - }, - get ViewPropTypes(): $FlowFixMe { -- invariant( -- false, -- 'ViewPropTypes has been removed from React Native. Migrate to ' + -- "ViewPropTypes exported from 'deprecated-react-native-prop-types'.", -- ); -+ return require("deprecated-react-native-prop-types").ViewPropTypes; - }, - }; - diff --git a/node_modules/react-native/scripts/react-native-xcode.back.sh b/node_modules/react-native/scripts/react-native-xcode.back.sh new file mode 100755 -index 0000000..927ec76 +index 0000000..e6fc8d1 --- /dev/null +++ b/node_modules/react-native/scripts/react-native-xcode.back.sh -@@ -0,0 +1,182 @@ +@@ -0,0 +1,192 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# @@ -175,7 +83,7 @@ index 0000000..927ec76 +DEST=$CONFIGURATION_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH + +# Enables iOS devices to get the IP address of the machine running Metro -+if [[ "$CONFIGURATION" = *Debug* && ! "$PLATFORM_NAME" == *simulator ]]; then ++if [[ ! "$SKIP_BUNDLING_METRO_IP" && "$CONFIGURATION" = *Debug* && ! "$PLATFORM_NAME" == *simulator ]]; then + for num in 0 1 2 3 4 5 6 7 8; do + IP=$(ipconfig getifaddr en${num}) + if [ ! -z "$IP" ]; then @@ -220,9 +128,8 @@ index 0000000..927ec76 + +# Path to react-native folder inside node_modules +REACT_NATIVE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -+# The project should be located next to where react-native is installed -+# in node_modules. -+PROJECT_ROOT=${PROJECT_ROOT:-"$REACT_NATIVE_DIR/../.."} ++# Most projects have their project root, one level up from their Xcode project dir (the "ios" directory) ++PROJECT_ROOT=${PROJECT_ROOT:-"$PROJECT_DIR/.."} + +cd "$PROJECT_ROOT" || exit + @@ -240,13 +147,24 @@ index 0000000..927ec76 +# shellcheck source=/dev/null +source "$REACT_NATIVE_DIR/scripts/node-binary.sh" + ++# If hermes-engine is in the Podfile.lock, it means that Hermes is a dependency of the project ++# and it is enabled. If not, it means that hermes is disabled. ++HERMES_ENABLED=$(grep hermes-engine $PODS_PODFILE_DIR_PATH/Podfile.lock) ++ ++# If hermes-engine is not in the Podfile.lock, it means that the app is not using Hermes. ++# Setting USE_HERMES is no the only way to set whether the app can use hermes or not: users ++# can also modify manually the Podfile. ++if [[ -z "$HERMES_ENABLED" ]]; then ++ USE_HERMES=false ++fi ++ +HERMES_ENGINE_PATH="$PODS_ROOT/hermes-engine" +[ -z "$HERMES_CLI_PATH" ] && HERMES_CLI_PATH="$HERMES_ENGINE_PATH/destroot/bin/hermesc" + +# Hermes is enabled in new projects by default, so we cannot assume that USE_HERMES=1 is set as an envvar. +# If hermes-engine is found in Pods, we can assume Hermes has not been disabled. +# If hermesc is not available and USE_HERMES is either unset or true, show error. -+if [[ -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then ++if [[ ! -z "$HERMES_ENABLED" && -f "$HERMES_ENGINE_PATH" && ! -f "$HERMES_CLI_PATH" ]]; then + echo "error: Hermes is enabled but the hermesc binary could not be found at ${HERMES_CLI_PATH}." \ + "Perhaps you need to run 'bundle exec pod install' or otherwise " \ + "point the HERMES_CLI_PATH variable to your custom location." >&2 @@ -328,7 +246,7 @@ index 0000000..927ec76 + if [[ $EMIT_SOURCEMAP == true ]]; then + EXTRA_COMPILER_ARGS="$EXTRA_COMPILER_ARGS -output-source-map" + fi -+ "$HERMES_CLI_PATH" -emit-binary $EXTRA_COMPILER_ARGS -out "$DEST/main.jsbundle" "$BUNDLE_FILE" ++ "$HERMES_CLI_PATH" -emit-binary -max-diagnostic-width=80 $EXTRA_COMPILER_ARGS -out "$DEST/main.jsbundle" "$BUNDLE_FILE" + if [[ $EMIT_SOURCEMAP == true ]]; then + HBC_SOURCEMAP_FILE="$DEST/main.jsbundle.map" + "$NODE_BINARY" "$COMPOSE_SOURCEMAP_PATH" "$PACKAGER_SOURCEMAP_FILE" "$HBC_SOURCEMAP_FILE" -o "$SOURCEMAP_FILE" @@ -343,10 +261,10 @@ index 0000000..927ec76 + exit 2 +fi diff --git a/node_modules/react-native/scripts/react-native-xcode.sh b/node_modules/react-native/scripts/react-native-xcode.sh -index 927ec76..477fc27 100755 +index e6fc8d1..15ae060 100755 --- a/node_modules/react-native/scripts/react-native-xcode.sh +++ b/node_modules/react-native/scripts/react-native-xcode.sh -@@ -149,6 +149,7 @@ fi +@@ -159,6 +159,7 @@ fi --dev $DEV \ --reset-cache \ --bundle-output "$BUNDLE_FILE" \ @@ -354,3 +272,7 @@ index 927ec76..477fc27 100755 --assets-dest "$DEST" \ $EXTRA_ARGS \ $EXTRA_PACKAGER_ARGS +diff --git a/node_modules/react-native/types/.DS_Store b/node_modules/react-native/types/.DS_Store +new file mode 100644 +index 0000000..b6e1b29 +Binary files /dev/null and b/node_modules/react-native/types/.DS_Store differ diff --git a/patches/react-native-image-pan-zoom+2.1.12.patch b/patches/react-native-image-pan-zoom+2.1.12.patch new file mode 100644 index 00000000000..c17cc1660eb --- /dev/null +++ b/patches/react-native-image-pan-zoom+2.1.12.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/react-native-image-pan-zoom/built/image-zoom/image-zoom.component.d.ts b/node_modules/react-native-image-pan-zoom/built/image-zoom/image-zoom.component.d.ts +index 3f1724b..b033d5e 100644 +--- a/node_modules/react-native-image-pan-zoom/built/image-zoom/image-zoom.component.d.ts ++++ b/node_modules/react-native-image-pan-zoom/built/image-zoom/image-zoom.component.d.ts +@@ -1,7 +1,7 @@ + import * as React from 'react'; + import { LayoutChangeEvent } from 'react-native'; + import { ICenterOn, ImageZoomProps, ImageZoomState } from './image-zoom.type'; +-export default class ImageViewer extends React.Component { ++export default class ImageViewer extends React.Component, ImageZoomState> { + static defaultProps: ImageZoomProps; + state: ImageZoomState; + private lastPositionX; diff --git a/patches/react-native-pdf+6.4.0.patch b/patches/react-native-pdf+6.7.4.patch similarity index 78% rename from patches/react-native-pdf+6.4.0.patch rename to patches/react-native-pdf+6.7.4.patch index c6c9fc0c29b..9c63a043f06 100644 --- a/patches/react-native-pdf+6.4.0.patch +++ b/patches/react-native-pdf+6.7.4.patch @@ -1,8 +1,8 @@ -diff --git a/node_modules/react-native-pdf/ios/RCTPdf/RCTPdfView.m b/node_modules/react-native-pdf/ios/RCTPdf/RCTPdfView.m -index 52aafd8..5efda0b 100644 ---- a/node_modules/react-native-pdf/ios/RCTPdf/RCTPdfView.m -+++ b/node_modules/react-native-pdf/ios/RCTPdf/RCTPdfView.m -@@ -188,12 +188,16 @@ const float MIN_SCALE = 1.0f; +diff --git a/node_modules/react-native-pdf/ios/RNPDFPdf/RNPDFPdfView.mm b/node_modules/react-native-pdf/ios/RNPDFPdf/RNPDFPdfView.mm +index 1e8f5c6..ee3aa74 100644 +--- a/node_modules/react-native-pdf/ios/RNPDFPdf/RNPDFPdfView.mm ++++ b/node_modules/react-native-pdf/ios/RNPDFPdf/RNPDFPdfView.mm +@@ -374,12 +374,16 @@ using namespace facebook::react; } if (_pdfDocument && ([changedProps containsObject:@"path"] || [changedProps containsObject:@"enableAnnotationRendering"])) { diff --git a/patches/react-native-reanimated+2.10.0.patch b/patches/react-native-reanimated+2.10.0.patch_old similarity index 100% rename from patches/react-native-reanimated+2.10.0.patch rename to patches/react-native-reanimated+2.10.0.patch_old diff --git a/patches/react-native-reanimated+3.12.0.patch b/patches/react-native-reanimated+3.12.0.patch new file mode 100644 index 00000000000..26aa51ba482 --- /dev/null +++ b/patches/react-native-reanimated+3.12.0.patch @@ -0,0 +1,40 @@ +diff --git a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactHost/72/com/swmansion/reanimated/DevMenuUtils.java b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactHost/72/com/swmansion/reanimated/DevMenuUtils.java +index d39c8d6..86ac2e7 100644 +--- a/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactHost/72/com/swmansion/reanimated/DevMenuUtils.java ++++ b/node_modules/react-native-reanimated/android/src/reactNativeVersionPatch/ReactHost/72/com/swmansion/reanimated/DevMenuUtils.java +@@ -1,18 +1,23 @@ + package com.swmansion.reanimated; + ++import com.facebook.react.bridge.ReactApplicationContext; ++import com.facebook.react.ReactApplication; ++import com.facebook.react.devsupport.interfaces.DevOptionHandler; ++import com.facebook.react.devsupport.interfaces.DevSupportManager; ++ + public class DevMenuUtils { + +- private void addDevMenuOption(ReactApplicationContext context, DevOptionHandler handler) { +- // In Expo, `ApplicationContext` is not an instance of `ReactApplication` +- if (context.getApplicationContext() instanceof ReactApplication) { +- final DevSupportManager devSupportManager = +- ((ReactApplication) context.getApplicationContext()) +- .getReactNativeHost() +- .getReactInstanceManager() +- .getDevSupportManager(); ++ public static void addDevMenuOption(ReactApplicationContext context, DevOptionHandler handler) { ++ // In Expo, `ApplicationContext` is not an instance of `ReactApplication` ++ if (context.getApplicationContext() instanceof ReactApplication) { ++ final DevSupportManager devSupportManager = ++ ((ReactApplication) context.getApplicationContext()) ++ .getReactNativeHost() ++ .getReactInstanceManager() ++ .getDevSupportManager(); + +- devSupportManager.addCustomDevOption( +- "Toggle slow animations (Reanimated)", handler); ++ devSupportManager.addCustomDevOption( ++ "Toggle slow animations (Reanimated)", handler); ++ } + } +- } + } +\ No newline at end of file diff --git a/patches/react-native-vision-camera+2.15.4.patch b/patches/react-native-vision-camera+2.15.4.patch_old similarity index 100% rename from patches/react-native-vision-camera+2.15.4.patch rename to patches/react-native-vision-camera+2.15.4.patch_old diff --git a/publiccode.yml b/publiccode.yml index 0de6295f53f..a11d646bc06 100644 --- a/publiccode.yml +++ b/publiccode.yml @@ -5,11 +5,11 @@ publiccodeYmlVersion: '0.2' name: IO logo: "img/app-logo.svg" -releaseDate: '2024-07-01' +releaseDate: '2024-07-11' url: 'https://github.com/pagopa/io-app' applicationSuite: IO landingURL: 'https://io.italia.it/' -softwareVersion: 2.63.0-rc.5 +softwareVersion: 2.64.0-rc.1 developmentStatus: beta softwareType: standalone/mobile roadmap: 'https://io.italia.it/' diff --git a/scripts/remove-unused-locales.ts b/scripts/remove-unused-locales.ts index 00f619b075a..bdba5e33ece 100644 --- a/scripts/remove-unused-locales.ts +++ b/scripts/remove-unused-locales.ts @@ -57,7 +57,7 @@ const installYqIfNeeded = async () => { }; /** -* since some locales are accessed with a notation like +* since some locales are accessed with a notation like * `key.key.key.${myVal}`, * so we check for all possible combinations of the locale. * diff --git a/scripts/ts/checkOutdatedDependencies/types/GroupBySeverity.ts b/scripts/ts/checkOutdatedDependencies/types/GroupBySeverity.ts index e1b7126f1d1..bb274394dd5 100644 --- a/scripts/ts/checkOutdatedDependencies/types/GroupBySeverity.ts +++ b/scripts/ts/checkOutdatedDependencies/types/GroupBySeverity.ts @@ -1,5 +1,5 @@ const keyOfGroupBySeverity = ["major", "minor", "patch", "unknown"] as const; -type KeyOfGroupBySeverity = typeof keyOfGroupBySeverity[number]; +type KeyOfGroupBySeverity = (typeof keyOfGroupBySeverity)[number]; /** * Group the outdated library, grouped by severity diff --git a/scripts/ts/checkOutdatedDependencies/types/GroupByType.ts b/scripts/ts/checkOutdatedDependencies/types/GroupByType.ts index b3344a93abe..cc59dfe4cba 100644 --- a/scripts/ts/checkOutdatedDependencies/types/GroupByType.ts +++ b/scripts/ts/checkOutdatedDependencies/types/GroupByType.ts @@ -8,7 +8,7 @@ const keyOfGroupByType = [ "resolutionDependencies", "others" ] as const; -type KeyGroupByType = typeof keyOfGroupByType[number]; +type KeyGroupByType = (typeof keyOfGroupByType)[number]; /** * Represents the grouping of outdated dependencies by type diff --git a/scripts/ts/common/slack/postMessage.ts b/scripts/ts/common/slack/postMessage.ts index 9cf87d25dcd..52df257dffc 100644 --- a/scripts/ts/common/slack/postMessage.ts +++ b/scripts/ts/common/slack/postMessage.ts @@ -1,3 +1,4 @@ +import { URL } from "url"; import fetch from "node-fetch"; const endpoint = "https://slack.com/api/chat.postMessage"; diff --git a/ts/App.tsx b/ts/App.tsx index af5a6b91fbe..192314c7eec 100644 --- a/ts/App.tsx +++ b/ts/App.tsx @@ -6,7 +6,6 @@ import { } from "@pagopa/io-app-design-system"; import * as React from "react"; import { GestureHandlerRootView } from "react-native-gesture-handler"; -import { MenuProvider } from "react-native-popup-menu"; import { SafeAreaProvider } from "react-native-safe-area-context"; import { Provider } from "react-redux"; import { PersistGate } from "redux-persist/integration/react"; @@ -22,7 +21,7 @@ export type AppDispatch = typeof store.dispatch; * Main component of the application * @constructor */ -export const App: React.FunctionComponent = () => ( +export const App = (): JSX.Element => ( @@ -32,9 +31,7 @@ export const App: React.FunctionComponent = () => ( - - - + diff --git a/ts/api/backend.ts b/ts/api/backend.ts index aa8259716c9..714d058f72d 100644 --- a/ts/api/backend.ts +++ b/ts/api/backend.ts @@ -39,11 +39,7 @@ import { GetUserDataProcessingT, getUserMessageDefaultDecoder, getUserMessagesDefaultDecoder, - getUserMetadataDefaultDecoder, - GetUserMetadataT, GetUserProfileT, - getVisibleServicesDefaultDecoder, - GetVisibleServicesT, StartEmailValidationProcessT, updateProfileDefaultDecoder, UpdateProfileT, @@ -51,8 +47,6 @@ import { UpsertServicePreferencesT, upsertUserDataProcessingDefaultDecoder, UpsertUserDataProcessingT, - upsertUserMetadataDefaultDecoder, - UpsertUserMetadataT, upsertMessageStatusAttributesDefaultDecoder, UpsertMessageStatusAttributesT, getUserProfileDefaultDecoder, @@ -136,7 +130,6 @@ export type LogoutT = IPostApiRequestType< // Create client // -// eslint-disable-next-line export function BackendClient( baseUrl: string, token: SessionToken, @@ -181,14 +174,6 @@ export function BackendClient( response_decoder: upsertServicePreferencesDefaultDecoder() }; - const getVisibleServicesT: GetVisibleServicesT = { - method: "get", - url: () => "/api/v1/services", - query: _ => ({}), - headers: tokenHeaderProducer, - response_decoder: getVisibleServicesDefaultDecoder() - }; - const getMessagesT: GetUserMessagesT = { method: "get", url: _ => "/api/v1/messages", @@ -273,14 +258,6 @@ export function BackendClient( response_decoder: updateProfileDefaultDecoder() }; - const getUserMetadataT: GetUserMetadataT = { - method: "get", - url: () => "/api/v1/user-metadata", - query: _ => ({}), - headers: tokenHeaderProducer, - response_decoder: getUserMetadataDefaultDecoder() - }; - const postStartEmailValidationProcessT: StartEmailValidationProcessT = { method: "post", url: () => "/api/v1/email-validation-process", @@ -290,15 +267,6 @@ export function BackendClient( response_decoder: startEmailValidationProcessDefaultDecoder() }; - const createOrUpdateUserMetadataT: UpsertUserMetadataT = { - method: "post", - url: () => "/api/v1/user-metadata", - query: _ => ({}), - headers: composeHeaderProducers(tokenHeaderProducer, ApiHeaderJson), - body: p => JSON.stringify(p.body), - response_decoder: upsertUserMetadataDefaultDecoder() - }; - const getUserDataProcessingT: GetUserDataProcessingT = { method: "get", url: ({ choice }) => `/api/v1/user-data-processing/${choice}`, @@ -385,9 +353,6 @@ export function BackendClient( upsertServicePreference: withBearerToken( createFetchRequestForApi(upsertServicePreferenceT, options) ), - getVisibleServices: withBearerToken( - createFetchRequestForApi(getVisibleServicesT, options) - ), getMessages: withBearerToken( createFetchRequestForApi(getMessagesT, options) ), @@ -413,12 +378,6 @@ export function BackendClient( createOrUpdateProfile: withBearerToken( createFetchRequestForApi(createOrUpdateProfileT, options) ), - getUserMetadata: withBearerToken( - createFetchRequestForApi(getUserMetadataT, options) - ), - createOrUpdateUserMetadata: withBearerToken( - createFetchRequestForApi(createOrUpdateUserMetadataT, options) - ), createOrUpdateInstallation: withBearerToken( createFetchRequestForApi(createOrUpdateInstallationT, options) ), diff --git a/ts/api/pagopa.ts b/ts/api/pagopa.ts index 8b5323fb45b..5d5ce2623b5 100644 --- a/ts/api/pagopa.ts +++ b/ts/api/pagopa.ts @@ -124,7 +124,6 @@ export type GetTransactionsUsingGETT = r.IGetApiRequestType< { readonly Bearer: string; readonly start: number }, "Authorization", never, - // eslint-disable-next-line | r.IResponseType<200, TransactionListResponse> | r.IResponseType<401, undefined> | r.IResponseType<403, undefined> diff --git a/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap b/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap index 57278305868..61e306dae5e 100644 --- a/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap +++ b/ts/boot/__tests__/__snapshots__/persistedStore.test.ts.snap @@ -1,25 +1,25 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'authentication' state 1`] = ` -Object { +{ "kind": "LoggedOutWithoutIdp", "reason": "NOT_LOGGED_IN", } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'content' state 1`] = ` -Object { - "contextualHelp": Object { +{ + "contextualHelp": { "kind": "PotNone", }, - "idps": Object { + "idps": { "kind": "undefined", }, - "municipality": Object { - "codiceCatastale": Object { + "municipality": { + "codiceCatastale": { "kind": "PotNone", }, - "data": Object { + "data": { "kind": "PotNone", }, }, @@ -27,108 +27,52 @@ Object { `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'crossSessions' state 1`] = ` -Object { +{ "hashedFiscalCode": undefined, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'debug' state 1`] = ` -Object { +{ "isDebugModeEnabled": false, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'entities' state 1`] = ` -Object { - "calendarEvents": Object { - "byMessageId": Object {}, +{ + "calendarEvents": { + "byMessageId": {}, }, - "messages": Object { - "allPaginated": Object { - "archive": Object { - "data": Object { - "kind": "PotNone", - }, - "lastRequest": Object { - "_tag": "None", - }, - }, - "inbox": Object { - "data": Object { - "kind": "PotNone", - }, - "lastRequest": Object { - "_tag": "None", - }, - }, - "migration": Object { - "_tag": "None", - }, - "shownCategory": "INBOX", - }, - "detailsById": Object {}, - "downloads": Object {}, - "messageGetStatus": Object { - "status": "idle", - }, - "messagePrecondition": Object { - "content": Object { - "kind": "undefined", - }, - "messageId": Object { - "_tag": "None", - }, - }, - "paginatedById": Object {}, - "payments": Object { - "userSelectedPayments": Set {}, - }, - "thirdPartyById": Object {}, - }, - "messagesStatus": Object {}, - "organizations": Object { - "all": Array [], - "nameByFiscalCode": Object {}, - }, - "paymentByRptId": Object {}, - "services": Object { - "byId": Object {}, - "byOrgFiscalCode": Object {}, - "firstLoading": Object { - "isFirstServicesLoadingCompleted": false, - }, - "readState": Object {}, - "servicePreference": Object { - "kind": "PotNone", - }, - "visible": Object { - "kind": "PotNone", - }, + "messagesStatus": {}, + "organizations": { + "all": [], + "nameByFiscalCode": {}, }, + "paymentByRptId": {}, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'identification' state 1`] = ` -Object { +{ "fail": undefined, - "progress": Object { + "progress": { "kind": "unidentified", }, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'installation' state 1`] = ` -Object { - "appVersionHistory": Array [], +{ + "appVersionHistory": [], "isFirstRunAfterInstall": true, } `; -exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'installation.appVersionHistory' state 1`] = `Array []`; +exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'installation.appVersionHistory' state 1`] = `[]`; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'notifications' state 1`] = ` -Object { - "installation": Object { +{ + "installation": { "id": "fakeInstallationId", "registeredToken": undefined, "token": undefined, @@ -138,26 +82,26 @@ Object { `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'onboarding' state 1`] = ` -Object { +{ "isFingerprintAcknowledged": false, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'payments' state 1`] = ` -Object { - "creditCardInsertion": Array [], - "current": Object { +{ + "creditCardInsertion": [], + "current": { "kind": "UNSTARTED", }, - "history": Array [], + "history": [], "lastDeleted": null, } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'persistedPreferences' state 1`] = ` -Object { +{ "continueWithRootOrJailbreak": false, - "isCustomEmailChannelEnabled": Object { + "isCustomEmailChannelEnabled": { "kind": "PotNone", }, "isDesignSystemEnabled": false, @@ -176,19 +120,13 @@ Object { `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'profile' state 1`] = ` -Object { - "kind": "PotNone", -} -`; - -exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'userMetadata' state 1`] = ` -Object { +{ "kind": "PotNone", } `; exports[`Check the addition for new fields to the persisted store. If one of this test fails, check that exists the migration before updating the snapshot! Freeze 'wallet.wallets.walletById' state 1`] = ` -Object { +{ "kind": "PotNone", } `; diff --git a/ts/boot/__tests__/persistedStore.test.ts b/ts/boot/__tests__/persistedStore.test.ts index ad2db917a6e..0ee2af66af5 100644 --- a/ts/boot/__tests__/persistedStore.test.ts +++ b/ts/boot/__tests__/persistedStore.test.ts @@ -1,3 +1,4 @@ +import _ from "lodash"; import { applicationChangeState } from "../../store/actions/application"; import { appReducer } from "../../store/reducers"; import { GlobalState } from "../../store/reducers/types"; @@ -36,14 +37,12 @@ describe("Check the addition for new fields to the persisted store. If one of th it("Freeze 'content' state", () => { expect(globalState.content).toMatchSnapshot(); }); - it("Freeze 'userMetadata' state", () => { - expect(globalState.userMetadata).toMatchSnapshot(); - }); it("Freeze 'crossSessions' state", () => { expect(globalState.crossSessions).toMatchSnapshot(); }); it("Freeze 'entities' state", () => { - expect(globalState.entities).toMatchSnapshot(); + const entitiesWithoutMessages = _.omit(globalState.entities, "messages"); + expect(entitiesWithoutMessages).toMatchSnapshot(); }); it("Freeze 'authentication' state", () => { expect(globalState.authentication).toMatchSnapshot(); diff --git a/ts/boot/configureStoreAndPersistor.ts b/ts/boot/configureStoreAndPersistor.ts index 588dd63430f..8f6b6d72e0d 100644 --- a/ts/boot/configureStoreAndPersistor.ts +++ b/ts/boot/configureStoreAndPersistor.ts @@ -1,7 +1,7 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import AsyncStorage from "@react-native-async-storage/async-storage"; import * as O from "fp-ts/lib/Option"; -import _, { merge } from "lodash"; +import _, { merge, omit } from "lodash"; import { applyMiddleware, compose, @@ -53,7 +53,7 @@ import { configureReactotron } from "./configureRectotron"; /** * Redux persist will migrate the store to the current version */ -const CURRENT_REDUX_STORE_VERSION = 30; +const CURRENT_REDUX_STORE_VERSION = 31; // see redux-persist documentation: // https://github.com/rt2zz/redux-persist/blob/master/docs/migrations.md @@ -158,16 +158,7 @@ const migrations: MigrationManifest = { // Version 7 // we empty the services list to get both services list and services metadata being reloaded and persisted - "7": (state: PersistedState) => ({ - ...state, - entities: { - ...(state as PersistedGlobalState).entities, - services: { - ...(state as PersistedGlobalState).entities.services, - byId: {} - } - } - }), + "7": (state: PersistedState) => _.set(state, "entities.services.byId", {}), // Version 8 // we load services scope in an specific view. So now it is uselss to hold (old) services metadata @@ -424,7 +415,10 @@ const migrations: MigrationManifest = { persistedPreferences: { isNewHomeSectionEnabled: false } - }) + }), + // version 31 + // remove userMetadata from persisted state + "31": (state: PersistedState) => omit(state, "userMetadata") }; const isDebuggingInChrome = isDevEnv && !!window.navigator.userAgent; @@ -447,7 +441,6 @@ const rootPersistConfig: PersistConfig = { "installation", "payments", "content", - "userMetadata", "crossSessions" ], // Transform functions used to manipulate state on store/rehydrate diff --git a/ts/components/AppVersion.tsx b/ts/components/AppVersion.tsx index dc9c5c0819d..3141ef42813 100644 --- a/ts/components/AppVersion.tsx +++ b/ts/components/AppVersion.tsx @@ -1,4 +1,3 @@ -import I18n from "i18n-js"; import * as React from "react"; import { GestureResponderEvent, @@ -6,6 +5,7 @@ import { StyleSheet, View } from "react-native"; +import I18n from "../i18n"; import { WithTestID } from "../types/WithTestID"; import { getAppVersion } from "../utils/appVersion"; import { LabelSmall } from "./core/typography/LabelSmall"; diff --git a/ts/components/BulletList.tsx b/ts/components/BulletList.tsx index 9fd6f34dbd5..93788ceae0b 100644 --- a/ts/components/BulletList.tsx +++ b/ts/components/BulletList.tsx @@ -1,14 +1,24 @@ import { View } from "react-native"; import React, { ComponentProps, memo, useCallback } from "react"; -import { Body, IOSpacer, VSpacer } from "@pagopa/io-app-design-system"; +import { + Body, + HSpacer, + IOSpacer, + IOStyles, + VSpacer +} from "@pagopa/io-app-design-system"; +import { + BodyProps, + ComposedBodyFromArray +} from "./core/typography/ComposedBodyFromArray"; const BULLET_ITEM = "\u2022"; -type ListItem = { +export type BulletListItem = { /** * The text of the list item. */ - value: string; + value: string | Array; /** * The id used as key, it must be unique. */ @@ -20,7 +30,7 @@ type ListItem = { /** * Nested list. */ - list?: Array>; + list?: Array>; }; type Props = { @@ -31,7 +41,7 @@ type Props = { /** * The list to display bullet items. */ - list: Array; + list: Array; /** * Title extra props. */ @@ -43,10 +53,10 @@ type Props = { }; /** - * This component renders a bullet list. It supports two levels of nesting, so the first level `ListItem` can contain a `list` prop. + * This component renders a bullet list. It supports two levels of nesting, so the first level `BulletListItem` can contain a `list` prop. * * @param {string} title The bullet list title. - * @param {Array} list The array used to render the bullet list. + * @param {Array} list The array used to render the bullet list. * @param {ComponentProps} [titleProps] Used to customize title. * @param {IOSpacer} [spacing] Used to define list item indentation and space between title and list. * @returns {JSX.Element} The rendered component. @@ -71,16 +81,23 @@ type Props = { export const BulletList = memo( ({ title, list, spacing = 8, titleProps = {} }: Props) => { /** - * @param {Array} [list] The list to iterate. + * @param {Array} [list] The list to iterate. * @param {number} [count=0] The number a recursive calls, used to stop the cycle when nesting level is equal to two. * @returns {JSX.Element} The rendered list. */ const renderListItems = useCallback( - (list?: Array, count: number = 0) => + (list?: Array, count: number = 0) => list?.map(({ id, value, textProps = {}, ...rest }) => ( - - - {BULLET_ITEM} {value} + + + {BULLET_ITEM} + + + {Array.isArray(value) ? ( + + ) : ( + value + )} {"list" in rest && count === 0 && @@ -94,7 +111,9 @@ export const BulletList = memo( {title} - {renderListItems(list)} + + {renderListItems(list)} + ); } diff --git a/ts/components/ContextualInfo.tsx b/ts/components/ContextualInfo.tsx index 2880c5557a4..bdb802e9a60 100644 --- a/ts/components/ContextualInfo.tsx +++ b/ts/components/ContextualInfo.tsx @@ -1,12 +1,12 @@ import * as React from "react"; import { BackHandler, NativeEventSubscription, View } from "react-native"; -import I18n from "i18n-js"; import { ContentWrapper, HeaderSecondLevel, IOColors, IOStyles } from "@pagopa/io-app-design-system"; +import I18n from "../i18n"; import { H1 } from "./core/typography/H1"; type Props = Readonly<{ diff --git a/ts/components/DebugPrettyPrint.tsx b/ts/components/DebugPrettyPrint.tsx index 106955af4bf..79e7c3f7590 100644 --- a/ts/components/DebugPrettyPrint.tsx +++ b/ts/components/DebugPrettyPrint.tsx @@ -1,4 +1,4 @@ -/* +/* WARNING: This component is not referenced anywhere, but is used for development purposes. for development purposes. Don't REMOVE it! */ diff --git a/ts/components/FooterTopShadow.tsx b/ts/components/FooterTopShadow.tsx index a32c1d30755..1eeeae0cbfb 100644 --- a/ts/components/FooterTopShadow.tsx +++ b/ts/components/FooterTopShadow.tsx @@ -17,7 +17,7 @@ const styles = StyleSheet.create({ * @param props * @constructor */ -export const FooterTopShadow: React.FunctionComponent = props => ( +export const FooterTopShadow = (props: React.PropsWithChildren) => ( {props.children} diff --git a/ts/components/ScreenHeader.tsx b/ts/components/ScreenHeader.tsx index 12d260946c4..cf81e93fe3c 100644 --- a/ts/components/ScreenHeader.tsx +++ b/ts/components/ScreenHeader.tsx @@ -65,7 +65,7 @@ const ScreenHeader = ({ rightComponent }: ScreenHeader) => { /* The following function doesn't seem very elegant, - but I inherited this logic. Considering that this + but I inherited this logic. Considering that this component may be replaced soon and that it affects a lot of pages, I give up on refactoring it. */ const getIcon = () => { diff --git a/ts/components/SectionStatus/__tests__/SectionStatusComponent.test.tsx b/ts/components/SectionStatus/__tests__/SectionStatusComponent.test.tsx index 24f1a353b33..ee24134bad8 100644 --- a/ts/components/SectionStatus/__tests__/SectionStatusComponent.test.tsx +++ b/ts/components/SectionStatus/__tests__/SectionStatusComponent.test.tsx @@ -41,6 +41,13 @@ const mockSectionStatusState = ( config: { assistanceTool: { tool: ToolEnum.none }, cgn: { enabled: true }, + newPaymentSection: { + enabled: false, + min_app_version: { + android: "0.0.0.0", + ios: "0.0.0.0" + } + }, fims: { enabled: true } } as Config } as BackendStatus) diff --git a/ts/components/SectionStatus/__tests__/__snapshots__/SectionStatusContent.test.tsx.snap b/ts/components/SectionStatus/__tests__/__snapshots__/SectionStatusContent.test.tsx.snap index e3eab340795..23c5fdc1a98 100644 --- a/ts/components/SectionStatus/__tests__/__snapshots__/SectionStatusContent.test.tsx.snap +++ b/ts/components/SectionStatus/__tests__/__snapshots__/SectionStatusContent.test.tsx.snap @@ -6,8 +6,8 @@ exports[`StatusContent should match the snapshot 1`] = ` accessibilityRole="link" accessible={true} style={ - Array [ - Object { + [ + { "alignContent": "center", "alignItems": "flex-start", "flexDirection": "row", @@ -17,7 +17,7 @@ exports[`StatusContent should match the snapshot 1`] = ` "paddingTop": 8, "width": "100%", }, - Object { + { "backgroundColor": "#00C5CA", }, ] @@ -26,7 +26,7 @@ exports[`StatusContent should match the snapshot 1`] = ` > ", } } style={ - Object { + { "flex": 1, } } diff --git a/ts/components/__tests__/__snapshots__/WebviewComponent.test.tsx.snap b/ts/components/__tests__/__snapshots__/WebviewComponent.test.tsx.snap index 89464d64bd6..528581f7d2c 100644 --- a/ts/components/__tests__/__snapshots__/WebviewComponent.test.tsx.snap +++ b/ts/components/__tests__/__snapshots__/WebviewComponent.test.tsx.snap @@ -3,7 +3,7 @@ exports[`WebviewComponent tests snapshot for component 1`] = ` } entering={enteringAnimation} exiting={exitingAnimation} > diff --git a/ts/components/box/InfoBox.tsx b/ts/components/box/InfoBox.tsx index d1ebf45fdaa..0826bba78eb 100644 --- a/ts/components/box/InfoBox.tsx +++ b/ts/components/box/InfoBox.tsx @@ -39,7 +39,7 @@ const ICON_SIZE: IOIconSizeScale = 32; * @param props * @constructor */ -export const InfoBox: React.FunctionComponent = props => { +export const InfoBox = (props: React.PropsWithChildren) => { const iconName = props.iconName ?? "notice"; const iconColor = props.iconColor ?? "blue"; const iconSize = props.iconSize ?? ICON_SIZE; diff --git a/ts/components/cie/CieCardReadingAnimation.tsx b/ts/components/cie/CieCardReadingAnimation.tsx index b3981a34a01..dd9eb5e1b9e 100644 --- a/ts/components/cie/CieCardReadingAnimation.tsx +++ b/ts/components/cie/CieCardReadingAnimation.tsx @@ -55,7 +55,7 @@ export default class CieCardReadingAnimation extends React.PureComponent< this.state = { progressBarValue: 0 }; - // eslint-disable-next-line + this.progressAnimatedValue = new Animated.Value(0); this.createAnimation(); } diff --git a/ts/components/core/__test__/fontsAndroid.test.ts b/ts/components/core/__test__/fontsAndroid.test.ts deleted file mode 100644 index 9b51f066b5e..00000000000 --- a/ts/components/core/__test__/fontsAndroid.test.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Platform } from "react-native"; -import { fontWeightsMocks, italics } from "../__mock__/fontMocks"; -import { makeFontStyleObject } from "../fonts"; - -jest.mock("react-native", () => ({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Platform: { OS: "android", select: obj => obj.android } -})); - -describe("makeFontStyleObject behaviour on Android", () => { - it("Platform set correctly to Android", () => { - expect(Platform.OS).toBe("android"); - }); - it("fontWeight and fontStyle should be undefined", () => { - const font = makeFontStyleObject(); - expect(font.fontStyle).toBeUndefined(); - expect(font.fontWeight).toBeUndefined(); - }); - it("default font family should be Titillium if specify the weight", () => { - const font = makeFontStyleObject("Bold"); - expect(font.fontFamily).toBe("TitilliumSansPro-Bold"); - expect(font.fontStyle).toBeUndefined(); - expect(font.fontWeight).toBeUndefined(); - }); - it("TitilliumSansPro, all weight", () => { - italics.map(isItalic => - fontWeightsMocks.map(fw => - expect( - makeFontStyleObject(fw, isItalic, "TitilliumSansPro").fontFamily - ).toBe(`TitilliumSansPro-${fw}${isItalic ? "Italic" : ""}`) - ) - ); - }); - it("RobotoMono, all weight", () => { - italics.map(isItalic => - fontWeightsMocks.map(fw => - expect(makeFontStyleObject(fw, isItalic, "RobotoMono").fontFamily).toBe( - `RobotoMono-${fw}${isItalic ? "Italic" : ""}` - ) - ) - ); - }); -}); diff --git a/ts/components/core/selection/__test__/__snapshots__/RemoteSwitch.test.tsx.snap b/ts/components/core/selection/__test__/__snapshots__/RemoteSwitch.test.tsx.snap index 02e69c1c723..57d8a8c1d07 100644 --- a/ts/components/core/selection/__test__/__snapshots__/RemoteSwitch.test.tsx.snap +++ b/ts/components/core/selection/__test__/__snapshots__/RemoteSwitch.test.tsx.snap @@ -3,7 +3,7 @@ exports[`RemoteSwitch tests Snapshot for pot.none 1`] = ` { config: { assistanceTool: { tool }, cgn: { enabled: true }, + newPaymentSection: { + enabled: false, + min_app_version: { + android: "0.0.0.0", + ios: "0.0.0.0" + } + }, fims: { enabled: true } } as Config } as BackendStatus) @@ -74,6 +81,13 @@ describe("BaseScreenComponent", () => { config: { assistanceTool: { tool: ToolEnum.zendesk }, cgn: { enabled: true }, + newPaymentSection: { + enabled: false, + min_app_version: { + android: "0.0.0.0", + ios: "0.0.0.0" + } + }, fims: { enabled: true } } as Config } as BackendStatus) diff --git a/ts/components/screens/BaseScreenComponent/index.tsx b/ts/components/screens/BaseScreenComponent/index.tsx index 3e017bec197..56cd1c27600 100644 --- a/ts/components/screens/BaseScreenComponent/index.tsx +++ b/ts/components/screens/BaseScreenComponent/index.tsx @@ -17,7 +17,7 @@ import { AccessibilityEvents, BaseHeader } from "../BaseHeader"; export type ContextualHelpProps = { title: string; - body: () => React.ReactNode; + body: React.ReactNode; }; export type ContextualHelpPropsMarkdown = { diff --git a/ts/components/screens/BaseScreenComponent/utils.tsx b/ts/components/screens/BaseScreenComponent/utils.tsx index 27bd6d1d037..52178841440 100644 --- a/ts/components/screens/BaseScreenComponent/utils.tsx +++ b/ts/components/screens/BaseScreenComponent/utils.tsx @@ -49,7 +49,7 @@ export const getContextualHelpConfig = ( ? { body: contextualHelp.body, title: contextualHelp.title } : contextualHelpMarkdown ? { - body: () => ( + body: ( { +export default class DarkLayout extends React.Component< + React.PropsWithChildren +> { screenContent() { const wrapper = (children: React.ReactNode) => this.props.gradientHeader ? ( diff --git a/ts/components/screens/IdpCustomContextualHelpContent.tsx b/ts/components/screens/IdpCustomContextualHelpContent.tsx index f8f188d4311..95dba0d333b 100644 --- a/ts/components/screens/IdpCustomContextualHelpContent.tsx +++ b/ts/components/screens/IdpCustomContextualHelpContent.tsx @@ -1,10 +1,10 @@ -import I18n from "i18n-js"; import * as React from "react"; import { ButtonOutline, ButtonSolid, VSpacer } from "@pagopa/io-app-design-system"; +import I18n from "../../i18n"; import { Idp } from "../../../definitions/content/Idp"; import { handleItemOnPress } from "../../utils/url"; import LegacyMarkdown from "../ui/Markdown/LegacyMarkdown"; @@ -16,7 +16,7 @@ type Props = Readonly<{ const IdpCustomContextualHelpContent = (idpTextData: Idp) => ({ title: I18n.t("authentication.idp_login.contextualHelpTitle2"), - body: () => + body: }); const IdpCustomContextualHelpBody: React.FunctionComponent = props => { diff --git a/ts/components/screens/PinCreation/PinCreation.tsx b/ts/components/screens/PinCreation/PinCreation.tsx index 8090729b3ab..d46979daca2 100644 --- a/ts/components/screens/PinCreation/PinCreation.tsx +++ b/ts/components/screens/PinCreation/PinCreation.tsx @@ -150,7 +150,7 @@ export const PinCreation = ({ isOnboarding = false }: Props) => { /** * pinRef is used to avoid having to pass pin as a dependency of useCallback around `handlePinConfirmation`. */ - // eslint-disable-next-line functional/immutable-data + pinRef.current = v; scrollToConfirmation(); } else { diff --git a/ts/components/screens/ScreenContent.tsx b/ts/components/screens/ScreenContent.tsx index d69d60ff4a5..e2614345882 100644 --- a/ts/components/screens/ScreenContent.tsx +++ b/ts/components/screens/ScreenContent.tsx @@ -1,23 +1,26 @@ import * as React from "react"; import { ScrollView, StyleProp, ViewStyle } from "react-native"; import { IOStyles } from "@pagopa/io-app-design-system"; -import { ComponentProps } from "../../types/react"; import { ScreenContentHeader } from "./ScreenContentHeader"; interface OwnProps { hideHeader?: boolean; contentStyle?: StyleProp; bounces?: boolean; - contentRefreshControl?: ComponentProps["refreshControl"]; + contentRefreshControl?: React.ComponentProps< + typeof ScrollView + >["refreshControl"]; referenceToContentScreen?: React.RefObject; } -type Props = OwnProps & ComponentProps; +type Props = OwnProps & React.ComponentProps; /** * Wraps a BaseScreenComponent with a title and a subtitle */ -class ScreenContent extends React.PureComponent { +class ScreenContent extends React.PureComponent< + React.PropsWithChildren +> { public render() { const { title, diff --git a/ts/components/screens/SectionHeaderComponent.tsx b/ts/components/screens/SectionHeaderComponent.tsx deleted file mode 100644 index 7c8870d39c8..00000000000 --- a/ts/components/screens/SectionHeaderComponent.tsx +++ /dev/null @@ -1,87 +0,0 @@ -/** - * A component to render a custom section header - * TODO: use the same component for: - * - message list https://www.pivotaltracker.com/story/show/165716236 - * - service lists https://www.pivotaltracker.com/story/show/166792020 - */ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { - View, - AccessibilityRole, - StyleProp, - StyleSheet, - ViewStyle -} from "react-native"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import customVariables from "../../theme/variables"; -import { H2 } from "../core/typography/H2"; -import { IOStyles } from "../core/variables/IOStyles"; -import OrganizationLogo from "../services/OrganizationLogo"; -import { MultiImage } from "../ui/MultiImage"; - -type Props = Readonly<{ - sectionHeader: string; - style?: StyleProp; - logoUri?: React.ComponentProps["source"]; - rightItem?: React.ReactNode; - accessibilityRole?: AccessibilityRole; -}>; - -const styles = StyleSheet.create({ - withoutLogo: { - paddingTop: 19, - paddingBottom: 11, - alignItems: "center" - }, - withLogo: { - paddingTop: customVariables.spacerWidth, - paddingBottom: 0, - alignItems: "center" - }, - sectionView: { - backgroundColor: IOColors.white, - flexDirection: "row", - borderBottomColor: customVariables.itemSeparator, - borderBottomWidth: StyleSheet.hairlineWidth - } -}); - -export default class SectionHeaderComponent extends React.Component { - public render() { - const rightItem = pipe( - this.props.rightItem, - O.fromNullable, - O.getOrElseW(() => - pipe( - this.props.logoUri, - O.fromNullable, - O.map(uri => ), - O.toUndefined - ) - ) - ); - return ( - -

- {this.props.sectionHeader} -

- <> - {rightItem} - - - - ); - } -} diff --git a/ts/components/screens/TopScreenComponent.tsx b/ts/components/screens/TopScreenComponent.tsx index a93dbf521dc..8596b5822af 100644 --- a/ts/components/screens/TopScreenComponent.tsx +++ b/ts/components/screens/TopScreenComponent.tsx @@ -1,6 +1,5 @@ import * as React from "react"; import type { IOColors, IOIcons } from "@pagopa/io-app-design-system"; -import { ComponentProps } from "../../types/react"; import { FAQsCategoriesType } from "../../utils/faq"; import { AccessibilityEvents } from "./BaseHeader"; import BaseScreenComponent from "./BaseScreenComponent"; @@ -32,16 +31,19 @@ type BaseScreenComponentProps = | "hideSafeArea"; type Props = OwnProps & - Pick, BaseScreenComponentProps>; + Pick< + React.ComponentProps, + BaseScreenComponentProps + >; -export type TopScreenComponentProps = Props; +export type TopScreenComponentProps = React.PropsWithChildren; /** * It wraps a `BaseScreenComponent` with a title and an optional subtitle * @deprecated This component wraps the `BaseScreenComponent` component, which is marked as deprecated. * Please read the `BaseScreenComponent` deprecation note to understand how to replace it. */ -class TopScreenComponent extends React.PureComponent { +class TopScreenComponent extends React.PureComponent { public render() { const { dark, diff --git a/ts/components/screens/__tests__/__snapshots__/LoadingScreenContent.test.tsx.snap b/ts/components/screens/__tests__/__snapshots__/LoadingScreenContent.test.tsx.snap index 3a541e53200..4d6d1167d4c 100644 --- a/ts/components/screens/__tests__/__snapshots__/LoadingScreenContent.test.tsx.snap +++ b/ts/components/screens/__tests__/__snapshots__/LoadingScreenContent.test.tsx.snap @@ -3,7 +3,7 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, header hidden 1`] = ` - - - - + + @@ -590,7 +610,7 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea exports[`LoadingScreenContent should match the snapshot with title, a child, header shown 1`] = ` - - - - + + @@ -1182,7 +1217,7 @@ exports[`LoadingScreenContent should match the snapshot with title, a child, hea exports[`LoadingScreenContent should match the snapshot with title, no children, header hidden 1`] = ` - - - - + + @@ -1766,7 +1821,7 @@ exports[`LoadingScreenContent should match the snapshot with title, no children, exports[`LoadingScreenContent should match the snapshot with title, no children, header shown 1`] = ` - - - - + + diff --git a/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap b/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap index 556c4183178..cfbebbdd6e6 100644 --- a/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap +++ b/ts/components/screens/__tests__/__snapshots__/OperationResultScreenContent.test.tsx.snap @@ -3,7 +3,7 @@ exports[`OperationResultScreenContent should match the snapshot with default props 1`] = ` - - - - + + diff --git a/ts/components/screens/__tests__/__snapshots__/ScreenWithListItems.test.tsx.snap b/ts/components/screens/__tests__/__snapshots__/ScreenWithListItems.test.tsx.snap index 2b385ff72d9..9888c22ba56 100644 --- a/ts/components/screens/__tests__/__snapshots__/ScreenWithListItems.test.tsx.snap +++ b/ts/components/screens/__tests__/__snapshots__/ScreenWithListItems.test.tsx.snap @@ -3,7 +3,7 @@ exports[`ScreenWithListItems Rendering renders correctly with default props 1`] = ` - - - - + + @@ -956,7 +979,7 @@ exports[`ScreenWithListItems Rendering renders correctly with default props 1`] exports[`ScreenWithListItems Rendering renders correctly without optional props 1`] = ` - - - - + + @@ -1586,7 +1632,7 @@ exports[`ScreenWithListItems Rendering renders correctly without optional props exports[`ScreenWithListItems Rendering renders subtitle as array correctly 1`] = ` - - - - + + diff --git a/ts/components/search/SearchButton.tsx b/ts/components/search/SearchButton.tsx index dc5bb849189..90204e25411 100644 --- a/ts/components/search/SearchButton.tsx +++ b/ts/components/search/SearchButton.tsx @@ -9,7 +9,6 @@ import I18n from "../../i18n"; import { disableSearch, searchMessagesEnabled, - searchServicesEnabled, updateSearchText } from "../../store/actions/search"; import { Dispatch } from "../../store/actions/types"; @@ -18,7 +17,7 @@ import { ICON_BUTTON_MARGIN } from "../screens/BaseHeader"; export const MIN_CHARACTER_SEARCH_TEXT = 3; -export type SearchType = "Messages" | "Services"; +export type SearchType = "Messages"; interface OwnProps { color?: IOColors; @@ -129,9 +128,6 @@ const mapDispatchToProps = (dispatch: Dispatch, props: OwnProps) => ({ case "Messages": dispatch(searchMessagesEnabled(isSearchEnabled)); break; - case "Services": - dispatch(searchServicesEnabled(isSearchEnabled)); - break; } } }); diff --git a/ts/components/services/ContactPreferencesToggles/PreferenceToggleRow.tsx b/ts/components/services/ContactPreferencesToggles/PreferenceToggleRow.tsx deleted file mode 100644 index 87f3f8a8a19..00000000000 --- a/ts/components/services/ContactPreferencesToggles/PreferenceToggleRow.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { Icon, NativeSwitch } from "@pagopa/io-app-design-system"; -import { H4 } from "../../core/typography/H4"; -import { IOStyles } from "../../core/variables/IOStyles"; -import TouchableDefaultOpacity from "../../TouchableDefaultOpacity"; -import I18n from "../../../i18n"; -import { WithTestID } from "../../../types/WithTestID"; -import ActivityIndicator from "../../ui/ActivityIndicator"; - -type Props = WithTestID<{ - label: string; - onPress: (value: boolean) => void; - value: boolean; - graphicalState: "loading" | "error" | "ready"; - onReload: () => void; - disabled?: boolean; - accessiblityLabel?: string; -}>; - -const styles = StyleSheet.create({ - row: { - flexDirection: "row", - flex: 1, - justifyContent: "space-between", - paddingVertical: 12 - } -}); - -const PreferenceToggleRow = ({ - label, - onPress, - value, - graphicalState, - onReload, - disabled, - testID = "preference-toggle-row" -}: Props): React.ReactElement => { - const getComponentByGraphicalState = () => { - switch (graphicalState) { - case "loading": - return ( - - ); - case "error": - return ( - - - - ); - case "ready": - return ( - - ); - } - }; - return ( - - -

- {label} -

-
- {getComponentByGraphicalState()} -
- ); -}; - -export default PreferenceToggleRow; diff --git a/ts/components/services/ContactPreferencesToggles/__test__/ContactPreferencesToggles.test.tsx b/ts/components/services/ContactPreferencesToggles/__test__/ContactPreferencesToggles.test.tsx deleted file mode 100644 index ced4797c63e..00000000000 --- a/ts/components/services/ContactPreferencesToggles/__test__/ContactPreferencesToggles.test.tsx +++ /dev/null @@ -1,228 +0,0 @@ -import React from "react"; -import { Store } from "redux"; -import configureMockStore from "redux-mock-store"; -import { NotificationChannelEnum } from "../../../../../definitions/backend/NotificationChannel"; -import { ServiceId } from "../../../../../definitions/backend/ServiceId"; -import I18n from "../../../../i18n"; -import { applicationChangeState } from "../../../../store/actions/application"; -import { loadServicePreference } from "../../../../features/services/details/store/actions/preference"; -import { appReducer } from "../../../../store/reducers"; -import { GlobalState } from "../../../../store/reducers/types"; -import { ServicePreferenceResponse } from "../../../../features/services/details/types/ServicePreferenceResponse"; -import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; -import ContactPreferencesToggles from "../index"; - -jest.useFakeTimers(); - -describe("ContactPreferencesToggles component", () => { - it("should render the section header", () => { - const store = mockState({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: false, - can_access_message_read_status: false, - settings_version: 0 - } - }); - const component = renderComponent(store, {}); - expect( - component.getByText(I18n.t("serviceDetail.contacts.title")) - ).toBeDefined(); - }); - describe("when channels are not defined", () => { - it("should render all the switches", () => { - const store = mockState({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: false, - can_access_message_read_status: false, - settings_version: 0 - } - }); - const component = renderComponent(store, {}); - expect( - component.getByTestId("contact-preferences-inbox-switch") - ).toBeDefined(); - expect( - component.getByTestId("contact-preferences-webhook-switch") - ).toBeDefined(); - // TODO this option should be reintegrated once option will supported back from backend https://pagopa.atlassian.net/browse/IARS-17 - // expect( - // component.getByTestId("contact-preferences-email-switch") - // ).toBeDefined(); - }); - }); - - describe("when channels is an empty array", () => { - it("should render the INBOX switch", () => { - const store = mockState({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: false, - can_access_message_read_status: false, - settings_version: 0 - } - }); - const component = renderComponent(store, { channels: [] }); - expect( - component.getByTestId("contact-preferences-inbox-switch") - ).toBeDefined(); - }); - it("should not render WEBHOOK and EMAIL switches", () => { - const store = mockState({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: false, - can_access_message_read_status: false, - settings_version: 0 - } - }); - const component = renderComponent(store, { channels: [] }); - expect( - component.queryByTestId("contact-preferences-webhook-switch") - ).toBeNull(); - expect( - component.queryByTestId("contact-preferences-email-switch") - ).toBeNull(); - }); - }); - - describe("when channels contains all the items ", () => { - it("should render all the switches", () => { - const store = mockState({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: false, - can_access_message_read_status: false, - settings_version: 0 - } - }); - const component = renderComponent(store, { - channels: [ - NotificationChannelEnum.EMAIL, - NotificationChannelEnum.WEBHOOK - ] - }); - expect( - component.getByTestId("contact-preferences-inbox-switch") - ).toBeDefined(); - expect( - component.getByTestId("contact-preferences-webhook-switch") - ).toBeDefined(); - // TODO this option should be reintegrated once option will supported back from backend https://pagopa.atlassian.net/browse/IARS-17 - // expect( - // component.getByTestId("contact-preferences-email-switch") - // ).toBeDefined(); - }); - }); - - describe("when channels are loading", () => { - it("should render activity indicator on inbox", () => { - const initialState = appReducer( - undefined, - loadServicePreference.success({ - id: "some_id" as ServiceId, - kind: "success", - value: { - inbox: true, - email: true, - push: true, - can_access_message_read_status: false, - settings_version: 0 - } - }) - ); - // the store will be in someLoading - const state = appReducer( - initialState, - loadServicePreference.request("aServiceID" as ServiceId) - ); - const mockStore = configureMockStore(); - const store = mockStore({ - ...state - } as GlobalState); - const component = renderComponent(store, { - channels: [ - NotificationChannelEnum.EMAIL, - NotificationChannelEnum.WEBHOOK - ] - }); - expect( - component.getByTestId("contact-preferences-inbox-switch-loading") - ).toBeDefined(); - expect( - component.getByTestId("contact-preferences-webhook-switch-loading") - ).toBeDefined(); - }); - - it("should render activity indicator on inbox and webhook", () => { - const initialState = appReducer( - undefined, - applicationChangeState("active") - ); - const state = appReducer( - initialState, - loadServicePreference.request("aServiceID" as ServiceId) - ); - const mockStore = configureMockStore(); - const store = mockStore({ - ...state - } as GlobalState); - const component = renderComponent(store, { - channels: [ - NotificationChannelEnum.EMAIL, - NotificationChannelEnum.WEBHOOK - ] - }); - expect( - component.getByTestId("contact-preferences-inbox-switch-loading") - ).toBeDefined(); - }); - }); -}); - -const mockState = (servicePreference: ServicePreferenceResponse) => { - const initialState = appReducer(undefined, applicationChangeState("active")); - const state = appReducer( - initialState, - loadServicePreference.success(servicePreference) - ); - const mockStore = configureMockStore(); - return mockStore({ - ...state - } as GlobalState); -}; - -const renderComponent = ( - store: Store, - options: { - channels?: ReadonlyArray; - } -) => - renderScreenWithNavigationStoreContext( - () => ( - - ), - "route", - {}, - store - ); diff --git a/ts/components/services/ContactPreferencesToggles/__test__/PreferenceToggleRow.test.tsx b/ts/components/services/ContactPreferencesToggles/__test__/PreferenceToggleRow.test.tsx deleted file mode 100644 index a00c4f47abb..00000000000 --- a/ts/components/services/ContactPreferencesToggles/__test__/PreferenceToggleRow.test.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React from "react"; -import { render, fireEvent } from "@testing-library/react-native"; - -import PreferenceToggleRow from "../PreferenceToggleRow"; - -describe("PreferenceToggleRow component", () => { - const options: Partial[0]> = { - label: "Push Notifications", - value: true, - onPress: jest.fn() - }; - it("should match the snapshot", () => { - const component = renderComponent(options); - expect(component.toJSON()).toMatchSnapshot(); - }); - - it("should show the label", () => { - const component = renderComponent(options); - expect(component.getByText("Push Notifications")).toBeDefined(); - }); - it("should expose a working switch", () => { - const component = renderComponent(options); - const switchComponent = component.getByRole("switch"); - expect(switchComponent).toBeDefined(); - fireEvent(switchComponent, "onValueChange", false); - expect(options.onPress).toHaveBeenCalledWith(false); - }); - it("should use a default testID", () => { - const component = renderComponent(options); - expect(component.getByTestId("preference-toggle-row")).toBeDefined(); - }); - describe("when a testID is passed", () => { - it("should honour the property", () => { - const component = renderComponent({ ...options, testID: "new-test-id" }); - expect(component.getByTestId("new-test-id")).toBeDefined(); - }); - }); - - describe("handle different status", () => { - it("should display activity indicator", () => { - const component = renderComponent({ - ...options, - graphicalState: "loading" - }); - - expect( - component.getByTestId("preference-toggle-row-loading") - ).toBeDefined(); - }); - - it("should display reload button", () => { - const spy = jest.fn(); - const component = renderComponent({ - ...options, - graphicalState: "error", - onReload: spy - }); - const reloadComponent = component.getByTestId( - "preference-toggle-row-reload" - ); - expect(reloadComponent).toBeDefined(); - fireEvent.press(reloadComponent); - expect(spy).toHaveBeenCalledTimes(1); - }); - - it("should display switch disabled", () => { - const component = renderComponent({ - ...options, - graphicalState: "ready", - disabled: true - }); - expect(component.getByTestId("preference-toggle-row")).toBeDefined(); - const switchComponent = component.getByRole("switch"); - expect(switchComponent).toBeDefined(); - expect(switchComponent).toHaveProp("disabled", true); - }); - }); -}); - -function renderComponent( - options: Partial[0]> -) { - const onPress = jest.fn(); - const onReload = jest.fn(); - return render( - - ); -} diff --git a/ts/components/services/ContactPreferencesToggles/__test__/__snapshots__/PreferenceToggleRow.test.tsx.snap b/ts/components/services/ContactPreferencesToggles/__test__/__snapshots__/PreferenceToggleRow.test.tsx.snap deleted file mode 100644 index 22b9ea6c100..00000000000 --- a/ts/components/services/ContactPreferencesToggles/__test__/__snapshots__/PreferenceToggleRow.test.tsx.snap +++ /dev/null @@ -1,83 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`PreferenceToggleRow component should match the snapshot 1`] = ` - - - - Push Notifications - - - - -`; diff --git a/ts/components/services/ContactPreferencesToggles/index.tsx b/ts/components/services/ContactPreferencesToggles/index.tsx deleted file mode 100644 index c6e54eedeaa..00000000000 --- a/ts/components/services/ContactPreferencesToggles/index.tsx +++ /dev/null @@ -1,232 +0,0 @@ -import { IOToast } from "@pagopa/io-app-design-system"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useIsFocused } from "@react-navigation/native"; -import * as O from "fp-ts/lib/Option"; -import { pipe } from "fp-ts/lib/function"; -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { connect } from "react-redux"; -import I18n from "i18n-js"; -import { NotificationChannelEnum } from "../../../../definitions/backend/NotificationChannel"; -import { ServiceId } from "../../../../definitions/backend/ServiceId"; -import { trackPNPushSettings } from "../../../features/pn/analytics"; -import { - loadServicePreference, - upsertServicePreference -} from "../../../features/services/details/store/actions/preference"; -import { Dispatch } from "../../../store/actions/types"; -import { useIOSelector } from "../../../store/hooks"; -import { isPremiumMessagesOptInOutEnabledSelector } from "../../../store/reducers/backendStatus"; -import { - servicePreferenceSelector, - ServicePreferenceState -} from "../../../features/services/details/store/reducers/servicePreference"; -import { GlobalState } from "../../../store/reducers/types"; -import { - isServicePreferenceResponseSuccess, - ServicePreference -} from "../../../features/services/details/types/ServicePreferenceResponse"; -import { isStrictSome } from "../../../utils/pot"; -import ItemSeparatorComponent from "../../ItemSeparatorComponent"; -import SectionHeader from "../SectionHeader"; -import PreferenceToggleRow from "./PreferenceToggleRow"; - -type Item = "email" | "push" | "inbox" | "can_access_message_read_status"; - -type Props = { - channels?: ReadonlyArray; - serviceId: ServiceId; - isSpecialService: boolean; - customSpecialFlowOpt?: string; -} & ReturnType & - ReturnType; - -const hasChannel = ( - channel: NotificationChannelEnum, - channels?: ReadonlyArray -) => - pipe( - channels, - O.fromNullable, - O.map(anc => anc.indexOf(channel) !== -1), - O.getOrElse(() => true) - ); - -/** - * Utility function to get the user preference value for a specific channel - * return false if preference state is pot.none or if an error occurred on API Response - * */ -const getChannelPreference = ( - potServicePreference: ServicePreferenceState, - key: Item -): boolean => { - if ( - pot.isSome(potServicePreference) && - isServicePreferenceResponseSuccess(potServicePreference.value) - ) { - return potServicePreference.value.value[key]; - } - return false; -}; - -const ContactPreferencesToggle: React.FC = (props: Props) => { - const { isLoading, isError } = props; - const [isFirstRender, setIsFirstRender] = useState(true); - const { serviceId, loadServicePreference } = props; - - const loadPreferences = useCallback( - () => loadServicePreference(serviceId), - [serviceId, loadServicePreference] - ); - - const isFocused = useIsFocused(); - - const isPremiumMessagesOptInOutEnabled = useIOSelector( - isPremiumMessagesOptInOutEnabledSelector - ); - - useEffect(() => { - loadPreferences(); - }, [serviceId, loadPreferences, isFocused]); - - useEffect(() => { - if (!isFirstRender) { - if (isError) { - IOToast.error(I18n.t("global.genericError")); - } - } else { - setIsFirstRender(false); - } - }, [isError, isFirstRender]); - - const onValueChange = (value: boolean, type: Item) => { - if ( - isStrictSome(props.servicePreferenceStatus) && - isServicePreferenceResponseSuccess(props.servicePreferenceStatus.value) - ) { - props.upsertServicePreference(props.serviceId, { - ...props.servicePreferenceStatus.value.value, - [type]: value - }); - } - }; - - const graphicalState = useMemo( - () => (isLoading ? "loading" : isError ? "error" : "ready"), - [isLoading, isError] - ); - - return ( - <> - - {/* - This Toggle is disabled if the current service is a Special Service cause user can - enable or disable the service only using the proper Special Service flow and not only tapping the specific toggle - */} - onValueChange(value, "inbox")} - disabled={props.isSpecialService} - graphicalState={graphicalState} - onReload={loadPreferences} - value={getChannelPreference(props.servicePreferenceStatus, "inbox")} - testID={"contact-preferences-inbox-switch"} - /> - - {hasChannel(NotificationChannelEnum.WEBHOOK, props.channels) && - getChannelPreference(props.servicePreferenceStatus, "inbox") && ( - // toggle is disabled if the inbox value is false to prevent inconsistent data - <> - { - pipe( - props.customSpecialFlowOpt, - O.fromNullable, - O.filter(customSpecialFlow => customSpecialFlow === "pn"), - O.fold( - () => undefined, - _ => trackPNPushSettings(value) - ) - ); - onValueChange(value, "push"); - }} - value={getChannelPreference( - props.servicePreferenceStatus, - "push" - )} - graphicalState={graphicalState} - onReload={loadPreferences} - testID={"contact-preferences-webhook-switch"} - /> - - - )} - {isPremiumMessagesOptInOutEnabled && - getChannelPreference(props.servicePreferenceStatus, "inbox") && ( - // toggle is disabled if the inbox value is false to prevent inconsistent data - <> - - onValueChange(value, "can_access_message_read_status") - } - value={getChannelPreference( - props.servicePreferenceStatus, - "can_access_message_read_status" - )} - graphicalState={graphicalState} - onReload={loadPreferences} - testID={"contact-preferences-trackSeen-switch"} - /> - - - )} - - {/* Email toggle is temporary removed until the feature will be enabled back from the backend */} - {/* TODO this option should be reintegrated once option will supported back from backend https://pagopa.atlassian.net/browse/IARS-17 */} - {/* {hasChannel(NotificationChannelEnum.EMAIL, props.channels) && getChannelPreference(props.servicePreferenceStatus, "inbox") && ( */} - {/* <> */} - {/* onValueChange(value, "email")} */} - {/* value={getChannelPreference(props.servicePreferenceStatus, "email")} */} - {/* graphicalState={graphicalState} */} - {/* isError={isError} */} - {/* testID={"contact-preferences-email-switch"} */} - {/* /> */} - {/* */} - {/* */} - {/* )} */} - - ); -}; - -const mapStateToProps = (state: GlobalState) => { - const servicePreferenceStatus = servicePreferenceSelector(state); - const isLoading = - pot.isLoading(servicePreferenceStatus) || - pot.isUpdating(servicePreferenceStatus); - - const isError = - pot.isError(servicePreferenceStatus) || - (isStrictSome(servicePreferenceStatus) && - servicePreferenceStatus.value.kind !== "success"); - - return { - isLoading, - isError, - servicePreferenceStatus - }; -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - upsertServicePreference: (id: ServiceId, sp: ServicePreference) => - dispatch(upsertServicePreference.request({ id, ...sp })), - loadServicePreference: (id: ServiceId) => - dispatch(loadServicePreference.request(id)) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ContactPreferencesToggle); diff --git a/ts/components/services/LinkRow.tsx b/ts/components/services/LinkRow.tsx deleted file mode 100644 index b25001d0e87..00000000000 --- a/ts/components/services/LinkRow.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import * as React from "react"; -import { StyleSheet } from "react-native"; - -import { IOToast } from "@pagopa/io-app-design-system"; -import { TranslationKeys } from "../../../locales/locales"; -import I18n from "../../i18n"; -import { openWebUrl } from "../../utils/url"; - -import { Link } from "../core/typography/Link"; -import ItemSeparatorComponent from "../ItemSeparatorComponent"; - -const styles = StyleSheet.create({ - link: { - paddingVertical: 16 - } -}); - -type Props = { - text: TranslationKeys; - href: string; -}; - -const LinkRow = ({ text, href }: Props) => ( - <> - - openWebUrl(href, () => IOToast.error(I18n.t("global.jserror.title"))) - } - numberOfLines={1} - style={styles.link} - > - {I18n.t(text)} - - - -); - -export default LinkRow; diff --git a/ts/components/services/LocalServicesWebView.tsx b/ts/components/services/LocalServicesWebView.tsx deleted file mode 100644 index 82a889e0363..00000000000 --- a/ts/components/services/LocalServicesWebView.tsx +++ /dev/null @@ -1,175 +0,0 @@ -import { IOColors, IOToast, hexToRgba } from "@pagopa/io-app-design-system"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as E from "fp-ts/lib/Either"; -import * as O from "fp-ts/lib/Option"; -import { pipe } from "fp-ts/lib/function"; -import React from "react"; -import { StyleSheet, View } from "react-native"; -import WebView, { WebViewMessageEvent } from "react-native-webview"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { ServiceId } from "../../../definitions/backend/ServiceId"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import { localServicesWebUrl } from "../../config"; -import { useTabItemPressWhenScreenActive } from "../../hooks/useTabItemPressWhenScreenActive"; -import I18n from "../../i18n"; -import { loadServiceDetail } from "../../features/services/details/store/actions/details"; -import { servicesByIdSelector } from "../../features/services/details/store/reducers/servicesById"; -import { GlobalState } from "../../store/reducers/types"; -import { isStrictSome } from "../../utils/pot"; -import { AVOID_ZOOM_JS, closeInjectedScript } from "../../utils/webview"; -import { withLightModalContext } from "../helpers/withLightModalContext"; -import GenericErrorComponent from "../screens/GenericErrorComponent"; -import { RefreshIndicator } from "../ui/RefreshIndicator"; - -type Props = { - onServiceSelect: (service: ServicePublic) => void; -} & ReturnType & - ReturnType; - -const opaqueBgColor = hexToRgba(IOColors.white, 0.5); - -const styles = StyleSheet.create({ - refreshIndicatorContainer: { - position: "absolute", - left: 0, - right: 0, - top: 0, - bottom: 0, - backgroundColor: opaqueBgColor, - justifyContent: "center", - alignItems: "center", - zIndex: 1000 - }, - genericError: { - flex: 1, - position: "absolute", - left: 0, - right: 0, - top: 0, - bottom: 0 - }, - webView: { - flex: 1 - } -}); -const renderLoading = () => ( - - - -); - -/** - * This component is basically a webview that loads an url showing local services - * It intercepts the request of loading a service and it does: - * - block that request from loading - * - extract from the request url the service id - * - load the selected service starting from the service id (load and error are handled) - */ -const LocalServicesWebView = (props: Props) => { - const [serviceIdToLoad, setServiceIdToLoad] = React.useState< - string | undefined - >(undefined); - const [webViewError, setWebViewError] = React.useState(false); - const webViewRef = React.createRef(); - - const scrollWebview = (x: number, y: number) => { - const script = `window.scrollTo(${x}, ${y})`; - webViewRef.current?.injectJavaScript(script); - }; - - useTabItemPressWhenScreenActive(() => scrollWebview(0, 0), true); - - const { servicesById, onServiceSelect } = props; - - React.useEffect(() => { - pipe( - serviceIdToLoad, - O.fromNullable, - O.chainNullableK(sid => servicesById[sid]), - O.map(servicePot => { - // if service has been loaded - if (isStrictSome(servicePot)) { - onServiceSelect(servicePot.value); - setServiceIdToLoad(undefined); - return; - } - if (pot.isError(servicePot)) { - IOToast.error(I18n.t("global.genericError")); - } - }) - ); - }, [servicesById, onServiceSelect, serviceIdToLoad]); - - const reloadWebView = () => { - if (webViewRef.current) { - webViewRef.current.reload(); - setWebViewError(false); - } - }; - - /** - * 'listen' on web message - * if a serviceId is sent: dispatch service loading request - * @param event - */ - const handleWebviewMessage = (event: WebViewMessageEvent) => { - pipe( - event.nativeEvent.data, - ServiceId.decode, - E.map(sId => { - setServiceIdToLoad(sId); - // request loading service - props.loadService(sId); - }) - ); - }; - - const isLoadingServiceLoading = pipe( - serviceIdToLoad, - O.fromNullable, - O.chainNullableK(sid => props.servicesById[sid]), - O.fold(() => false, pot.isLoading) - ); - return ( - <> - {isLoadingServiceLoading && renderLoading()} - - setWebViewError(true)} - onMessage={handleWebviewMessage} - startInLoadingState={true} - renderLoading={renderLoading} - javaScriptEnabled={true} - /> - {webViewError && ( - - - - )} - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadService: (serviceId: string) => - dispatch(loadServiceDetail.request(serviceId)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - servicesById: servicesByIdSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(withLightModalContext(LocalServicesWebView)); diff --git a/ts/components/services/NewServiceListItem.tsx b/ts/components/services/NewServiceListItem.tsx deleted file mode 100644 index 29c6469db17..00000000000 --- a/ts/components/services/NewServiceListItem.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { GestureResponderEvent } from "react-native"; -import { ListItemNav } from "@pagopa/io-app-design-system"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import I18n from "../../i18n"; - -type Props = { - item: pot.Pot; - onSelect: (service: ServicePublic) => void; - hideSeparator: boolean; -}; - -const NewServiceListItem = (props: Props): React.ReactElement => { - const potService = props.item; - const onPress = pot.toUndefined( - pot.map(potService, service => () => props.onSelect(service)) - ); - - const serviceName = pot.fold( - potService, - () => I18n.t("global.remoteStates.loading"), - () => I18n.t("global.remoteStates.loading"), - () => I18n.t("global.remoteStates.notAvailable"), - () => I18n.t("global.remoteStates.notAvailable"), - service => service.service_name, - () => I18n.t("global.remoteStates.loading"), - service => service.service_name, - () => I18n.t("global.remoteStates.notAvailable") - ); - - return ( - void} - testID={serviceName} - /> - ); -}; - -export default NewServiceListItem; diff --git a/ts/components/services/OrganizationLogo.tsx b/ts/components/services/OrganizationLogo.tsx deleted file mode 100644 index 3a8850ef60c..00000000000 --- a/ts/components/services/OrganizationLogo.tsx +++ /dev/null @@ -1,35 +0,0 @@ -// A component to provide organization logo -import * as React from "react"; -import { ImageStyle, StyleProp, StyleSheet } from "react-native"; -import variables from "../../theme/variables"; -import { MultiImage } from "../ui/MultiImage"; - -type Props = { - logoUri: React.ComponentProps["source"]; - imageStyle?: StyleProp; -}; - -const styles = StyleSheet.create({ - organizationLogo: { - height: 32, - width: 32, - resizeMode: "contain", - marginBottom: 6, - alignSelf: "flex-start", - marginRight: variables.spacingBase - } -}); - -class OrganizationLogo extends React.Component { - public render(): React.ReactNode { - const { logoUri } = this.props; - return ( - - ); - } -} - -export default OrganizationLogo; diff --git a/ts/components/services/SectionHeader.tsx b/ts/components/services/SectionHeader.tsx deleted file mode 100644 index 1d701068645..00000000000 --- a/ts/components/services/SectionHeader.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from "react"; -import { StyleSheet, View } from "react-native"; - -import { IOIcons, Icon, HSpacer } from "@pagopa/io-app-design-system"; -import { TranslationKeys } from "../../../locales/locales"; -import I18n from "../../i18n"; - -import { H3 } from "../core/typography/H3"; - -const styles = StyleSheet.create({ - header: { - flexDirection: "row", - paddingVertical: 8, - alignItems: "center" - } -}); - -type Props = { - iconName: IOIcons; - title: TranslationKeys; -}; - -/** - * Renders a header for any section in the service's details page - */ -const sectionHeader: React.FC = ({ iconName, title }) => ( - - - -

- {I18n.t(title)} -

-
-); - -export default sectionHeader; diff --git a/ts/components/services/ServiceList.tsx b/ts/components/services/ServiceList.tsx deleted file mode 100644 index a03dbb14199..00000000000 --- a/ts/components/services/ServiceList.tsx +++ /dev/null @@ -1,134 +0,0 @@ -/** - * A component to render a list of services grouped by organization. - */ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import React from "react"; -import { - Animated, - ListRenderItemInfo, - NativeScrollEvent, - NativeSyntheticEvent, - RefreshControl, - SectionList, - SectionListData -} from "react-native"; -import { IOVisualCostants } from "@pagopa/io-app-design-system"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import { getLogoForOrganization } from "../../utils/organizations"; -import { - TabBarItemPressType, - withUseTabItemPressWhenScreenActive -} from "../helpers/withUseTabItemPressWhenScreenActive"; - -import ItemSeparatorComponent from "../ItemSeparatorComponent"; -import SectionHeaderComponent from "../screens/SectionHeaderComponent"; -import NewServiceListItem from "./NewServiceListItem"; - -type AnimatedProps = { - animated?: { - onScroll: (_: NativeSyntheticEvent) => void; - scrollEventThrottle?: number; - }; -}; - -type OwnProps = { - sections: ReadonlyArray>>; - isRefreshing: boolean; - onRefresh: () => void; - onSelect: (service: ServicePublic) => void; - ListEmptyComponent?: React.ComponentProps< - typeof SectionList - >["ListEmptyComponent"]; -}; - -type Props = OwnProps & AnimatedProps & TabBarItemPressType; - -class ServiceList extends React.Component { - componentDidMount() { - const { setHasInternalTab: setHasInternalTab, setTabPressCallback } = - this.props; - - setHasInternalTab(true); - setTabPressCallback(() => () => { - sectionListRef.current?.scrollToLocation({ - animated: true, - itemIndex: 0, - sectionIndex: 0, - viewOffset: 0 - }); - }); - } - - private renderServiceItem = ( - itemInfo: ListRenderItemInfo> - ) => ( - - ); - - private getServiceKey = ( - potService: pot.Pot, - index: number - ): string => - pot.getOrElse( - pot.map( - potService, - service => `${service.service_id}-${service.version}` - ), - `service-pot-${index}` - ); - - private renderServiceSectionHeader = (info: { - section: SectionListData>; - }): React.ReactNode => ( - - ); - - public render() { - const { sections, isRefreshing, onRefresh, ListEmptyComponent } = - this.props; - - const refreshControl = ( - - ); - - return ( - - ); - } -} - -const sectionListRef = React.createRef(); - -export default withUseTabItemPressWhenScreenActive(ServiceList); diff --git a/ts/components/services/ServiceMetadata/InformationRow.tsx b/ts/components/services/ServiceMetadata/InformationRow.tsx deleted file mode 100644 index bc89c79b197..00000000000 --- a/ts/components/services/ServiceMetadata/InformationRow.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import React from "react"; -import { View, StyleSheet } from "react-native"; -import { HSpacer } from "@pagopa/io-app-design-system"; -import { TranslationKeys } from "../../../../locales/locales"; -import { capitalize } from "../../../utils/strings"; -import I18n from "../../../i18n"; -import { H4 } from "../../core/typography/H4"; -import TouchableDefaultOpacity from "../../TouchableDefaultOpacity"; -import ItemSeparatorComponent from "../../ItemSeparatorComponent"; - -const styles = StyleSheet.create({ - touchable: { - flexDirection: "row", - marginVertical: 16 - }, - value: { - flexGrow: 1, - flexShrink: 1, - textAlign: "right" - } -}); - -type Props = { - value: string; - label: TranslationKeys; - onPress: () => void; - isLast?: boolean; - accessibilityLabel?: string; -}; - -const InformationRow = ({ - value, - label, - onPress, - isLast, - accessibilityLabel -}: Props) => ( - - -

- {capitalize(I18n.t(label))} -

- -

- {value} -

-
- {!isLast && } -
-); - -export default InformationRow; diff --git a/ts/components/services/ServiceMetadata/__tests__/InformationRow.test.tsx b/ts/components/services/ServiceMetadata/__tests__/InformationRow.test.tsx deleted file mode 100644 index a06f9af6a94..00000000000 --- a/ts/components/services/ServiceMetadata/__tests__/InformationRow.test.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react-native"; -import { constNull } from "fp-ts/lib/function"; - -import InformationRow from "../InformationRow"; - -describe("the InformationRow component", () => { - it("should match the snapshot", () => { - expect( - render( - - ).toJSON() - ).toMatchSnapshot(); - }); -}); diff --git a/ts/components/services/ServiceMetadata/__tests__/ServiceMetadata.test.tsx b/ts/components/services/ServiceMetadata/__tests__/ServiceMetadata.test.tsx deleted file mode 100644 index 40e4ab86560..00000000000 --- a/ts/components/services/ServiceMetadata/__tests__/ServiceMetadata.test.tsx +++ /dev/null @@ -1,329 +0,0 @@ -import { OrganizationFiscalCode } from "@pagopa/ts-commons/lib/strings"; -import { fireEvent, render } from "@testing-library/react-native"; -import React from "react"; -import { testableGenServiceMetadataAccessibilityLabel } from "../"; -import { ServiceId } from "../../../../../definitions/backend/ServiceId"; -import { ServiceMetadata } from "../../../../../definitions/backend/ServiceMetadata"; -import { ServiceScopeEnum } from "../../../../../definitions/backend/ServiceScope"; -import { StandardServiceCategoryEnum } from "../../../../../definitions/backend/StandardServiceCategory"; -import { TranslationKeys } from "../../../../../locales/locales"; -import I18n from "../../../../i18n"; -import { capitalize } from "../../../../utils/strings"; -import * as utilsUrl from "../../../../utils/url"; -import ServiceMetadataComponent from "../../ServiceMetadata"; - -jest.mock("../../../../utils/platform"); - -const spyOpenWebUrl = jest.spyOn(utilsUrl, "openWebUrl"); - -const defaultServiceMetadata: ServiceMetadata = { - scope: ServiceScopeEnum.NATIONAL, - category: StandardServiceCategoryEnum.STANDARD -}; - -const defaultProps = { - getItemOnPress: jest.fn(), - isDebugModeEnabled: false, - organizationFiscalCode: "01234567891" as OrganizationFiscalCode, - serviceId: "ABC123" as ServiceId, - servicesMetadata: defaultServiceMetadata -}; - -const genServiceMetadataAccessibilityLabel = - testableGenServiceMetadataAccessibilityLabel!; - -describe("ServiceMetadata component", () => { - beforeEach(() => { - defaultProps.getItemOnPress.mockReset(); - spyOpenWebUrl.mockReset(); - }); - afterEach(() => { - jest.dontMock("../../../../utils/url"); - }); - - it("should render the section header", () => { - expect( - renderComponent({ ...defaultProps }).getByText( - I18n.t("services.contactsAndInfo") - ) - ).toBeDefined(); - }); - - describe("when debug mode is enabled", () => { - const currentOptions = { - ...defaultProps, - isDebugModeEnabled: true - }; - it("should render the serviceId label", () => { - expect( - renderComponent(currentOptions).getByText( - capitalize(I18n.t("global.id")) - ) - ).toBeDefined(); - }); - - it("should render the serviceId value", () => { - expect( - renderComponent(currentOptions).getByText(currentOptions.serviceId) - ).toBeDefined(); - }); - - it(`should call "getItemOnPress" with (${currentOptions.serviceId}, "COPY")`, () => { - renderComponent(currentOptions); - expect(currentOptions.getItemOnPress).toHaveBeenCalledWith( - currentOptions.serviceId, - "COPY" - ); - }); - }); - - describe("given an organizationFiscalCode", () => { - it("should render the organizationFiscalCode label", () => { - expect( - renderComponent(defaultProps).getByText( - capitalize(I18n.t("serviceDetail.fiscalCode")) - ) - ).toBeDefined(); - }); - - it("should render the organizationFiscalCode value", () => { - expect( - renderComponent(defaultProps).getByText( - defaultProps.organizationFiscalCode - ) - ).toBeDefined(); - }); - - it("should render the correct accessibility label", () => { - const a11yLabel = genServiceMetadataAccessibilityLabel( - I18n.t("serviceDetail.fiscalCode"), - defaultProps.organizationFiscalCode, - I18n.t("serviceDetail.fiscalCodeAccessibilityCopy") - ); - - expect( - renderComponent(defaultProps).getByA11yLabel(a11yLabel) - ).toBeDefined(); - }); - - it(`should call "getItemOnPress" with (${defaultProps.organizationFiscalCode}, "COPY")`, () => { - renderComponent(defaultProps); - expect(defaultProps.getItemOnPress).toHaveBeenCalledWith( - defaultProps.organizationFiscalCode, - "COPY" - ); - }); - }); - - [ - [ - "address", - "via genova", - "services.contactAddress", - "MAP", - "openMaps.openAddressOnMap" - ] - ].forEach(([name, value, label, action, hint]) => { - describe(`when ${name} is defined`, () => { - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - [name]: value - } - }; - it(`should render its label "${label}"`, () => { - expect( - renderComponent(currentOptions).getByText( - capitalize(I18n.t(label as TranslationKeys)) - ) - ).toBeDefined(); - }); - it(`should render its value "${value}"`, () => { - expect(renderComponent(currentOptions).getByText(value)).toBeDefined(); - }); - it(`should call "getItemOnPress" with ("${value}", ${action})`, () => { - renderComponent(currentOptions); - expect(currentOptions.getItemOnPress).toHaveBeenCalledWith( - value, - action - ); - }); - it("should render the correct accessibility label", () => { - const a11yLabel = genServiceMetadataAccessibilityLabel( - I18n.t(label as TranslationKeys), - value, - I18n.t(hint as TranslationKeys) - ); - - expect( - renderComponent(currentOptions).getByA11yLabel(a11yLabel) - ).toBeDefined(); - }); - }); - }); - - [ - ["email", "jest@test.com", "global.media.email", "mailto:"], - ["pec", "jest.pec@test.com", "global.media.pec", "mailto:"], - ["phone", "12341234", "global.media.phone", "tel:"] - ].forEach(([name, value, label, prefix]) => { - describe(`when ${name} is defined`, () => { - const currentOptions = { - ...defaultProps, - servicesMetadata: { ...defaultServiceMetadata, [name]: value } - }; - // eslint-disable-next-line sonarjs/no-identical-functions - it(`should render its label "${label}"`, () => { - expect( - renderComponent(currentOptions).getByText( - capitalize(I18n.t(label as TranslationKeys)) - ) - ).toBeDefined(); - }); - it(`should render its value "${value}"`, () => { - expect(renderComponent(currentOptions).getByText(value)).toBeDefined(); - }); - it(`should call "getItemOnPress" with ("${prefix}:${value}")`, () => { - renderComponent(currentOptions); - expect(currentOptions.getItemOnPress).toHaveBeenCalledWith( - `${prefix}${value}` - ); - }); - }); - }); - - [ - ["support_url", "www.support.it", "services.askForAssistance"], - ["web_url", "www.product.it", "services.visitWebsite"] - ].forEach(([name, value, label]) => { - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - [name]: value - } - }; - describe(`when ${name} is defined`, () => { - it(`should render a link with "${label}"`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - expect(link).toBeDefined(); - expect(link.children.toString()).toMatch( - I18n.t(label as TranslationKeys) - ); - }); - describe("when the link is pressed", () => { - it(`should open the url "${value}"`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - fireEvent(link, "onPress"); - expect(spyOpenWebUrl).toHaveBeenCalledWith( - value, - expect.any(Function) - ); - }); - }); - }); - }); - - describe("when the platform is Android", () => { - beforeAll(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - require("../../../../utils/platform").test_setPlatform("android"); - }); - - describe(`and servicesMetadata.app_android is defined`, () => { - const androidUrl = "http://www.android.google"; - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - app_android: androidUrl - } as ServiceMetadata - }; - it(`should render the Android link`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - expect(link.children.toString()).toMatch( - I18n.t("services.otherAppAndroid") - ); - }); - it(`the link should open ${androidUrl}`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - fireEvent(link, "onPress"); - expect(spyOpenWebUrl).toHaveBeenCalledWith( - androidUrl, - expect.any(Function) - ); - }); - }); - - describe(`and servicesMetadata.app_ios is defined`, () => { - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - app_ios: "dummy" - } as ServiceMetadata - }; - it(`should not render it`, () => { - expect(renderComponent(currentOptions).queryByRole("link")).toBeNull(); - }); - }); - }); - - describe("when the platform is iOS", () => { - beforeAll(() => { - // eslint-disable-next-line @typescript-eslint/no-var-requires - require("../../../../utils/platform").test_setPlatform("ios"); - }); - - describe(`and servicesMetadata.app_ios is defined`, () => { - const iosUrl = "http://www.ios.apple"; - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - app_ios: iosUrl - } as ServiceMetadata - }; - it(`should render the iOS link`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - expect(link.children.toString()).toMatch( - I18n.t("services.otherAppIos") - ); - }); - it(`the link should open ${iosUrl}`, () => { - const component = renderComponent(currentOptions); - const link = component.getByRole("link"); - fireEvent(link, "onPress"); - expect(spyOpenWebUrl).toHaveBeenCalledWith( - iosUrl, - expect.any(Function) - ); - }); - }); - - describe(`and servicesMetadata.app_android is defined`, () => { - const currentOptions = { - ...defaultProps, - servicesMetadata: { - ...defaultServiceMetadata, - app_android: "dummy" - } as ServiceMetadata - }; - it(`should not render it`, () => { - expect(renderComponent(currentOptions).queryByRole("link")).toBeNull(); - }); - }); - }); -}); - -function renderComponent( - props: Parameters[0] -) { - return render(); -} diff --git a/ts/components/services/ServiceMetadata/__tests__/__snapshots__/InformationRow.test.tsx.snap b/ts/components/services/ServiceMetadata/__tests__/__snapshots__/InformationRow.test.tsx.snap deleted file mode 100644 index 907c0eb4256..00000000000 --- a/ts/components/services/ServiceMetadata/__tests__/__snapshots__/InformationRow.test.tsx.snap +++ /dev/null @@ -1,112 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`the InformationRow component should match the snapshot 1`] = ` - - - - Institution's Fiscal Code - - - - via Roma - - - - -`; diff --git a/ts/components/services/ServiceMetadata/index.tsx b/ts/components/services/ServiceMetadata/index.tsx deleted file mode 100644 index 89c9c00b2ca..00000000000 --- a/ts/components/services/ServiceMetadata/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { OrganizationFiscalCode } from "@pagopa/ts-commons/lib/strings"; -import React from "react"; -import { ServiceId } from "../../../../definitions/backend/ServiceId"; -import { ServiceMetadata } from "../../../../definitions/backend/ServiceMetadata"; -import I18n from "../../../i18n"; -import { isTestEnv } from "../../../utils/environment"; -import { isAndroid, isIos } from "../../../utils/platform"; -import { ItemAction } from "../../../utils/url"; -import LinkRow from ".././LinkRow"; -import SectionHeader from ".././SectionHeader"; -import InformationRow from "./InformationRow"; - -type Props = { - getItemOnPress: (value: string, valueType?: ItemAction) => () => void; - isDebugModeEnabled: boolean; - organizationFiscalCode: OrganizationFiscalCode; - serviceId: ServiceId; - servicesMetadata?: ServiceMetadata; -}; - -/** - * Function used to generate the `accessibilityLabel` for a single - * row in the `ServiceMetadataComponent`. Given the `field`, `value, - * and `hint` it creates a custom label that contains all these - * informations. - */ -const genServiceMetadataAccessibilityLabel = ( - field: string, - value: string, - hint: string -) => `${field}: ${value}, ${hint}`; - -export const testableGenServiceMetadataAccessibilityLabel = isTestEnv - ? genServiceMetadataAccessibilityLabel - : undefined; - -/** - * Renders a dedicated section with a service's metadata and the header. - */ -const ServiceMetadataComponent: React.FC = ({ - organizationFiscalCode, - getItemOnPress, - serviceId, - servicesMetadata, - isDebugModeEnabled -}: Props) => { - const { - address, - app_android, - email, - app_ios, - pec, - phone, - support_url, - web_url - } = servicesMetadata || {}; - return ( - <> - - - {/* links */} - {web_url && } - {support_url && ( - - )} - {isIos && app_ios && ( - - )} - {isAndroid && app_android && ( - - )} - - {/* touchable rows */} - { - - } - {address && ( - - )} - {phone && ( - - )} - {email && ( - - )} - {pec && ( - - )} - {isDebugModeEnabled && serviceId && ( - - )} - - ); -}; - -export default ServiceMetadataComponent; diff --git a/ts/components/services/ServicesSearch.tsx b/ts/components/services/ServicesSearch.tsx deleted file mode 100644 index acb7d6cb7b0..00000000000 --- a/ts/components/services/ServicesSearch.tsx +++ /dev/null @@ -1,162 +0,0 @@ -/** - * A component that renders a list of services that match a search text. - * TODO: fix scroll: some items are displayed only if the keyboard is hidden - * https://www.pivotaltracker.com/story/show/168803731 - */ - -import * as pot from "@pagopa/ts-commons/lib/pot"; -import React from "react"; -import { SectionListData } from "react-native"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import { ServicesSectionState } from "../../store/reducers/entities/services"; -import { isDefined } from "../../utils/guards"; -import { serviceContainsText } from "../../utils/services"; -import { SearchNoResultMessage } from "../search/SearchNoResultMessage"; -import ServicesSectionsList from "./ServicesSectionsList"; - -type OwnProps = { - sectionsState: ReadonlyArray; - searchText: string; - onRefresh: () => void; - navigateToServiceDetail: (service: ServicePublic) => void; -}; - -type Props = OwnProps; - -type State = { - potFilteredServiceSectionsStates: pot.Pot< - ReadonlyArray, - Error - >; -}; - -/** - * Filter only the services that match the searchText. - */ -const generateSectionsServicesStateMatchingSearchTextArrayAsync = ( - servicesState: ReadonlyArray, - searchText: string -): Promise> => - new Promise(resolve => { - const result = servicesState - .map(section => - filterSectionListDataMatchingSearchText(section, searchText) - ) - .filter(isDefined); - - resolve(result); - }); - -function filterSectionListDataMatchingSearchText( - sectionListData: SectionListData>, - searchText: string -) { - const filteredData = sectionListData.data - .map(potService => - pot.filter(potService, servicePublic => - // Search in service properties - serviceContainsText(servicePublic, searchText) - ) - ) - .filter(pot.isSome); - - const sectionListDataFiltered = { - organizationName: sectionListData.organizationName, - organizationFiscalCode: sectionListData.organizationFiscalCode, - data: filteredData - }; - return filteredData.length > 0 ? sectionListDataFiltered : null; -} - -class ServicesSearch extends React.PureComponent { - constructor(props: Props) { - super(props); - this.state = { - potFilteredServiceSectionsStates: pot.noneLoading - }; - } - - public async componentDidMount() { - const { sectionsState, searchText } = this.props; - const { potFilteredServiceSectionsStates } = this.state; - - // Set filtering status - this.setState({ - potFilteredServiceSectionsStates: pot.toLoading( - potFilteredServiceSectionsStates - ) - }); - - // Start filtering services - const filteredServiceSectionsStates = - await generateSectionsServicesStateMatchingSearchTextArrayAsync( - sectionsState, - searchText - ); - - // Unset filtering status - this.setState({ - potFilteredServiceSectionsStates: pot.some(filteredServiceSectionsStates) - }); - } - - public async componentDidUpdate(prevProps: Props) { - const { sectionsState: prevServicesState, searchText: prevSearchText } = - prevProps; - const { sectionsState, searchText } = this.props; - const { potFilteredServiceSectionsStates } = this.state; - - if (sectionsState !== prevServicesState || searchText !== prevSearchText) { - // Set filtering status - this.setState({ - potFilteredServiceSectionsStates: pot.toLoading( - potFilteredServiceSectionsStates - ) - }); - - // Start filtering services - const filteredServiceSectionsStates = - await generateSectionsServicesStateMatchingSearchTextArrayAsync( - sectionsState, - searchText - ); - - // Unset filtering status - this.setState({ - potFilteredServiceSectionsStates: pot.some( - filteredServiceSectionsStates - ) - }); - } - } - - public render() { - const { potFilteredServiceSectionsStates } = this.state; - const { onRefresh } = this.props; - - const isFiltering = pot.isLoading(potFilteredServiceSectionsStates); - - const filteredServiceSectionsStates = pot.getOrElse( - potFilteredServiceSectionsStates, - [] - ); - - return filteredServiceSectionsStates.length > 0 ? ( - - ) : ( - - ); - } - - private handleOnServiceSelect = (service: ServicePublic) => { - this.props.navigateToServiceDetail(service); - }; -} - -export default ServicesSearch; diff --git a/ts/components/services/ServicesSectionsList.tsx b/ts/components/services/ServicesSectionsList.tsx deleted file mode 100644 index 9e30ae9ea87..00000000000 --- a/ts/components/services/ServicesSectionsList.tsx +++ /dev/null @@ -1,81 +0,0 @@ -/** - * A component to render a list of services organized in sections, one for each organization. - */ -import { VSpacer } from "@pagopa/io-app-design-system"; -import React from "react"; -import { - Image, - NativeScrollEvent, - NativeSyntheticEvent, - StyleSheet, - View -} from "react-native"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import I18n from "../../i18n"; -import { ServicesSectionState } from "../../store/reducers/entities/services"; -import customVariables from "../../theme/variables"; -import { Body } from "../core/typography/Body"; -import { IOStyles } from "../core/variables/IOStyles"; -import ServiceList from "./ServiceList"; - -type AnimatedProps = { - animated?: { - onScroll: (_: NativeSyntheticEvent) => void; - scrollEventThrottle?: number; - }; -}; - -type OwnProps = { - sections: ReadonlyArray; - isRefreshing: boolean; - onRefresh: () => void; - onSelect: (service: ServicePublic) => void; -}; - -type Props = AnimatedProps & OwnProps; - -const styles = StyleSheet.create({ - contentWrapper: { - flex: 1 - }, - headerContentWrapper: { - paddingTop: customVariables.contentPadding / 2, - paddingBottom: customVariables.contentPadding / 2, - alignItems: "center" - } -}); - -// component used when the list is empty -const emptyListComponent = () => ( - - - - - - {I18n.t("services.emptyListMessage")} - - -); - -const ServicesSectionsList = (props: Props) => ( - - {/* TODO: This is a workaround to make sure that the list is not placed under the tab bar - https://pagopa.atlassian.net/jira/software/projects/IOAPPFD0/boards/313?selectedIssue=IOAPPFD0-40 */} - - - -); - -export default ServicesSectionsList; diff --git a/ts/components/services/ServicesTab.tsx b/ts/components/services/ServicesTab.tsx deleted file mode 100644 index b38b8556e7b..00000000000 --- a/ts/components/services/ServicesTab.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * A component to render a tab containing a list of services organized in sections - */ -import * as React from "react"; -import { Animated } from "react-native"; -import { ServicePublic } from "../../../definitions/backend/ServicePublic"; -import { ServicesSectionState } from "../../store/reducers/entities/services"; -import { withLightModalContext } from "../helpers/withLightModalContext"; -import { LightModalContextInterface } from "../ui/LightModal"; -import ServicesSectionsList from "./ServicesSectionsList"; - -type OwnProps = Readonly<{ - sections: ReadonlyArray; - isRefreshing: boolean; - onRefresh: (hideToast?: boolean) => void; // eslint-disable-line - onServiceSelect: (service: ServicePublic) => void; - tabScrollOffset: Animated.Value; -}>; - -type Props = OwnProps & LightModalContextInterface; - -const ServicesTab = (props: Props): React.ReactElement => { - const onTabScroll = () => ({ - onScroll: Animated.event([ - { - nativeEvent: { - contentOffset: { y: props.tabScrollOffset } - } - } - ]), - scrollEventThrottle: 8 - }); - - return ( - - ); -}; - -export default withLightModalContext(ServicesTab); diff --git a/ts/components/services/SpecialServices/LegacySpecialServicesCTA.tsx b/ts/components/services/SpecialServices/LegacySpecialServicesCTA.tsx deleted file mode 100644 index 9cda98014f0..00000000000 --- a/ts/components/services/SpecialServices/LegacySpecialServicesCTA.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import * as React from "react"; -import { constNull, pipe } from "fp-ts/lib/function"; -import * as B from "fp-ts/lib/boolean"; -import * as O from "fp-ts/lib/Option"; -import { ButtonSolid } from "@pagopa/io-app-design-system"; -import { ServiceId } from "../../../../definitions/backend/ServiceId"; -import { cdcEnabled } from "../../../config"; -import CdcServiceCTA from "../../../features/bonus/cdc/components/CdcServiceCTA"; -import LegacyCgnServiceCTA from "../../../features/bonus/cgn/components/LegacyCgnServiceCTA"; -import LegacyPnServiceCTA from "../../../features/pn/components/LegacyServiceCTA"; -import I18n from "../../../i18n"; -import { useIOSelector } from "../../../store/hooks"; -import { - isCdcEnabledSelector, - isCGNEnabledSelector, - isPnEnabledSelector, - isPnSupportedSelector -} from "../../../store/reducers/backendStatus"; -import { openAppStoreUrl } from "../../../utils/url"; - -type SpecialServiceConfig = { - isEnabled: boolean; - isSupported: boolean; -}; - -type Props = { - customSpecialFlowOpt?: string; - serviceId: ServiceId; - activate?: boolean; -}; - -const UpdateAppCTA = () => { - // utility to open the app store on the OS - const openAppStore = React.useCallback(() => openAppStoreUrl(), []); - - return ( - - ); -}; - -const renderCta = ( - isEnabled: boolean, - isSupported: boolean, - cta: JSX.Element -) => - pipe( - isEnabled, - B.fold(constNull, () => - pipe( - isSupported, - B.fold( - () => , - () => cta - ) - ) - ) - ); - -const LegacySpecialServicesCTA = (props: Props) => { - const { customSpecialFlowOpt } = props; - - const isCGNEnabled = useIOSelector(isCGNEnabledSelector); - const cdcEnabledSelector = useIOSelector(isCdcEnabledSelector); - - const isCdcEnabled = cdcEnabledSelector && cdcEnabled; - - const isPnEnabled = useIOSelector(isPnEnabledSelector); - const isPnSupported = useIOSelector(isPnSupportedSelector); - - const mapSpecialServiceConfig = new Map([ - ["cgn", { isEnabled: isCGNEnabled, isSupported: true }], - ["cdc", { isEnabled: isCdcEnabled, isSupported: true }], - ["pn", { isEnabled: isPnEnabled, isSupported: isPnSupported }] - ]); - - return pipe( - customSpecialFlowOpt, - O.fromNullable, - O.fold(constNull, csf => - pipe( - mapSpecialServiceConfig.get(csf), - O.fromNullable, - O.fold( - () => , - ({ isEnabled, isSupported }) => { - switch (csf) { - case "cgn": - return renderCta( - isEnabled, - isSupported, - - ); - case "cdc": - return renderCta(isEnabled, isSupported, ); - case "pn": - return renderCta( - isEnabled, - isSupported, - - ); - default: - return ; - } - } - ) - ) - ) - ); -}; - -export default LegacySpecialServicesCTA; diff --git a/ts/components/services/TosAndPrivacyBox.tsx b/ts/components/services/TosAndPrivacyBox.tsx deleted file mode 100644 index 3beb2e53496..00000000000 --- a/ts/components/services/TosAndPrivacyBox.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import * as React from "react"; -import { View } from "react-native"; - -import LinkRow from "./LinkRow"; -import SectionHeader from "./SectionHeader"; - -type Props = { - privacyUrl?: string; - tosUrl?: string; -}; - -/** - * Renders a dedicated section with TOS, Privacy urls, and the header. - * It **doesn't render** if both links are not defined! - */ -const TosAndPrivacy: React.FC = ({ tosUrl, privacyUrl }) => { - if (tosUrl === undefined && privacyUrl === undefined) { - return null; - } - return ( - - - {tosUrl && } - {privacyUrl && } - - ); -}; - -export default TosAndPrivacy; diff --git a/ts/components/services/__tests__/LinkRow.test.tsx b/ts/components/services/__tests__/LinkRow.test.tsx deleted file mode 100644 index dae0e1b439f..00000000000 --- a/ts/components/services/__tests__/LinkRow.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react-native"; - -import LinkRow from "../LinkRow"; - -describe("LinkRow component", () => { - it("should match the snapshot", () => { - const component = render( - - ); - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/ts/components/services/__tests__/SectionHeader.test.tsx b/ts/components/services/__tests__/SectionHeader.test.tsx deleted file mode 100644 index 6d62e9fb5b7..00000000000 --- a/ts/components/services/__tests__/SectionHeader.test.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React from "react"; -import { render } from "@testing-library/react-native"; - -import SectionHeader from "../SectionHeader"; - -describe("SectionHeader component", () => { - it("should match the snapshot", () => { - const component = render( - - ); - expect(component.toJSON()).toMatchSnapshot(); - }); -}); diff --git a/ts/components/services/__tests__/TosAndPrivacyBox.test.tsx b/ts/components/services/__tests__/TosAndPrivacyBox.test.tsx deleted file mode 100644 index e37277a0504..00000000000 --- a/ts/components/services/__tests__/TosAndPrivacyBox.test.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { fireEvent, render } from "@testing-library/react-native"; -import React from "react"; - -import { IOToast } from "@pagopa/io-app-design-system"; -import I18n from "../../../i18n"; -import TosAndPrivacyBox from "../TosAndPrivacyBox"; - -// eslint-disable-next-line functional/no-let -let MOCK_URL_WILL_FAIL = false; - -const mockOpenWebUrl = jest.fn(); - -jest.mock("../../../utils/url", () => ({ - openWebUrl: (_: string, onError: () => void) => { - mockOpenWebUrl(); - // we rely on an internal of `openWebUrl`, this might be improved? - if (MOCK_URL_WILL_FAIL) { - onError(); - } - } -})); - -const options = { - tosUrl: "https://www.fsf.org/", - privacyUrl: "https://gnupg.org/" -}; - -describe("TosAndPrivacyBox component", () => { - beforeEach(() => { - mockOpenWebUrl.mockReset(); - MOCK_URL_WILL_FAIL = false; - }); - - it("should have one header", () => { - const component = renderComponent({ ...options }); - expect(component.getByRole("header")).toBeDefined(); - expect(component.getByText(I18n.t("services.tosAndPrivacy"))).toBeDefined(); - }); - - describe("when both URLs are defined", () => { - it("should call `openWebUrl` for TOS link", () => { - const component = renderComponent(options); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const link = component - .getAllByRole("link") - .find(item => item.children[0] === I18n.t("services.tosLink"))!; - fireEvent(link, "onPress"); - expect(mockOpenWebUrl).toHaveBeenCalledTimes(1); - }); - - it("should call `openWebUrl` for Privacy link", () => { - const component = renderComponent(options); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const link = component - .getAllByRole("link") - .find(item => item.children[0] === I18n.t("services.privacyLink"))!; - fireEvent(link, "onPress"); - expect(mockOpenWebUrl).toHaveBeenCalledTimes(1); - }); - - it("should call `showToast` when then link fails", () => { - MOCK_URL_WILL_FAIL = true; - const component = renderComponent(options); - const showToastSpy = jest.spyOn(IOToast, "error"); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const link = component - .getAllByRole("link") - .find(item => item.children[0] === I18n.t("services.privacyLink"))!; - fireEvent(link, "onPress"); - expect(showToastSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe("when either URL is not defined", () => { - it("should not render TOS link", () => { - expect( - renderComponent({ ...options, tosUrl: undefined }) - .getAllByRole("link") - .find(item => item.children[0] === I18n.t("services.tosLink")) - ).toBeUndefined(); - }); - - it("should not render Privacy link", () => { - expect( - renderComponent({ ...options, privacyUrl: undefined }) - .getAllByRole("link") - .find(item => item.children[0] === I18n.t("services.privacyLink")) - ).toBeUndefined(); - }); - }); - - describe("when neither URL is defined", () => { - it("should not render anything", () => { - expect(renderComponent({}).toJSON()).toEqual(null); - }); - }); -}); - -function renderComponent({ - tosUrl, - privacyUrl -}: Parameters[0]) { - return render(); -} diff --git a/ts/components/services/__tests__/__snapshots__/LinkRow.test.tsx.snap b/ts/components/services/__tests__/__snapshots__/LinkRow.test.tsx.snap deleted file mode 100644 index aae05bcac23..00000000000 --- a/ts/components/services/__tests__/__snapshots__/LinkRow.test.tsx.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LinkRow component should match the snapshot 1`] = ` -Array [ - - Email - , - , -] -`; diff --git a/ts/components/services/__tests__/__snapshots__/SectionHeader.test.tsx.snap b/ts/components/services/__tests__/__snapshots__/SectionHeader.test.tsx.snap deleted file mode 100644 index 792bd226546..00000000000 --- a/ts/components/services/__tests__/__snapshots__/SectionHeader.test.tsx.snap +++ /dev/null @@ -1,110 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`SectionHeader component should match the snapshot 1`] = ` - - - - - - - - - ID - - -`; diff --git a/ts/components/ui/LightModal.tsx b/ts/components/ui/LightModal.tsx index f674549dfb1..3c44343f393 100644 --- a/ts/components/ui/LightModal.tsx +++ b/ts/components/ui/LightModal.tsx @@ -134,7 +134,10 @@ export type AnimationLightModal = export const LightModalConsumer = LightModalContext.Consumer; -export class LightModalProvider extends React.Component { +export class LightModalProvider extends React.Component< + React.PropsWithChildren, + State +> { public showAnimatedModal = async ( childComponent: React.ReactNode, styledAnimation: AnimationLightModal = RightLeftAnimation @@ -230,6 +233,6 @@ export class LightModalProvider extends React.Component { } } -export const LightModalRoot: React.SFC = () => ( +export const LightModalRoot = () => ( {({ component }) => component} ); diff --git a/ts/components/ui/LoadingIndicator.tsx b/ts/components/ui/LoadingIndicator.tsx index 857bb04ac61..944ee3628a4 100644 --- a/ts/components/ui/LoadingIndicator.tsx +++ b/ts/components/ui/LoadingIndicator.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import { LoadingSpinner } from "@pagopa/io-app-design-system"; -import I18n from "i18n-js"; +import I18n from "../../i18n"; import { WithTestID } from "../../types/WithTestID"; import { useInteractiveElementDefaultColor } from "../../utils/hooks/theme"; diff --git a/ts/components/ui/Markdown/Markdown.tsx b/ts/components/ui/Markdown/Markdown.tsx index 85077568d73..89983dba5c0 100644 --- a/ts/components/ui/Markdown/Markdown.tsx +++ b/ts/components/ui/Markdown/Markdown.tsx @@ -12,6 +12,7 @@ import { ViewStyle } from "react-native"; import WebView from "react-native-webview"; +import { filterXSS } from "xss"; import { closeInjectedScript } from "../../../utils/webview"; import { remarkProcessor } from "../../../utils/markdown"; import { MarkdownWebviewComponent } from "./MarkdownWebviewComponent"; @@ -108,7 +109,7 @@ export const Markdown = (props: MarkdownProps) => { // 'true' value but the underlying MarkdownWebviewComponent does not // reload its content (the html is recompiled but it does not change), // thus, not calling the `handleLoadEnd` callback - const html = generateHtml( + const generatedHtml = generateHtml( String(file), cssStyle, useCustomSortedList, @@ -118,8 +119,8 @@ export const Markdown = (props: MarkdownProps) => { ...currentInternalState, isLoading: currentInternalState.isLoading && - currentInternalState.html !== html, - html + currentInternalState.html !== generatedHtml, + html: generatedHtml })); } ); diff --git a/ts/components/ui/TabNavigation.tsx b/ts/components/ui/TabNavigation.tsx deleted file mode 100644 index 0b5b546ab1f..00000000000 --- a/ts/components/ui/TabNavigation.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import React from "react"; -import { FlexStyle, LayoutChangeEvent, StyleSheet, View } from "react-native"; -import { ScrollView } from "react-native-gesture-handler"; -import { TabItem } from "./TabItem"; - -export type TabNavigationItem = Omit< - TabItem, - "onPress" | "color" | "selected" | "accessibilityLabel" | "accessibilityHint" ->; - -type TabNavigationChildren = - | React.ReactElement - | Array>; - -type TabAlignment = "start" | "center" | "end" | "stretch"; - -type TabNavigation = { - // Configuration - color?: TabItem["color"]; - selectedIndex?: number; - tabAlignment?: TabAlignment; - // Events - onItemPress?: (index: number) => void; - // Tabs - children: TabNavigationChildren; -}; - -const itemsJustify: Record = { - start: "flex-start", - center: "center", - end: "flex-end", - stretch: "space-between" -}; - -const TabNavigation = ({ - color = "light", - selectedIndex: forceSelectedIndex, - tabAlignment = "center", - onItemPress, - children -}: TabNavigation) => { - const [itemMinWidth, setItemMinWidth] = React.useState(0); - const [selectedIndex, setSelectedIndex] = React.useState( - forceSelectedIndex ?? 0 - ); - - const handleItemPress = (index: number) => { - setSelectedIndex(forceSelectedIndex ?? index); - onItemPress?.(index); - }; - - const handleItemOnLayout = (event: LayoutChangeEvent) => { - const { width } = event.nativeEvent.layout; - setItemMinWidth(current => Math.max(current, width)); - }; - - const stretchItems = tabAlignment === "stretch"; - - const wrapChild = (child: React.ReactElement, index: number = 0) => ( - - {React.cloneElement(child, { - onPress: event => { - child.props.onPress?.(event); - handleItemPress(index); - }, - selected: selectedIndex === index, - color - })} - - ); - - return ( - - {Array.isArray(children) ? children.map(wrapChild) : wrapChild(children)} - - ); -}; - -const styles = StyleSheet.create({ - container: { - flexGrow: 1, - paddingHorizontal: 24 - }, - item: { - flexGrow: 0, - flexShrink: 1, - flexBasis: 100, - alignItems: "center" - } -}); - -export { TabNavigation }; diff --git a/ts/components/ui/__test__/__snapshots__/BoxedRefreshIndicator.test.tsx.snap b/ts/components/ui/__test__/__snapshots__/BoxedRefreshIndicator.test.tsx.snap index 88bd59945c9..352e4530e95 100644 --- a/ts/components/ui/__test__/__snapshots__/BoxedRefreshIndicator.test.tsx.snap +++ b/ts/components/ui/__test__/__snapshots__/BoxedRefreshIndicator.test.tsx.snap @@ -3,14 +3,14 @@ exports[`BoxedRefreshIndicator Should match all-properties snapshot 1`] = ` { const animationScaleValue = IOScaleValues?.basicButton?.pressedState; // Interpolate animation values from `isPressed` values - // eslint-disable-next-line sonarjs/no-identical-functions const animatedScaleStyle = useAnimatedStyle(() => { const scale = interpolate( progress.value, diff --git a/ts/components/wallet/PaymentBannerComponent.tsx b/ts/components/wallet/PaymentBannerComponent.tsx index 71c123f0ebc..2d28a92e942 100644 --- a/ts/components/wallet/PaymentBannerComponent.tsx +++ b/ts/components/wallet/PaymentBannerComponent.tsx @@ -29,7 +29,7 @@ const styles = StyleSheet.create({ * Used for the screens from the identification of the transaction to the end of the procedure. * Fee is shown only when a method screen is selected */ -const PaymentBannerComponent: React.SFC = props => { +const PaymentBannerComponent = (props: Props) => { const totalAmount = pipe( props.fee, O.fromNullable, diff --git a/ts/components/wallet/PaymentHistoryItem.tsx b/ts/components/wallet/PaymentHistoryItem.tsx index 7557d61261e..698a1715430 100644 --- a/ts/components/wallet/PaymentHistoryItem.tsx +++ b/ts/components/wallet/PaymentHistoryItem.tsx @@ -39,7 +39,9 @@ const styles = StyleSheet.create({ } }); -export default class PaymentHistoryItem extends React.PureComponent { +export default class PaymentHistoryItem extends React.PureComponent< + React.PropsWithChildren +> { public render() { return ( void; - ListEmptyComponent?: React.ReactNode; + ListEmptyComponent?: React.JSX.Element; }>; const styles = StyleSheet.create({ diff --git a/ts/components/wallet/WalletLayout.tsx b/ts/components/wallet/WalletLayout.tsx index 8f39aa0a42f..64bc93c0762 100644 --- a/ts/components/wallet/WalletLayout.tsx +++ b/ts/components/wallet/WalletLayout.tsx @@ -47,7 +47,9 @@ type Props = Readonly<{ referenceToContentScreen?: React.RefObject; }>; -export default class WalletLayout extends React.Component { +export default class WalletLayout extends React.Component< + React.PropsWithChildren +> { public render(): React.ReactNode { const { title, diff --git a/ts/components/wallet/card/CardComponent.tsx b/ts/components/wallet/card/CardComponent.tsx index 1dbe1b0fa8b..a8383c68db5 100644 --- a/ts/components/wallet/card/CardComponent.tsx +++ b/ts/components/wallet/card/CardComponent.tsx @@ -3,19 +3,13 @@ * with different appearences based on * the props passed */ -import { HSpacer, IOColors, Icon, VSpacer } from "@pagopa/io-app-design-system"; +import { IOColors, Icon, VSpacer } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; import * as E from "fp-ts/lib/Either"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import * as React from "react"; -import { Alert, Image, Platform, StyleSheet, View } from "react-native"; -import { - Menu, - MenuOption, - MenuOptions, - MenuTrigger -} from "react-native-popup-menu"; +import { Image, Platform, StyleSheet, View } from "react-native"; import { BlurredPan } from "../../../features/wallet/component/card/BlurredPan"; import I18n from "../../../i18n"; import { CreditCard, CreditCardType, Wallet } from "../../../types/pagopa"; @@ -26,7 +20,6 @@ import { FOUR_UNICODE_CIRCLES } from "../../../utils/wallet"; import TouchableDefaultOpacity from "../../TouchableDefaultOpacity"; import { Body } from "../../core/typography/Body"; import { H5 } from "../../core/typography/H5"; -import { Label } from "../../core/typography/Label"; import Logo, { cardIcons } from "./Logo"; interface BaseProps { @@ -36,7 +29,6 @@ interface BaseProps { interface FullCommonProps extends BaseProps { isFavorite?: pot.Pot; onSetFavorite?: (willBeFavorite: boolean) => void; - hideMenu?: boolean; extraSpace?: boolean; hideFavoriteIcon?: boolean; onDelete?: () => void; @@ -120,27 +112,6 @@ const styles = StyleSheet.create({ * @deprecated Use {@link BaseCardComponent} and related custom implementation (eg: {@link CreditCardComponent}) */ export default class CardComponent extends React.Component { - private handleDeleteSelect = () => - Alert.alert( - I18n.t("cardComponent.deleteTitle"), - I18n.t("cardComponent.deleteMsg"), - [ - { - text: I18n.t("global.buttons.cancel"), - style: "cancel" - }, - { - text: I18n.t("global.buttons.ok"), - style: "destructive", - onPress: - this.props.type === "Full" || this.props.type === "Header" - ? this.props.onDelete - : undefined - } - ], - { cancelable: false } - ); - private handleFavoritePress = () => { if ( (this.props.type === "Full" || this.props.type === "Header") && @@ -164,13 +135,7 @@ export default class CardComponent extends React.Component { } if (this.props.type === "Header") { - const { - hideFavoriteIcon, - isFavorite, - onSetFavorite, - onDelete, - hideMenu - } = this.props; + const { hideFavoriteIcon, isFavorite } = this.props; return ( @@ -186,45 +151,6 @@ export default class CardComponent extends React.Component { /> )} - - {!hideMenu && ( - - - - - - - - {onSetFavorite && isFavorite !== undefined && ( - - - - )} - - {onDelete && ( - - - - )} - - - )} ); } diff --git a/ts/components/wallet/card/Logo.tsx b/ts/components/wallet/card/Logo.tsx index 1f9d6bea05f..a9db17dad56 100644 --- a/ts/components/wallet/card/Logo.tsx +++ b/ts/components/wallet/card/Logo.tsx @@ -67,7 +67,7 @@ type Props = Readonly<{ pspLogo?: string; }>; -const Logo: React.SFC = props => { +const Logo = (props: Props) => { const getSource = () => { if (props.pspLogo && props.pspLogo.trim().length > 0) { return { uri: props.pspLogo }; diff --git a/ts/config.ts b/ts/config.ts index ebdd49c6fe9..174c24247fa 100644 --- a/ts/config.ts +++ b/ts/config.ts @@ -36,9 +36,6 @@ const DEFAULT_FAST_LOGIN_MAX_RETRIES = 3; // Default number of workers to fetch message. const DEFAULT_TOT_MESSAGE_FETCH_WORKERS = 5; -// Default number of workers to fetch service. -const DEFAULT_TOT_SERVICE_FETCH_WORKERS = 5; - // TODO: calculate the page size based on available screen space and item's height // https://pagopa.atlassian.net/browse/IA-474 const DEFAULT_PAGE_SIZE = 12; @@ -151,12 +148,6 @@ export const totMessageFetchWorkers = pipe( E.getOrElse(() => DEFAULT_TOT_MESSAGE_FETCH_WORKERS) ); -export const totServiceFetchWorkers = pipe( - parseInt(Config.TOT_SERVICE_FETCH_WORKERS, 10), - t.Integer.decode, - E.getOrElse(() => DEFAULT_TOT_SERVICE_FETCH_WORKERS) -); - export const shufflePinPadOnPayment = Config.SHUFFLE_PINPAD_ON_PAYMENT === "YES"; @@ -172,12 +163,6 @@ export const zendeskPrivacyUrl: string = pipe( E.getOrElse(() => "https://www.pagopa.it/it/privacy-policy-assistenza/") ); -export const localServicesWebUrl: string = pipe( - Config.LOCAL_SERVICE_WEB_URL, - t.string.decode, - E.getOrElse(() => "https://io.italia.it") -); - export const unsupportedDeviceMoreInfoUrl: string = pipe( Config.UNSUPPORTED_DEVICE_MORE_INFO_URL, NonEmptyString.decode, diff --git a/ts/features/barcode/components/BarcodeScanBaseScreenComponent.tsx b/ts/features/barcode/components/BarcodeScanBaseScreenComponent.tsx index ac9207f2e0f..0772fc1a6c7 100644 --- a/ts/features/barcode/components/BarcodeScanBaseScreenComponent.tsx +++ b/ts/features/barcode/components/BarcodeScanBaseScreenComponent.tsx @@ -11,9 +11,12 @@ import { useRoute } from "@react-navigation/native"; import React from "react"; -import { Platform, SafeAreaView, StyleSheet, View } from "react-native"; +import { StyleSheet, View } from "react-native"; import LinearGradient from "react-native-linear-gradient"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { + SafeAreaView, + useSafeAreaInsets +} from "react-native-safe-area-context"; import { ToolEnum } from "../../../../definitions/content/AssistanceToolConfig"; import { BaseHeader } from "../../../components/screens/BaseHeader"; import { @@ -302,12 +305,13 @@ const BarcodeScanBaseScreenComponent = ({ colors={["#03134480", "#03134400"]} style={styles.headerContainer} > - + + {/* This overrides BaseHeader status bar configuration */} + {/* FIXME: replace with new header */} - {/* This overrides BaseHeader status bar configuration */} - diff --git a/ts/features/barcode/hooks/useIOBarcodeCameraScanner.tsx b/ts/features/barcode/hooks/useIOBarcodeCameraScanner.tsx index 85a1f433eb2..aeef4e41319 100644 --- a/ts/features/barcode/hooks/useIOBarcodeCameraScanner.tsx +++ b/ts/features/barcode/hooks/useIOBarcodeCameraScanner.tsx @@ -154,10 +154,7 @@ export const retrieveNextBarcode = ( ), O.of, O.map( - barcodes => - barcodes[BarcodeFormat.QR_CODE] || - barcodes[BarcodeFormat.DATA_MATRIX] || - null + b => b[BarcodeFormat.QR_CODE] || b[BarcodeFormat.DATA_MATRIX] || null ), O.chain(O.fromNullable) ); @@ -271,9 +268,9 @@ export const useIOBarcodeCameraScanner = ({ * Handles the scanned barcodes and calls the callbacks for the results */ const handleScannedBarcodes = React.useCallback( - (barcodes: Array) => + (codes: Array) => pipe( - retrieveNextBarcode(barcodes), + retrieveNextBarcode(codes), O.map(detectedBarcode => { // After a scan (even if not successful) the decoding is disabled for a while // to avoid multiple scans of the same barcode diff --git a/ts/features/barcode/utils/imageDecodingTask.ts b/ts/features/barcode/utils/imageDecodingTask.ts index e767d7a6cb1..6f1b04622c9 100644 --- a/ts/features/barcode/utils/imageDecodingTask.ts +++ b/ts/features/barcode/utils/imageDecodingTask.ts @@ -1,4 +1,3 @@ -/* eslint-disable sonarjs/no-identical-functions */ import { sequenceS } from "fp-ts/lib/Apply"; import * as A from "fp-ts/lib/Array"; import * as O from "fp-ts/lib/Option"; diff --git a/ts/features/bonus/cgn/components/CgnServiceCTA.tsx b/ts/features/bonus/cgn/components/CgnServiceCTA.tsx index ba10902f8cf..131839402a4 100644 --- a/ts/features/bonus/cgn/components/CgnServiceCTA.tsx +++ b/ts/features/bonus/cgn/components/CgnServiceCTA.tsx @@ -6,9 +6,9 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import I18n from "../../../../i18n"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { - servicePreferenceResponseSuccessSelector, - servicePreferenceSelector -} from "../../../services/details/store/reducers/servicePreference"; + servicePreferencePotSelector, + servicePreferenceResponseSuccessSelector +} from "../../../services/details/store/reducers"; import { ServiceId } from "../../../../../definitions/backend/ServiceId"; import { cgnActivationStart } from "../store/actions/activation"; import { cgnUnsubscribe } from "../store/actions/unsubscribe"; @@ -31,7 +31,7 @@ export const CgnServiceCta = ({ serviceId }: CgnServiceCtaProps) => { servicePreferenceResponseSuccessSelector ); - const servicePreferencePot = useIOSelector(servicePreferenceSelector); + const servicePreferencePot = useIOSelector(servicePreferencePotSelector); const unsubscriptionStatus = useIOSelector(cgnUnsubscribeSelector); diff --git a/ts/features/bonus/cgn/components/LegacyCgnServiceCTA.tsx b/ts/features/bonus/cgn/components/LegacyCgnServiceCTA.tsx index 810dd4b461d..9beb95491ad 100644 --- a/ts/features/bonus/cgn/components/LegacyCgnServiceCTA.tsx +++ b/ts/features/bonus/cgn/components/LegacyCgnServiceCTA.tsx @@ -10,7 +10,7 @@ import { } from "@pagopa/io-app-design-system"; import I18n from "../../../../i18n"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; -import { servicePreferenceSelector } from "../../../services/details/store/reducers/servicePreference"; +import { servicePreferencePotSelector } from "../../../services/details/store/reducers"; import { isServicePreferenceResponseSuccess } from "../../../services/details/types/ServicePreferenceResponse"; import { ServiceId } from "../../../../../definitions/backend/ServiceId"; import { cgnActivationStart } from "../store/actions/activation"; @@ -27,10 +27,10 @@ type Props = { const LegacyCgnServiceCTA = (props: Props) => { const isFirstRender = useRef(true); const dispatch = useIODispatch(); - const servicePreference = useIOSelector(servicePreferenceSelector); + const servicePreferencePot = useIOSelector(servicePreferencePotSelector); const unsubscriptionStatus = useIOSelector(cgnUnsubscribeSelector); - const servicePreferenceValue = pot.getOrElse(servicePreference, undefined); + const servicePreferenceValue = pot.getOrElse(servicePreferencePot, undefined); useEffect(() => { if (!isFirstRender.current) { diff --git a/ts/features/bonus/cgn/components/__test__/__snapshots__/CgnCard.test.tsx.snap b/ts/features/bonus/cgn/components/__test__/__snapshots__/CgnCard.test.tsx.snap index 0235284693a..e4022ace400 100644 --- a/ts/features/bonus/cgn/components/__test__/__snapshots__/CgnCard.test.tsx.snap +++ b/ts/features/bonus/cgn/components/__test__/__snapshots__/CgnCard.test.tsx.snap @@ -3,21 +3,21 @@ exports[`CgnCard should match the snapshot 1`] = ` { /> ); - const renderComponentEycaStatus = (eyca: EycaCard): React.ReactNode => { + const renderComponentEycaStatus = ( + eyca: EycaCard + ): React.JSX.Element | null => { switch (eyca.status) { case "ACTIVATED": case "REVOKED": diff --git a/ts/features/bonus/cgn/components/merchants/__test__/__snapshots__/CgnDiscountDetail.test.tsx.snap b/ts/features/bonus/cgn/components/merchants/__test__/__snapshots__/CgnDiscountDetail.test.tsx.snap index b4d2bfea444..9f0509220a9 100644 --- a/ts/features/bonus/cgn/components/merchants/__test__/__snapshots__/CgnDiscountDetail.test.tsx.snap +++ b/ts/features/bonus/cgn/components/merchants/__test__/__snapshots__/CgnDiscountDetail.test.tsx.snap @@ -3,11 +3,11 @@ exports[`when rendering on match snapshot for OTP discount 1`] = ` , ( )}`} - { testID={"CGNCardDetailsScrollView"} > {props.cgnDetails && ( <> diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantLandingWebview.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantLandingWebview.tsx index 880c67412c2..f74e1e31c6a 100644 --- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantLandingWebview.tsx +++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantLandingWebview.tsx @@ -1,7 +1,7 @@ import { Route, useRoute } from "@react-navigation/core"; import * as React from "react"; import { SafeAreaView } from "react-native"; -import I18n from "i18n-js"; +import I18n from "../../../../../i18n"; import { IOStyles } from "../../../../../components/core/variables/IOStyles"; import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; import WebviewComponent from "../../../../../components/WebviewComponent"; diff --git a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx index a343608b10e..e266159a99f 100644 --- a/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx +++ b/ts/features/bonus/cgn/screens/merchants/CgnMerchantsListByCategory.tsx @@ -204,7 +204,6 @@ const CgnMerchantsListByCategory = () => { = (_: Props) => { isSearchAvailable={{ enabled: true, onSearchTap: openFiltersModal }} > - {/* TABS component should be replaced with MaterialTopTab Navigator as done in Services and Messages home */} + {/* TABS component should be replaced with MaterialTopTab Navigator as done in Messages home */} {/* = (_: Props) => { heading={I18n.t("bonus.cgn.merchantsList.places")} > - TODO PLACEHOLDER HERE GOES THE MAP + TODO PLACEHOLDER HERE GOES THE MAP

{`${I18n.t("bonus.cgn.merchantsList.places")} TAB`}

diff --git a/ts/features/bonus/common/components/ProgressBar.tsx b/ts/features/bonus/common/components/ProgressBar.tsx index 495bc8e107c..83ccfbf05b1 100644 --- a/ts/features/bonus/common/components/ProgressBar.tsx +++ b/ts/features/bonus/common/components/ProgressBar.tsx @@ -1,6 +1,6 @@ import { IOColors } from "@pagopa/io-app-design-system"; import * as React from "react"; -import { View, StyleSheet } from "react-native"; +import { View, StyleSheet, DimensionValue } from "react-native"; type Props = { // between 0 and 1 @@ -23,7 +23,9 @@ const styles = StyleSheet.create({ * the percentage is set to 1 * @param progressPercentage */ -const calculateStylePercentage = (progressPercentage: number) => { +const calculateStylePercentage = ( + progressPercentage: number +): DimensionValue => { // clamp between 0 and 100 to avoid over/under flow const percentageValue = Math.max(Math.min(progressPercentage * 100, 100), 0); return `${percentageValue === 0 ? 1 : percentageValue}%`; @@ -40,7 +42,9 @@ export const ProgressBar: React.FunctionComponent = props => ( testID={"progressBar"} style={[ styles.fillBar, - { width: calculateStylePercentage(props.progressPercentage) } + { + width: calculateStylePercentage(props.progressPercentage) + } ]} />
diff --git a/ts/features/bonus/common/screens/AvailableBonusScreen.tsx b/ts/features/bonus/common/screens/AvailableBonusScreen.tsx index 62465364bee..f9b6665cc16 100644 --- a/ts/features/bonus/common/screens/AvailableBonusScreen.tsx +++ b/ts/features/bonus/common/screens/AvailableBonusScreen.tsx @@ -16,7 +16,6 @@ import { } from "react-native"; import { connect } from "react-redux"; import { ServiceId } from "../../../../../definitions/backend/ServiceId"; -import { ServicePublic } from "../../../../../definitions/backend/ServicePublic"; import { BonusAvailable } from "../../../../../definitions/content/BonusAvailable"; import { IOStyles } from "../../../../components/core/variables/IOStyles"; import BaseScreenComponent, { @@ -28,7 +27,6 @@ import { navigateBack, navigateToServiceDetailsScreen } from "../../../../store/actions/navigation"; -import { showServiceDetails } from "../../../../store/actions/services"; import { Dispatch } from "../../../../store/actions/types"; import { isCGNEnabledSelector, @@ -113,12 +111,10 @@ class AvailableBonusScreen extends React.PureComponent { // TODO: add mixpanel tracking and alert: https://pagopa.atlassian.net/browse/AP-14 IOToast.show(I18n.t("bonus.cdc.serviceEntryPoint.notAvailable")); }, - s => () => { - this.props.showServiceDetails(s); + s => () => this.props.navigateToServiceDetailsScreen({ serviceId: s.service_id - }); - } + }) ) ); }); @@ -242,9 +238,7 @@ const mapDispatchToProps = (dispatch: Dispatch) => ({ navigateToServiceDetailsScreen(params), serviceDetailsLoad: (serviceId: ServiceId) => { dispatch(loadServiceDetail.request(serviceId)); - }, - showServiceDetails: (service: ServicePublic) => - dispatch(showServiceDetails(service)) + } }); const AvailableBonusScreenFC: React.FunctionComponent = ( diff --git a/ts/features/bonus/common/store/reducers/__tests__/availableBonusesTypes.test.ts b/ts/features/bonus/common/store/reducers/__tests__/availableBonusesTypes.test.ts index 9a02fcd16f8..b385f2aafbb 100644 --- a/ts/features/bonus/common/store/reducers/__tests__/availableBonusesTypes.test.ts +++ b/ts/features/bonus/common/store/reducers/__tests__/availableBonusesTypes.test.ts @@ -38,10 +38,11 @@ const mockBonus: BonusAvailable = { describe("availableBonusesTypes with FF enabled", () => { it("should return 1 bonus available", () => { - jest.spyOn(bonus, "mapBonusIdFeatureFlag").mockImplementation( - // eslint-disable-next-line sonarjs/no-identical-functions - () => new Map([[ID_CGN_TYPE, true]]) - ); + jest + .spyOn(bonus, "mapBonusIdFeatureFlag") + .mockImplementation( + () => new Map([[ID_CGN_TYPE, true]]) + ); expect( supportedAvailableBonusSelector.resultFunc( pot.some([...availableBonuses]) @@ -51,7 +52,6 @@ describe("availableBonusesTypes with FF enabled", () => { it("should return 2 bonuses available if an experimental bonus with no FF is available", () => { jest.spyOn(bonus, "mapBonusIdFeatureFlag").mockImplementation( - // eslint-disable-next-line sonarjs/no-identical-functions () => new Map([ [ID_CGN_TYPE, true], diff --git a/ts/features/bonus/common/store/selectors/index.ts b/ts/features/bonus/common/store/selectors/index.ts index 9f02c7c371f..42f040cd766 100644 --- a/ts/features/bonus/common/store/selectors/index.ts +++ b/ts/features/bonus/common/store/selectors/index.ts @@ -8,7 +8,7 @@ import { GlobalState } from "../../../../../store/reducers/types"; import { ServicePublic } from "../../../../../../definitions/backend/ServicePublic"; import { BonusVisibilityEnum } from "../../../../../../definitions/content/BonusVisibility"; -import { servicesByIdSelector } from "../../../../services/details/store/reducers/servicesById"; +import { servicesByIdSelector } from "../../../../services/details/store/reducers"; import { mapBonusIdFeatureFlag } from "../../utils"; import { AvailableBonusTypesState } from "../reducers/availableBonusesTypes"; diff --git a/ts/features/design-system/components/DesignSystemSection.tsx b/ts/features/design-system/components/DesignSystemSection.tsx index aad792fdc16..9eb5227d40d 100644 --- a/ts/features/design-system/components/DesignSystemSection.tsx +++ b/ts/features/design-system/components/DesignSystemSection.tsx @@ -11,11 +11,11 @@ const styles = StyleSheet.create({ } }); -type OwnProps = { +type OwnProps = React.PropsWithChildren<{ title: string; -}; +}>; -export const DesignSystemSection: React.FunctionComponent = props => ( +export const DesignSystemSection = (props: OwnProps) => (

{props.title}

{props.children} diff --git a/ts/features/design-system/core/DSButtons.tsx b/ts/features/design-system/core/DSButtons.tsx index 620b324a575..e35c8f6e0f9 100644 --- a/ts/features/design-system/core/DSButtons.tsx +++ b/ts/features/design-system/core/DSButtons.tsx @@ -37,7 +37,6 @@ const onButtonPress = () => { Alert.alert("Alert", "Action triggered"); }; -// eslint-disable-next-line complexity, sonarjs/cognitive-complexity export const DSButtons = () => { const { isExperimental } = useIOExperimentalDesign(); const theme = useIOTheme(); diff --git a/ts/features/design-system/core/DSColors.tsx b/ts/features/design-system/core/DSColors.tsx index 3cfec3e2da7..bbca2d6cc1e 100644 --- a/ts/features/design-system/core/DSColors.tsx +++ b/ts/features/design-system/core/DSColors.tsx @@ -361,7 +361,7 @@ const ColorBox = ({ : { backgroundColor: color } ]} > - {color && {color}} + {color && {color as string}}
{name && ( diff --git a/ts/features/design-system/core/DSFooterActionsSticky.tsx b/ts/features/design-system/core/DSFooterActionsSticky.tsx index 07f7980a8b1..0ac89daa612 100644 --- a/ts/features/design-system/core/DSFooterActionsSticky.tsx +++ b/ts/features/design-system/core/DSFooterActionsSticky.tsx @@ -70,7 +70,7 @@ export const DSFooterActionsSticky = () => { ); const actionBlockAnimatedStyle = useAnimatedStyle(() => ({ - /* + /* We only start translating the action block when it reaches the top of the placeholder 0 = Translate is blocked diff --git a/ts/features/design-system/core/DSGradientScroll.tsx b/ts/features/design-system/core/DSGradientScroll.tsx deleted file mode 100644 index 4c0e4bfaaff..00000000000 --- a/ts/features/design-system/core/DSGradientScroll.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react"; -import { Alert, View } from "react-native"; -import { - ButtonOutline, - GradientScrollView, - IOColors -} from "@pagopa/io-app-design-system"; -import { H2 } from "../../../components/core/typography/H2"; -import { Body } from "../../../components/core/typography/Body"; - -export const DSGradientScroll = () => ( - - Alert.alert("Primary action pressed! (⁠⁠ꈍ⁠ᴗ⁠ꈍ⁠)") - }} - > -

Start

- {[...Array(50)].map((_el, i) => ( - Repeated text - ))} - Alert.alert("Test button")} - /> - {[...Array(2)].map((_el, i) => ( - Repeated text - ))} -

End

-
-
-); diff --git a/ts/features/design-system/core/DSLegacyPictograms.tsx b/ts/features/design-system/core/DSLegacyPictograms.tsx index db4795a66fd..a24e04cd1d4 100644 --- a/ts/features/design-system/core/DSLegacyPictograms.tsx +++ b/ts/features/design-system/core/DSLegacyPictograms.tsx @@ -23,7 +23,6 @@ import Question from "../../../../img/pictograms/doubt.png"; import CompletedRaster from "../../../../img/pictograms/payment-completed.png"; import BeerMug from "../../../../img/search/beer-mug.png"; import Search from "../../../../img/search/search-icon.png"; -import Puzzle from "../../../../img/services/icon-loading-services.png"; import ABILogo from "../../../../img/wallet/cards-icons/abiLogoFallback.png"; import Umbrella from "../../../../img/wallet/errors/generic-error-icon.png"; import NotAvailable from "../../../../img/wallet/errors/payment-unavailable-icon.png"; @@ -119,11 +118,6 @@ export const DSLegacyPictograms = () => { name={"Search"} image={renderRasterImage(Search)} /> - { fontSize: 14 }, headerTitleAlign: "center", - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + headerStyle: { height: insets.top + IOVisualCostants.headerHeight }, headerLeft: RNNBackButton, headerMode: "screen" diff --git a/ts/features/euCovidCert/components/__test__/__snapshots__/EuCovidCertHeader.test.tsx.snap b/ts/features/euCovidCert/components/__test__/__snapshots__/EuCovidCertHeader.test.tsx.snap index b1d0d10d4ce..8dadfa1ddd3 100644 --- a/ts/features/euCovidCert/components/__test__/__snapshots__/EuCovidCertHeader.test.tsx.snap +++ b/ts/features/euCovidCert/components/__test__/__snapshots__/EuCovidCertHeader.test.tsx.snap @@ -3,7 +3,7 @@ exports[`EuCovidCertHeader it should match the snapshot 1`] = ` { 456789, // 567890, "345678" - ].forEach( - // eslint-disable-next-line sonarjs/no-identical-functions - input => { - it(`${input}`, () => { - expect(String(input).length).toBe(6); - expect(isValidPinNumber(input)).toBe(false); - }); - } - ); + ].forEach(input => { + it(`${input}`, () => { + expect(String(input).length).toBe(6); + expect(isValidPinNumber(input)).toBe(false); + }); + }); }); describe("returns false for descending sequences", () => { - // eslint-disable-next-line sonarjs/no-identical-functions [ 654321, 543210, diff --git a/ts/features/fci/components/LinkedText.tsx b/ts/features/fci/components/LinkedText.tsx index dcdcc524548..89486fc56e1 100644 --- a/ts/features/fci/components/LinkedText.tsx +++ b/ts/features/fci/components/LinkedText.tsx @@ -55,7 +55,7 @@ const LinkedText = (props: Props) => { text: string, onPress: (holder: string) => void ) => { - const parts = getMatchedLinks(text); + const innerParts = getMatchedLinks(text); // a function to render the link const getTextWithLinkComponent = (matched: string, index: number) => { @@ -74,7 +74,7 @@ const LinkedText = (props: Props) => { ); }; - return parts.map( + return innerParts.map( (part, index) => part && getTextWithLinkComponent(part, index) ); }; @@ -88,14 +88,14 @@ const LinkedText = (props: Props) => {

{textWithSeparator.split("$@").map((text, index) => O.isSome(O.fromNullable(arrayOfLinkedText[index])) ? ( - <> +
{text}
{arrayOfLinkedText[index]} - +
) : ( -

+
{text}
) diff --git a/ts/features/fci/components/__tests__/__snapshots__/DocumentsNavigationBar.test.tsx.snap b/ts/features/fci/components/__tests__/__snapshots__/DocumentsNavigationBar.test.tsx.snap index 6a7c4a7d6d5..43180a3f769 100644 --- a/ts/features/fci/components/__tests__/__snapshots__/DocumentsNavigationBar.test.tsx.snap +++ b/ts/features/fci/components/__tests__/__snapshots__/DocumentsNavigationBar.test.tsx.snap @@ -3,13 +3,13 @@ exports[`Test DocumentsNavigationBar component should render a DocumentsNavigationBar Component with props correctly 1`] = ` - - - - + + diff --git a/ts/features/fci/components/__tests__/__snapshots__/SignatureFieldItem.test.tsx.snap b/ts/features/fci/components/__tests__/__snapshots__/SignatureFieldItem.test.tsx.snap index b8cace47f01..54289052cfe 100644 --- a/ts/features/fci/components/__tests__/__snapshots__/SignatureFieldItem.test.tsx.snap +++ b/ts/features/fci/components/__tests__/__snapshots__/SignatureFieldItem.test.tsx.snap @@ -3,7 +3,7 @@ exports[`Test SignatureFieldItem component should render a SignatureFieldItem component with props correctly 1`] = ` { const dispatch = useIODispatch(); const fciServiceId = useIOSelector(fciMetadataServiceIdSelector); - const servicePreference = useIOSelector(servicePreferenceSelector); + const servicePreferencePot = useIOSelector(servicePreferencePotSelector); const fciEnvironment = useIOSelector(fciEnvironmentSelector); - const servicePreferenceValue = pot.getOrElse(servicePreference, undefined); + const servicePreferenceValue = pot.getOrElse(servicePreferencePot, undefined); const cancelButtonProps: ButtonSolidProps = { onPress: () => { dispatch(fciStartSigningRequest()); diff --git a/ts/features/fci/screens/valid/FciQtspClausesScreen.tsx b/ts/features/fci/screens/valid/FciQtspClausesScreen.tsx index cc1026b6ec7..8154a29627b 100644 --- a/ts/features/fci/screens/valid/FciQtspClausesScreen.tsx +++ b/ts/features/fci/screens/valid/FciQtspClausesScreen.tsx @@ -29,7 +29,7 @@ import { } from "../../store/reducers/fciPollFilledDocument"; import GenericErrorComponent from "../../components/GenericErrorComponent"; import LinkedText from "../../components/LinkedText"; -import { servicePreferenceSelector } from "../../../services/details/store/reducers/servicePreference"; +import { servicePreferencePotSelector } from "../../../services/details/store/reducers"; import { loadServicePreference } from "../../../services/details/store/actions/preference"; import { ServiceId } from "../../../../../definitions/backend/ServiceId"; import { useFciCheckService } from "../../hooks/useFciCheckService"; @@ -45,7 +45,7 @@ const FciQtspClausesScreen = () => { const dispatch = useIODispatch(); const navigation = useIONavigation(); const [clausesChecked, setClausesChecked] = React.useState(0); - const servicePreference = useIOSelector(servicePreferenceSelector); + const servicePreferencePot = useIOSelector(servicePreferencePotSelector); const qtspClausesSelector = useIOSelector(fciQtspClausesSelector); const qtspPrivacyTextSelector = useIOSelector(fciQtspPrivacyTextSelector); const qtspPrivacyUrlSelector = useIOSelector(fciQtspPrivacyUrlSelector); @@ -58,7 +58,7 @@ const FciQtspClausesScreen = () => { const fciServiceId = useIOSelector(fciMetadataServiceIdSelector); const fciEnvironment = useIOSelector(fciEnvironmentSelector); - const servicePreferenceValue = pot.getOrElse(servicePreference, undefined); + const servicePreferenceValue = pot.getOrElse(servicePreferencePot, undefined); const isServiceActive = servicePreferenceValue && diff --git a/ts/features/fci/screens/valid/FciSignatureFieldsScreen.tsx b/ts/features/fci/screens/valid/FciSignatureFieldsScreen.tsx index 0baff633782..0af32f420db 100644 --- a/ts/features/fci/screens/valid/FciSignatureFieldsScreen.tsx +++ b/ts/features/fci/screens/valid/FciSignatureFieldsScreen.tsx @@ -80,14 +80,21 @@ const FciSignatureFieldsScreen = () => { const { showModal, hideModal } = React.useContext(LightModalContext); // get signatureFields for the current document - const docSignatures = pipe( - documentsSignaturesSelector, - RA.findFirst(doc => doc.document_id === docId) + const docSignatures = React.useMemo( + () => + pipe( + documentsSignaturesSelector, + RA.findFirst(doc => doc.document_id === docId) + ), + [docId, documentsSignaturesSelector] ); // get required signatureFields for the current document // that user should check to sign the document - const requiredFields = getRequiredSignatureFields(signatureFieldsSelector); + const requiredFields = React.useMemo( + () => getRequiredSignatureFields(signatureFieldsSelector), + [signatureFieldsSelector] + ); React.useEffect(() => { // get the required signature fields for the current document, @@ -107,6 +114,7 @@ const FciSignatureFieldsScreen = () => { ); setIsClausesChecked(res.length >= requiredFields.length); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [docSignatures]); const { present, bottomSheet: fciAbortSignature } = @@ -168,7 +176,7 @@ const FciSignatureFieldsScreen = () => { {clauseLabel} - {/* + {/* Show info icon and signature field info only for unfair clauses NOTE: this could be a temporary solution, since we could have an improved user experience. diff --git a/ts/features/fci/utils/signature.ts b/ts/features/fci/utils/signature.ts index 9e80dace3a6..443b30da966 100644 --- a/ts/features/fci/utils/signature.ts +++ b/ts/features/fci/utils/signature.ts @@ -47,13 +47,11 @@ export const getTosSignature = (qtspClauses: QtspClauses) => getFileDigest, TE.map( documentDigest => - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands qtspClauses.nonce + "+" + documentDigest + qtspClauses.accepted_clauses.reduce( (finalString, currentClause) => - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands finalString + "+" + currentClause.text, "" ) @@ -89,7 +87,7 @@ export const getCustomSignature = ( signatureField.attrs.top_right.y ) .join("+"); - // eslint-disable-next-line @typescript-eslint/restrict-plus-operands + return S.isEmpty(attributes) ? hash : hash + "+" + attributes; }) ) diff --git a/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx b/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx index 52d138df9fb..ce8e2fe25bf 100644 --- a/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx +++ b/ts/features/fims/singleSignOn/components/FimsSuccessBody.tsx @@ -27,7 +27,7 @@ import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { useIOBottomSheetModal } from "../../../../utils/hooks/bottomSheet"; import { openWebUrl } from "../../../../utils/url"; import { loadServiceDetail } from "../../../services/details/store/actions/details"; -import { serviceByIdSelector } from "../../../services/details/store/reducers/servicesById"; +import { serviceByIdSelector } from "../../../services/details/store/reducers"; import { logoForService } from "../../../services/home/utils"; import { fimsGetRedirectUrlAndOpenIABAction } from "../store/actions"; import { ConsentData, FimsClaimType } from "../types"; diff --git a/ts/features/fims/singleSignOn/saga/handleFimsGetRedirectUrlAndOpenIAB.ts b/ts/features/fims/singleSignOn/saga/handleFimsGetRedirectUrlAndOpenIAB.ts index defd5bb5a01..fd3023c86c1 100644 --- a/ts/features/fims/singleSignOn/saga/handleFimsGetRedirectUrlAndOpenIAB.ts +++ b/ts/features/fims/singleSignOn/saga/handleFimsGetRedirectUrlAndOpenIAB.ts @@ -138,7 +138,7 @@ const recurseUntilRPUrl = async ( if (res.type === "failure") { return res; } - if (200 <= res.status && res.status < 300) { + if (res.status >= 200 && res.status < 300) { return res; } // error case @@ -424,7 +424,7 @@ const validateAndProcessExtractedFormData = ( formData: Map ): E.Either => { const method = formData.get("method"); - if ("post" !== method) { + if (method !== "post") { return E.left(`Invalid form 'method' found: ${method}`); } diff --git a/ts/features/idpay/configuration/xstate/__tests__/actions.test.ts b/ts/features/idpay/configuration/xstate/__tests__/actions.test.ts index 13b460e0011..6661f67e759 100644 --- a/ts/features/idpay/configuration/xstate/__tests__/actions.test.ts +++ b/ts/features/idpay/configuration/xstate/__tests__/actions.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ import * as O from "fp-ts/lib/Option"; import * as p from "@pagopa/ts-commons/lib/pot"; import { IOToast } from "@pagopa/io-app-design-system"; diff --git a/ts/features/idpay/details/components/__tests__/BeneficiaryDetailsContent.test.tsx b/ts/features/idpay/details/components/__tests__/BeneficiaryDetailsContent.test.tsx index 1438dd04805..3197264ed6a 100644 --- a/ts/features/idpay/details/components/__tests__/BeneficiaryDetailsContent.test.tsx +++ b/ts/features/idpay/details/components/__tests__/BeneficiaryDetailsContent.test.tsx @@ -1,4 +1,3 @@ -/* eslint-disable functional/immutable-data */ import { render, within } from "@testing-library/react-native"; import * as React from "react"; import { diff --git a/ts/features/idpay/onboarding/components/OnboardingPrivacyAdvice.tsx b/ts/features/idpay/onboarding/components/OnboardingPrivacyAdvice.tsx index 3fe0fedbe8c..09c9bf2b815 100644 --- a/ts/features/idpay/onboarding/components/OnboardingPrivacyAdvice.tsx +++ b/ts/features/idpay/onboarding/components/OnboardingPrivacyAdvice.tsx @@ -1,4 +1,3 @@ -/* eslint-disable functional/immutable-data */ import { IOToast } from "@pagopa/io-app-design-system"; import * as React from "react"; import { Body } from "../../../../components/core/typography/Body"; diff --git a/ts/features/idpay/onboarding/screens/CompletionScreen.tsx b/ts/features/idpay/onboarding/screens/CompletionScreen.tsx index 779eb3d1582..ac504a72dce 100644 --- a/ts/features/idpay/onboarding/screens/CompletionScreen.tsx +++ b/ts/features/idpay/onboarding/screens/CompletionScreen.tsx @@ -34,7 +34,7 @@ const CompletionScreen = () => { goBack={true} headerTitle={I18n.t("idpay.onboarding.headerTitle")} > - + ); diff --git a/ts/features/idpay/onboarding/screens/InitiativeDetailsScreen.tsx b/ts/features/idpay/onboarding/screens/InitiativeDetailsScreen.tsx index 30f0f5e6b17..8687dd0a4d5 100644 --- a/ts/features/idpay/onboarding/screens/InitiativeDetailsScreen.tsx +++ b/ts/features/idpay/onboarding/screens/InitiativeDetailsScreen.tsx @@ -1,4 +1,3 @@ -/* eslint-disable functional/immutable-data */ import { RouteProp, useRoute } from "@react-navigation/native"; import { useSelector } from "@xstate/react"; import * as O from "fp-ts/lib/Option"; diff --git a/ts/features/idpay/onboarding/screens/PDNDPrerequisitesScreen.tsx b/ts/features/idpay/onboarding/screens/PDNDPrerequisitesScreen.tsx index 60122d88c98..efabe0af636 100644 --- a/ts/features/idpay/onboarding/screens/PDNDPrerequisitesScreen.tsx +++ b/ts/features/idpay/onboarding/screens/PDNDPrerequisitesScreen.tsx @@ -22,7 +22,7 @@ import I18n from "../../../../i18n"; import { useIOSelector } from "../../../../store/hooks"; import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp"; import { useIOBottomSheetAutoresizableModal } from "../../../../utils/hooks/bottomSheet"; -import { serviceByIdPotSelector } from "../../../services/details/store/reducers/servicesById"; +import { serviceByIdPotSelector } from "../../../services/details/store/reducers"; import { getPDNDCriteriaDescription } from "../utils/strings"; import { useOnboardingMachineService } from "../xstate/provider"; import { pdndCriteriaSelector, selectServiceId } from "../xstate/selectors"; diff --git a/ts/features/idpay/onboarding/xstate/__tests__/actions.test.ts b/ts/features/idpay/onboarding/xstate/__tests__/actions.test.ts index 40698389315..ee9a086142f 100644 --- a/ts/features/idpay/onboarding/xstate/__tests__/actions.test.ts +++ b/ts/features/idpay/onboarding/xstate/__tests__/actions.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ import * as O from "fp-ts/lib/Option"; import { AppParamsList, diff --git a/ts/features/idpay/onboarding/xstate/__tests__/services.test.ts b/ts/features/idpay/onboarding/xstate/__tests__/services.test.ts index 5980c7ececf..6f68c1bd2f4 100644 --- a/ts/features/idpay/onboarding/xstate/__tests__/services.test.ts +++ b/ts/features/idpay/onboarding/xstate/__tests__/services.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ import * as E from "fp-ts/lib/Either"; import * as O from "fp-ts/lib/Option"; import { PreferredLanguageEnum } from "../../../../../../definitions/backend/PreferredLanguage"; diff --git a/ts/features/idpay/onboarding/xstate/machine.ts b/ts/features/idpay/onboarding/xstate/machine.ts index fe26c50c96c..1bec9b06980 100644 --- a/ts/features/idpay/onboarding/xstate/machine.ts +++ b/ts/features/idpay/onboarding/xstate/machine.ts @@ -1,5 +1,5 @@ import { pipe } from "fp-ts/lib/function"; -/* eslint-disable no-underscore-dangle */ + import * as O from "fp-ts/lib/Option"; import { assign, createMachine } from "xstate"; import { InitiativeDataDTO } from "../../../../../definitions/idpay/InitiativeDataDTO"; diff --git a/ts/features/idpay/onboarding/xstate/selectors.ts b/ts/features/idpay/onboarding/xstate/selectors.ts index 69d7692eaf8..c042d56bf3c 100644 --- a/ts/features/idpay/onboarding/xstate/selectors.ts +++ b/ts/features/idpay/onboarding/xstate/selectors.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ import { pipe } from "fp-ts/lib/function"; import * as O from "fp-ts/lib/Option"; import { createSelector } from "reselect"; diff --git a/ts/features/idpay/unsubscription/xstate/__tests__/machine.test.ts b/ts/features/idpay/unsubscription/xstate/__tests__/machine.test.ts index 83e537c0860..79ddc51fa72 100644 --- a/ts/features/idpay/unsubscription/xstate/__tests__/machine.test.ts +++ b/ts/features/idpay/unsubscription/xstate/__tests__/machine.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable sonarjs/no-identical-functions */ /* eslint-disable functional/no-let */ import { waitFor } from "@testing-library/react-native"; import { interpret } from "xstate"; diff --git a/ts/features/idpay/unsubscription/xstate/services.ts b/ts/features/idpay/unsubscription/xstate/services.ts index f7c733077c2..a86f3228ff7 100644 --- a/ts/features/idpay/unsubscription/xstate/services.ts +++ b/ts/features/idpay/unsubscription/xstate/services.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-underscore-dangle */ import * as E from "fp-ts/lib/Either"; import * as TE from "fp-ts/lib/TaskEither"; import { flow, pipe } from "fp-ts/lib/function"; diff --git a/ts/features/idpay/wallet/components/__tests__/__snapshots__/IdPayCard.test.tsx.snap b/ts/features/idpay/wallet/components/__tests__/__snapshots__/IdPayCard.test.tsx.snap index 30b5e7dd2a1..29be7381fd3 100644 --- a/ts/features/idpay/wallet/components/__tests__/__snapshots__/IdPayCard.test.tsx.snap +++ b/ts/features/idpay/wallet/components/__tests__/__snapshots__/IdPayCard.test.tsx.snap @@ -3,14 +3,14 @@ exports[`IdPayCard should match the snapshot 1`] = ` @@ -51,7 +51,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -103,7 +103,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -155,7 +155,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -207,7 +207,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -263,7 +263,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -315,7 +315,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` A simple paragraph. @@ -383,19 +383,19 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -404,11 +404,11 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -416,19 +416,19 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -437,29 +437,29 @@ exports[`ItwMarkdown should match snapshot 1`] = ` . @@ -472,18 +472,18 @@ exports[`ItwMarkdown should match snapshot 1`] = ` defaultWeight="Regular" font="TitilliumSansPro" fontStyle={ - Object { + { "fontSize": 16, "lineHeight": 24, } } style={ - Array [ - Object { + [ + { "fontSize": 16, "lineHeight": 24, }, - Object { + { "color": "#475A6D", "fontFamily": "Titillium Sans Pro", "fontStyle": "normal", @@ -495,12 +495,12 @@ exports[`ItwMarkdown should match snapshot 1`] = ` > @@ -509,29 +509,29 @@ exports[`ItwMarkdown should match snapshot 1`] = ` . @@ -545,7 +545,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` defaultWeight="Semibold" font="TitilliumSansPro" fontStyle={ - Object { + { "fontSize": 16, "lineHeight": 24, "textDecorationLine": "underline", @@ -554,13 +554,13 @@ exports[`ItwMarkdown should match snapshot 1`] = ` numberOfLines={1} onPress={[Function]} style={ - Array [ - Object { + [ + { "fontSize": 16, "lineHeight": 24, "textDecorationLine": "underline", }, - Object { + { "color": "#0073E6", "fontFamily": "Titillium Sans Pro", "fontStyle": "normal", @@ -572,12 +572,12 @@ exports[`ItwMarkdown should match snapshot 1`] = ` > @@ -586,29 +586,29 @@ exports[`ItwMarkdown should match snapshot 1`] = ` with some text. @@ -622,7 +622,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` defaultWeight="Semibold" font="TitilliumSansPro" fontStyle={ - Object { + { "fontSize": 16, "lineHeight": 24, "textDecorationLine": "underline", @@ -631,13 +631,13 @@ exports[`ItwMarkdown should match snapshot 1`] = ` numberOfLines={1} onPress={[Function]} style={ - Array [ - Object { + [ + { "fontSize": 16, "lineHeight": 24, "textDecorationLine": "underline", }, - Object { + { "color": "#0073E6", "fontFamily": "Titillium Sans Pro", "fontStyle": "normal", @@ -649,12 +649,12 @@ exports[`ItwMarkdown should match snapshot 1`] = ` > @@ -663,11 +663,11 @@ exports[`ItwMarkdown should match snapshot 1`] = ` @@ -677,7 +677,7 @@ exports[`ItwMarkdown should match snapshot 1`] = ` { + const machineRef = ItwEidIssuanceMachineContext.useActorRef(); + + useOnFirstRender(() => { + machineRef.send({ type: "start" }); + }); + useHeaderSecondLevel({ title: "", contextualHelp: emptyContextualHelp, @@ -50,7 +58,7 @@ const ItwDiscoveryInfoScreen = () => { primary: { label: I18n.t("global.buttons.continue"), accessibilityLabel: I18n.t("global.buttons.continue"), - onPress: () => undefined + onPress: () => machineRef.send({ type: "accept-tos" }) } }} /> diff --git a/ts/features/itwallet/discovery/screens/__tests__/ItwDiscoveryInfoScreen.test.tsx b/ts/features/itwallet/discovery/screens/__tests__/ItwDiscoveryInfoScreen.test.tsx index 1fe25013b55..7dec13a32f7 100644 --- a/ts/features/itwallet/discovery/screens/__tests__/ItwDiscoveryInfoScreen.test.tsx +++ b/ts/features/itwallet/discovery/screens/__tests__/ItwDiscoveryInfoScreen.test.tsx @@ -1,11 +1,13 @@ import * as React from "react"; import { createStore } from "redux"; -import { ItwDiscoveryInfoScreen } from "../ItwDiscoveryInfoScreen"; -import { ITW_ROUTES } from "../../../navigation/routes"; -import { appReducer } from "../../../../../store/reducers"; import { applicationChangeState } from "../../../../../store/actions/application"; -import { renderScreenWithNavigationStoreContext } from "../../../../../utils/testWrapper"; +import { appReducer } from "../../../../../store/reducers"; import { GlobalState } from "../../../../../store/reducers/types"; +import { renderScreenWithNavigationStoreContext } from "../../../../../utils/testWrapper"; +import { itwEidIssuanceMachine } from "../../../machine/eid/machine"; +import { ItwEidIssuanceMachineContext } from "../../../machine/provider"; +import { ITW_ROUTES } from "../../../navigation/routes"; +import { ItwDiscoveryInfoScreen } from "../ItwDiscoveryInfoScreen"; describe("Test ItwDiscoveryInfo screen", () => { it("it should render the screen correctly", () => { @@ -16,8 +18,19 @@ describe("Test ItwDiscoveryInfo screen", () => { const renderComponent = () => { const globalState = appReducer(undefined, applicationChangeState("active")); + + const logic = itwEidIssuanceMachine.provide({ + actions: { + navigateToTosScreen: () => undefined + } + }); + return renderScreenWithNavigationStoreContext( - () => , + () => ( + + + + ), ITW_ROUTES.DISCOVERY.INFO, {}, createStore(appReducer, globalState as any) diff --git a/ts/features/itwallet/identification/screens/ItwIdentificationIdpSelectionScreen.tsx b/ts/features/itwallet/identification/screens/ItwIdentificationIdpSelectionScreen.tsx index ee404d7d2e0..6cf9d895c4f 100644 --- a/ts/features/itwallet/identification/screens/ItwIdentificationIdpSelectionScreen.tsx +++ b/ts/features/itwallet/identification/screens/ItwIdentificationIdpSelectionScreen.tsx @@ -4,7 +4,6 @@ import React from "react"; import { SpidIdp } from "../../../../../definitions/content/SpidIdp"; import { isReady } from "../../../../common/model/RemoteValue"; import IdpsGrid from "../../../../components/IdpsGrid"; -import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader"; import I18n from "../../../../i18n"; import { useIONavigation } from "../../../../navigation/params/AppParamsList"; @@ -17,13 +16,15 @@ import { idps as idpsFallback } from "../../../../utils/idps"; import LoadingComponent from "../../../fci/components/LoadingComponent"; -import { getItwGenericMappedError } from "../../common/utils/itwErrorsUtils"; -import { ITW_ROUTES } from "../../navigation/routes"; -import { useItwIdpIdentification } from "../hooks/useItwIdpIdentification"; +import { ItwEidIssuanceMachineContext } from "../../machine/provider"; +import { ItwTags } from "../../machine/tags"; export const ItwIdentificationIdpSelectionScreen = () => { - const navigation = useIONavigation(); const dispatch = useIODispatch(); + const machineRef = ItwEidIssuanceMachineContext.useActorRef(); + const isLoading = ItwEidIssuanceMachineContext.useSelector(snap => + snap.hasTag(ItwTags.Loading) + ); const idps = useIOSelector(idpsRemoteValueSelector); const idpValue = isReady(idps) ? idps.value.items : idpsFallback; @@ -31,28 +32,17 @@ export const ItwIdentificationIdpSelectionScreen = () => { randomOrderIdps(idpValue) ); - const { startIdentification, ...identification } = useItwIdpIdentification(); - useFocusEffect( React.useCallback(() => { dispatch(loadIdps.request()); }, [dispatch]) ); - React.useEffect(() => { - if (identification.result) { - navigation.navigate(ITW_ROUTES.MAIN, { - screen: ITW_ROUTES.ISSUANCE.EID_PREVIEW - }); - } - }, [identification.result, navigation]); - - if (identification.error) { - const mappedError = getItwGenericMappedError(() => navigation.goBack()); - return ; - } + const onIdpSelected = (idp: LocalIdpsFallback) => { + machineRef.send({ type: "select-spid-idp", idp }); + }; - if (identification.isPending) { + if (isLoading) { return ; } @@ -60,7 +50,7 @@ export const ItwIdentificationIdpSelectionScreen = () => { } /> diff --git a/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx b/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx index 8a6eabdba4a..2880346f568 100644 --- a/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx +++ b/ts/features/itwallet/identification/screens/ItwIdentificationModeSelectionScreen.tsx @@ -8,28 +8,25 @@ import * as pot from "@pagopa/ts-commons/lib/pot"; import { useFocusEffect } from "@react-navigation/native"; import React from "react"; import { Alert } from "react-native"; -import { RNavScreenWithLargeHeader } from "../../../../components/ui/RNavScreenWithLargeHeader"; +import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader"; import I18n from "../../../../i18n"; import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { isCieSupportedSelector } from "../../../../store/reducers/cie"; import { cieFlowForDevServerEnabled } from "../../../cieLogin/utils"; +import { ItwEidIssuanceMachineContext } from "../../machine/provider"; +import { ITW_ROUTES } from "../../navigation/routes"; import { itwNfcIsEnabled } from "../store/actions"; import { itwIsNfcEnabledSelector } from "../store/selectors"; -import { ITW_ROUTES } from "../../navigation/routes"; export const ItwIdentificationModeSelectionScreen = () => { const navigation = useIONavigation(); const dispatch = useIODispatch(); + const machineRef = ItwEidIssuanceMachineContext.useActorRef(); + const isCieSupportedPot = useIOSelector(isCieSupportedSelector); const isNfcEnabledPot = useIOSelector(itwIsNfcEnabledSelector); - useFocusEffect( - React.useCallback(() => { - dispatch(itwNfcIsEnabled.request()); - }, [dispatch]) - ); - const isCieSupported = React.useMemo( () => cieFlowForDevServerEnabled || pot.getOrElse(isCieSupportedPot, false), [isCieSupportedPot] @@ -41,9 +38,7 @@ export const ItwIdentificationModeSelectionScreen = () => { ); const handleSpidPress = () => { - navigation.navigate(ITW_ROUTES.MAIN, { - screen: ITW_ROUTES.IDENTIFICATION.IDP_SELECTION - }); + machineRef.send({ type: "select-identification-mode", mode: "spid" }); }; const handleCiePinPress = () => { @@ -60,8 +55,14 @@ export const ItwIdentificationModeSelectionScreen = () => { Alert.alert("Not implemented"); }; + useFocusEffect( + React.useCallback(() => { + dispatch(itwNfcIsEnabled.request()); + }, [dispatch]) + ); + return ( - @@ -105,6 +106,6 @@ export const ItwIdentificationModeSelectionScreen = () => { onPress={handleCieIdPress} /> - + ); }; diff --git a/ts/features/itwallet/identification/screens/ItwIdentificationNfcInstructionsScreen.tsx b/ts/features/itwallet/identification/screens/ItwIdentificationNfcInstructionsScreen.tsx index 6cac3b9cd71..10da5751b1b 100644 --- a/ts/features/itwallet/identification/screens/ItwIdentificationNfcInstructionsScreen.tsx +++ b/ts/features/itwallet/identification/screens/ItwIdentificationNfcInstructionsScreen.tsx @@ -5,11 +5,10 @@ import { ListItemInfo } from "@pagopa/io-app-design-system"; import React from "react"; -import { RNavScreenWithLargeHeader } from "../../../../components/ui/RNavScreenWithLargeHeader"; +import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader"; import I18n from "../../../../i18n"; import { useIONavigation } from "../../../../navigation/params/AppParamsList"; import * as cieUtils from "../../../../utils/cie"; -import { FooterStackButton } from "../../common/components/FooterStackButton"; export const ItwIdentificationNfcInstructionsScreen = () => { const navigation = useIONavigation(); @@ -23,23 +22,20 @@ export const ItwIdentificationNfcInstructionsScreen = () => { }; return ( - - } + actions={{ + type: "TwoButtons", + primary: { + label: I18n.t("features.itWallet.identification.nfc.primaryAction"), + onPress: handleOpenSettingsPress + }, + secondary: { + label: I18n.t("features.itWallet.identification.nfc.secondaryAction"), + onPress: handleClosePress + } + }} > { icon="systemToggleInstructions" /> - + ); }; diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceEidPreviewScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceEidPreviewScreen.tsx index 4ede174e127..3e677bd68e0 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceEidPreviewScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceEidPreviewScreen.tsx @@ -17,18 +17,17 @@ import { } from "../../common/utils/itwErrorsUtils"; import { ItwCredentialsMocks } from "../../common/utils/itwMocksUtils"; import { StoredCredential } from "../../common/utils/itwTypesUtils"; -import { ITW_ROUTES } from "../../navigation/routes"; +import { ItwEidIssuanceMachineContext } from "../../machine/provider"; export const ItwIssuanceEidPreviewScreen = () => { + const machineRef = ItwEidIssuanceMachineContext.useActorRef(); const navigation = useIONavigation(); const dispatch = useIODispatch(); const eidOption = O.some(ItwCredentialsMocks.eid); const dismissDialog = useItwDismissalDialog(); const handleStoreEidSuccess = () => { - navigation.navigate(ITW_ROUTES.MAIN, { - screen: ITW_ROUTES.ISSUANCE.EID_RESULT - }); + machineRef.send({ type: "add-to-wallet" }); }; const handleSaveToWallet = () => { diff --git a/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx b/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx index 28149810b5f..c79bc809afa 100644 --- a/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx +++ b/ts/features/itwallet/issuance/screens/ItwIssuanceEidResultScreen.tsx @@ -1,34 +1,17 @@ -import { useIOToast } from "@pagopa/io-app-design-system"; import React from "react"; import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; import I18n from "../../../../i18n"; -import { useIONavigation } from "../../../../navigation/params/AppParamsList"; -import ROUTES from "../../../../navigation/routes"; -import { WalletRoutes } from "../../../newWallet/navigation/routes"; +import { ItwEidIssuanceMachineContext } from "../../machine/provider"; export const ItwIssuanceEidResultScreen = () => { - const navigation = useIONavigation(); - const toast = useIOToast(); + const machineRef = ItwEidIssuanceMachineContext.useActorRef(); const handleContinue = () => { - navigation.replace(WalletRoutes.WALLET_NAVIGATOR, { - screen: WalletRoutes.WALLET_CARD_ONBOARDING - }); + machineRef.send({ type: "add-new-credential" }); }; const handleClose = () => { - toast.success(I18n.t("features.itWallet.issuance.eidResult.success.toast")); - navigation.reset({ - index: 1, - routes: [ - { - name: ROUTES.MAIN, - params: { - screen: ROUTES.WALLET_HOME - } - } - ] - }); + machineRef.send({ type: "go-to-wallet" }); }; return ( diff --git a/ts/features/itwallet/machine/credential/actions.ts b/ts/features/itwallet/machine/credential/actions.ts new file mode 100644 index 00000000000..2f1e0e07b85 --- /dev/null +++ b/ts/features/itwallet/machine/credential/actions.ts @@ -0,0 +1,27 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { useIONavigation } from "../../../../navigation/params/AppParamsList"; +import { ITW_ROUTES } from "../../navigation/routes"; + +export default (navigation: ReturnType) => ({ + navigateToAuthDataScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.ISSUANCE.CREDENTIAL_AUTH + }); + }, + + navigateToCredentialPreviewScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.ISSUANCE.CREDENTIAL_PREVIEW + }); + }, + + navigateToFailureScreen: () => {}, + + navigateToWallet: () => {}, + + storeCredential: () => {}, + + closeIssuance: () => { + navigation.popToTop(); + } +}); diff --git a/ts/features/itwallet/machine/credential/actors.ts b/ts/features/itwallet/machine/credential/actors.ts new file mode 100644 index 00000000000..0d20712fe43 --- /dev/null +++ b/ts/features/itwallet/machine/credential/actors.ts @@ -0,0 +1,10 @@ +import { fromPromise } from "xstate5"; +import { StoredCredential } from "../../common/utils/itwTypesUtils"; + +export default () => ({ + registerWalletInstance: fromPromise(async () => ""), + requestCredential: fromPromise< + StoredCredential, + StoredCredential | undefined + >(async () => ({} as StoredCredential)) +}); diff --git a/ts/features/itwallet/machine/credential/context.ts b/ts/features/itwallet/machine/credential/context.ts new file mode 100644 index 00000000000..5a5fa773420 --- /dev/null +++ b/ts/features/itwallet/machine/credential/context.ts @@ -0,0 +1,16 @@ +import { CredentialType } from "../../common/utils/itwMocksUtils"; +import { StoredCredential } from "../../common/utils/itwTypesUtils"; + +export type Context = { + credentialType: CredentialType | undefined; + walletAttestation: string | undefined; + eid: StoredCredential | undefined; + credential: StoredCredential | undefined; +}; + +export const InitialContext: Context = { + credentialType: undefined, + walletAttestation: undefined, + eid: undefined, + credential: undefined +}; diff --git a/ts/features/itwallet/machine/credential/events.ts b/ts/features/itwallet/machine/credential/events.ts new file mode 100644 index 00000000000..75c821a44e4 --- /dev/null +++ b/ts/features/itwallet/machine/credential/events.ts @@ -0,0 +1,34 @@ +import { CredentialType } from "../../common/utils/itwMocksUtils"; + +export type SelecteCredential = { + type: "select-credential"; + credentialType: CredentialType; +}; + +export type ConfirmAuthData = { + type: "confirm-auth-data"; +}; + +export type AddToWallet = { + type: "add-to-wallet"; +}; + +export type Retry = { + type: "retry"; +}; + +export type Back = { + type: "back"; +}; + +export type Close = { + type: "close"; +}; + +export type CredentialIssuanceEvents = + | SelecteCredential + | ConfirmAuthData + | AddToWallet + | Retry + | Back + | Close; diff --git a/ts/features/itwallet/machine/credential/machine.ts b/ts/features/itwallet/machine/credential/machine.ts new file mode 100644 index 00000000000..97d6e652873 --- /dev/null +++ b/ts/features/itwallet/machine/credential/machine.ts @@ -0,0 +1,121 @@ +import { assign, fromPromise, setup } from "xstate5"; +import { StoredCredential } from "../../common/utils/itwTypesUtils"; +import { ItwTags } from "../tags"; +import { Context, InitialContext } from "./context"; +import { CredentialIssuanceEvents } from "./events"; + +const notImplemented = () => { + throw new Error("Not implemented"); +}; + +export const itwCredentialIssuanceMachine = setup({ + types: { + context: {} as Context, + events: {} as CredentialIssuanceEvents + }, + actions: { + navigateToAuthDataScreen: notImplemented, + navigateToCredentialPreviewScreen: notImplemented, + navigateToFailureScreen: notImplemented, + navigateToWallet: notImplemented, + storeCredential: notImplemented, + closeIssuance: notImplemented + }, + actors: { + getWalletAttestation: fromPromise(notImplemented), + requestCredential: fromPromise< + StoredCredential, + StoredCredential | undefined + >(notImplemented) + } +}).createMachine({ + id: "itwCredentialIssuanceMachine", + context: InitialContext, + initial: "Idle", + states: { + Idle: { + on: { + "select-credential": { + target: "WalletInitialization", + actions: assign(({ event }) => ({ + credentialType: event.credentialType + })) + } + } + }, + WalletInitialization: { + tags: [ItwTags.Loading], + description: "Wallet attestation issuance", + invoke: { + src: "getWalletAttestation", + onDone: { + target: "Identification" + }, + onError: { + target: "Failure" + } + } + }, + Identification: { + description: "User is identified using the eID", + entry: "navigateToAuthDataScreen", + on: { + "confirm-auth-data": { + target: "RequestingCredential" + }, + close: { + actions: "closeIssuance" + } + } + }, + RequestingCredential: { + tags: [ItwTags.Loading], + initial: "Loading", + states: { + Loading: { + description: "Dummy state. Credential is being requested" + }, + Hanging: { + description: + "Credential is hanging. We navigate to the next screen and show a loading message", + entry: "navigateToCredentialPreviewScreen" + } + }, + after: { + 4000: { + target: ".Hanging" + } + }, + invoke: { + src: "requestCredential", + input: ({ context }) => context.eid, + onDone: { + actions: assign(({ event }) => ({ credential: event.output })), + target: "DisplayingCredentialPreview" + }, + onError: { + target: "Failure" + } + } + }, + DisplayingCredentialPreview: { + entry: "navigateToCredentialPreviewScreen", + on: { + "add-to-wallet": { + actions: ["storeCredential", "navigateToWallet"] + }, + close: { + actions: "closeIssuance" + } + } + }, + Failure: { + entry: "navigateToFailureScreen", + on: { + close: { + actions: "closeIssuance" + } + } + } + } +}); diff --git a/ts/features/itwallet/machine/eid/actions.ts b/ts/features/itwallet/machine/eid/actions.ts new file mode 100644 index 00000000000..d195ce79f40 --- /dev/null +++ b/ts/features/itwallet/machine/eid/actions.ts @@ -0,0 +1,89 @@ +/* eslint-disable @typescript-eslint/no-empty-function */ +import { IOToast } from "@pagopa/io-app-design-system"; +import I18n from "../../../../i18n"; +import { useIONavigation } from "../../../../navigation/params/AppParamsList"; +import ROUTES from "../../../../navigation/routes"; +import { WalletRoutes } from "../../../newWallet/navigation/routes"; +import { ITW_ROUTES } from "../../navigation/routes"; + +export const createEidIssuanceActionsImplementation = ( + navigation: ReturnType, + toast: IOToast +) => ({ + navigateToTosScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.DISCOVERY.INFO + }); + }, + + navigateToIdentificationModeScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.IDENTIFICATION.MODE_SELECTION + }); + }, + + navigateToIdpSelectionScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.IDENTIFICATION.IDP_SELECTION + }); + }, + + navigateToEidPreviewScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.ISSUANCE.EID_PREVIEW + }); + }, + + navigateToSuccessScreen: () => { + navigation.navigate(ITW_ROUTES.MAIN, { + screen: ITW_ROUTES.ISSUANCE.EID_RESULT + }); + }, + + navigateToFailureScreen: () => {}, + + navigateToWallet: () => { + toast.success(I18n.t("features.itWallet.issuance.eidResult.success.toast")); + navigation.reset({ + index: 1, + routes: [ + { + name: ROUTES.MAIN, + params: { + screen: ROUTES.WALLET_HOME + } + } + ] + }); + }, + + navigateToCredentialCatalog: () => { + navigation.reset({ + index: 1, + routes: [ + { + name: ROUTES.MAIN, + params: { + screen: ROUTES.WALLET_HOME + } + }, + { + name: WalletRoutes.WALLET_NAVIGATOR, + params: { + screen: WalletRoutes.WALLET_CARD_ONBOARDING + } + } + ] + }); + }, + + closeIssuance: () => { + navigation.popToTop(); + }, + + storeWalletAttestation: () => {}, + + storeEidCredential: () => {}, + + requestAssistance: () => {} +}); diff --git a/ts/features/itwallet/machine/eid/actors.ts b/ts/features/itwallet/machine/eid/actors.ts new file mode 100644 index 00000000000..f3f5b404f9b --- /dev/null +++ b/ts/features/itwallet/machine/eid/actors.ts @@ -0,0 +1,18 @@ +import { openAuthenticationSession } from "@pagopa/io-react-native-login-utils"; +import { fromPromise } from "xstate5"; +import { LocalIdpsFallback } from "../../../../utils/idps"; +import { getIdpLoginUri } from "../../../../utils/login"; +import { StoredCredential } from "../../common/utils/itwTypesUtils"; + +export const createEidIssuanceActorsImplementation = () => ({ + registerWalletInstance: fromPromise(async () => ""), + + showSpidIdentificationWebView: fromPromise( + async ({ input }) => + openAuthenticationSession(getIdpLoginUri(input.id, 2), "iologin") + ), + + requestEid: fromPromise( + async () => ({} as StoredCredential) + ) +}); diff --git a/ts/features/itwallet/machine/eid/context.ts b/ts/features/itwallet/machine/eid/context.ts new file mode 100644 index 00000000000..67160483820 --- /dev/null +++ b/ts/features/itwallet/machine/eid/context.ts @@ -0,0 +1,13 @@ +import { StoredCredential } from "../../common/utils/itwTypesUtils"; + +export type Context = { + walletAttestation: string | undefined; + userToken: string | undefined; + eid: StoredCredential | undefined; +}; + +export const InitialContext: Context = { + walletAttestation: undefined, + userToken: undefined, + eid: undefined +}; diff --git a/ts/features/itwallet/machine/eid/events.ts b/ts/features/itwallet/machine/eid/events.ts new file mode 100644 index 00000000000..541954f3f01 --- /dev/null +++ b/ts/features/itwallet/machine/eid/events.ts @@ -0,0 +1,62 @@ +import { LocalIdpsFallback } from "../../../../utils/idps"; + +type IdentificationMode = "spid" | "ciePin" | "cieId"; + +export type Start = { + type: "start"; +}; + +export type AcceptTos = { + type: "accept-tos"; +}; + +export type AddToWallet = { + type: "add-to-wallet"; +}; + +export type GoToWallet = { + type: "go-to-wallet"; +}; + +export type AddNewCredential = { + type: "add-new-credential"; +}; + +export type RequestAssistance = { + type: "request-assistance"; +}; + +export type SelectIdentificationMode = { + type: "select-identification-mode"; + mode: IdentificationMode; +}; + +export type SelectSpidIdp = { + type: "select-spid-idp"; + idp: LocalIdpsFallback; +}; + +export type Retry = { + type: "retry"; +}; + +export type Back = { + type: "back"; +}; + +export type Close = { + type: "close"; +}; + +export type EidIssuanceEvents = + | Start + | AcceptTos + | SelectIdentificationMode + | SelectSpidIdp + | AddToWallet + | GoToWallet + | AddNewCredential + | RequestAssistance + | Retry + | Back + | Close; diff --git a/ts/features/itwallet/machine/eid/machine.ts b/ts/features/itwallet/machine/eid/machine.ts new file mode 100644 index 00000000000..a303aecea5c --- /dev/null +++ b/ts/features/itwallet/machine/eid/machine.ts @@ -0,0 +1,202 @@ +import { assertEvent, assign, fromPromise, setup } from "xstate5"; +import { LocalIdpsFallback } from "../../../../utils/idps"; +import { StoredCredential } from "../../common/utils/itwTypesUtils"; +import { ItwTags } from "../tags"; +import { Context, InitialContext } from "./context"; +import { EidIssuanceEvents } from "./events"; + +const notImplemented = () => { + throw new Error("Not implemented"); +}; + +export const itwEidIssuanceMachine = setup({ + types: { + context: {} as Context, + events: {} as EidIssuanceEvents + }, + actions: { + navigateToTosScreen: notImplemented, + navigateToIdentificationModeScreen: notImplemented, + navigateToIdpSelectionScreen: notImplemented, + navigateToEidPreviewScreen: notImplemented, + navigateToSuccessScreen: notImplemented, + navigateToFailureScreen: notImplemented, + navigateToWallet: notImplemented, + navigateToCredentialCatalog: notImplemented, + storeWalletAttestation: notImplemented, + storeEidCredential: notImplemented, + closeIssuance: notImplemented, + requestAssistance: notImplemented + }, + actors: { + registerWalletInstance: fromPromise(notImplemented), + showSpidIdentificationWebView: fromPromise( + notImplemented + ), + requestEid: fromPromise( + notImplemented + ) + }, + guards: {} +}).createMachine({ + id: "itwEidIssuanceMachine", + context: InitialContext, + initial: "Idle", + states: { + Idle: { + description: "The machine is in idle, ready to start the issuance flow", + on: { + start: { + target: "TosAcceptance" + } + } + }, + TosAcceptance: { + description: + "Display of the ToS to the user who must accept in order to proceed with the issuance of the eID", + entry: "navigateToTosScreen", + on: { + "accept-tos": { + target: "WalletInitialization" + } + } + }, + WalletInitialization: { + tags: [ItwTags.Loading], + description: "Wallet instance registration and attestation issuance", + invoke: { + src: "registerWalletInstance", + onDone: { + target: "UserIdentification", + actions: "storeWalletAttestation" + }, + onError: { + target: "Failure" + } + } + }, + UserIdentification: { + description: + "User identification flow. Once we get the user token we can continue to the eID issuance", + initial: "ModeSelection", + states: { + ModeSelection: { + entry: "navigateToIdentificationModeScreen", + on: { + "select-identification-mode": [ + { + guard: ({ event }) => event.mode === "spid", + target: "Spid" + }, + { + guard: ({ event }) => event.mode === "ciePin", + target: "CiePin" + }, + { + guard: ({ event }) => event.mode === "cieId", + target: "CieId" + } + ], + back: "#itwEidIssuanceMachine.TosAcceptance" + } + }, + Spid: { + initial: "IdpSelection", + states: { + IdpSelection: { + entry: "navigateToIdpSelectionScreen", + on: { + "select-spid-idp": { + target: "IdpIdentification" + }, + back: { + target: + "#itwEidIssuanceMachine.UserIdentification.ModeSelection" + } + } + }, + IdpIdentification: { + tags: [ItwTags.Loading], + invoke: { + input: ({ event }) => { + assertEvent(event, "select-spid-idp"); + return event.idp; + }, + src: "showSpidIdentificationWebView", + onDone: { + actions: assign(({ event }) => ({ userToken: event.output })), + target: "#itwEidIssuanceMachine.UserIdentification.Completed" + } + } + } + } + }, + CiePin: { + // TODO + }, + CieId: { + // TODO + }, + Completed: { + type: "final" + } + }, + onDone: { + target: "Issuance" + } + }, + Issuance: { + entry: "navigateToEidPreviewScreen", + initial: "RequestingEid", + states: { + RequestingEid: { + tags: [ItwTags.Loading], + invoke: { + src: "requestEid", + input: ({ context }) => context.userToken, + onDone: { + actions: assign(({ event }) => ({ eid: event.output })), + target: "#itwEidIssuanceMachine.Issuance.DisplayingPreview" + }, + onError: { + target: "#itwEidIssuanceMachine.Failure" + } + } + }, + DisplayingPreview: { + on: { + "add-to-wallet": { + actions: "storeEidCredential", + target: "#itwEidIssuanceMachine.Success" + }, + close: { + actions: "closeIssuance" + } + } + } + } + }, + Success: { + entry: "navigateToSuccessScreen", + on: { + "add-new-credential": { + actions: "navigateToCredentialCatalog" + }, + "go-to-wallet": { + actions: "navigateToWallet" + } + } + }, + Failure: { + entry: "navigateToFailureScreen", + on: { + close: { + actions: "closeIssuance" + }, + "request-assistance": { + actions: "requestAssistance" + } + } + } + } +}); diff --git a/ts/features/itwallet/machine/provider.tsx b/ts/features/itwallet/machine/provider.tsx new file mode 100644 index 00000000000..1dd8c4315aa --- /dev/null +++ b/ts/features/itwallet/machine/provider.tsx @@ -0,0 +1,47 @@ +import { useIOToast } from "@pagopa/io-app-design-system"; +import { createActorContext } from "@xstate5/react"; +import * as React from "react"; +import { useIONavigation } from "../../../navigation/params/AppParamsList"; +import createCredentialIssuanceActionsImplementation from "./credential/actions"; +import createCredentialIssuanceActorsImplementation from "./credential/actors"; +import { itwCredentialIssuanceMachine } from "./credential/machine"; +import { createEidIssuanceActionsImplementation } from "./eid/actions"; +import { createEidIssuanceActorsImplementation } from "./eid/actors"; +import { itwEidIssuanceMachine } from "./eid/machine"; + +type Props = { + children: JSX.Element; +}; + +export const ItwEidIssuanceMachineContext = createActorContext( + itwEidIssuanceMachine +); + +export const ItwCredentialIssuanceMachineContext = createActorContext( + itwCredentialIssuanceMachine +); + +export const ItWalletIssuanceMachineProvider = (props: Props) => { + const navigation = useIONavigation(); + const toast = useIOToast(); + + const eidIssuanceMachine = itwEidIssuanceMachine.provide({ + actions: createEidIssuanceActionsImplementation(navigation, toast), + actors: createEidIssuanceActorsImplementation() + }); + + const credentialIssuanceMachine = itwCredentialIssuanceMachine.provide({ + actions: createCredentialIssuanceActionsImplementation(navigation), + actors: createCredentialIssuanceActorsImplementation() + }); + + return ( + + + {props.children} + + + ); +}; diff --git a/ts/features/itwallet/machine/tags.ts b/ts/features/itwallet/machine/tags.ts new file mode 100644 index 00000000000..33aa906cd96 --- /dev/null +++ b/ts/features/itwallet/machine/tags.ts @@ -0,0 +1,3 @@ +export enum ItwTags { + Loading = "Loading" +} diff --git a/ts/features/itwallet/navigation/ItwStackNavigator.tsx b/ts/features/itwallet/navigation/ItwStackNavigator.tsx index f3fd4a4ec56..7d4ac61c89b 100644 --- a/ts/features/itwallet/navigation/ItwStackNavigator.tsx +++ b/ts/features/itwallet/navigation/ItwStackNavigator.tsx @@ -9,6 +9,11 @@ import { ItwIssuanceCredentialAuthScreen } from "../issuance/screens/ItwIssuance import { ItwIssuanceCredentialPreviewScreen } from "../issuance/screens/ItwIssuanceCredentialPreviewScreen"; import { ItwIssuanceEidPreviewScreen } from "../issuance/screens/ItwIssuanceEidPreviewScreen"; import { ItwIssuanceEidResultScreen } from "../issuance/screens/ItwIssuanceEidResultScreen"; +import { + ItWalletIssuanceMachineProvider, + ItwCredentialIssuanceMachineContext, + ItwEidIssuanceMachineContext +} from "../machine/provider"; import { ItwPresentationEidDetailScreen } from "../presentation/screens/ItwPresentationEidDetailScreen"; import ItwPlayground from "../playgrounds/screens/ItwPlayground"; import { ItwParamsList } from "./ItwParamsList"; @@ -17,55 +22,76 @@ import { ITW_ROUTES } from "./routes"; const Stack = createStackNavigator(); export const ItwStackNavigator = () => ( - - {/* DISCOVERY */} - - {/* IDENTIFICATION */} - - - - {/* ISSUANCE */} - - - - - {/* CREDENTIAL PRESENTATION */} - - {/* PLAYGROUNDS */} - - + + + ); + +const InnerNavigator = () => { + const eidIssuanceMachineRef = ItwEidIssuanceMachineContext.useActorRef(); + const credentialIssuanceMachineRef = + ItwCredentialIssuanceMachineContext.useActorRef(); + + return ( + { + // Read more on https://reactnavigation.org/docs/preventing-going-back/ + // Whenever we have a back navigation action we send a "back" event to the machine. + // Since the back event is accepted only by specific states, we can safely send a back event to each machine + eidIssuanceMachineRef.send({ type: "back" }); + credentialIssuanceMachineRef.send({ type: "back" }); + } + }} + > + {/* DISCOVERY */} + + {/* IDENTIFICATION */} + + + + {/* ISSUANCE */} + + + + + {/* CREDENTIAL PRESENTATION */} + + {/* PLAYGROUNDS */} + + + ); +}; diff --git a/ts/features/lollipop/screens/UnsupportedDeviceScreen.tsx b/ts/features/lollipop/screens/UnsupportedDeviceScreen.tsx index 6e82dd39b6a..293fd3313e9 100644 --- a/ts/features/lollipop/screens/UnsupportedDeviceScreen.tsx +++ b/ts/features/lollipop/screens/UnsupportedDeviceScreen.tsx @@ -1,6 +1,6 @@ -import I18n from "i18n-js"; import * as React from "react"; import { Modal } from "react-native"; +import I18n from "../../../i18n"; import { unsupportedDeviceLearnMoreUrl } from "../../../config"; import { openWebUrl } from "../../../utils/url"; import { useAvoidHardwareBackButton } from "../../../utils/useAvoidHardwareBackButton"; diff --git a/ts/features/messages/components/Home/ArchiveRestoreBar.tsx b/ts/features/messages/components/Home/ArchiveRestoreBar.tsx new file mode 100644 index 00000000000..0928d7da973 --- /dev/null +++ b/ts/features/messages/components/Home/ArchiveRestoreBar.tsx @@ -0,0 +1,108 @@ +import React, { useCallback, useEffect } from "react"; +import { StyleSheet, View } from "react-native"; +import { ButtonOutline, ButtonSolid } from "@pagopa/io-app-design-system"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { + useIODispatch, + useIOSelector, + useIOStore +} from "../../../../store/hooks"; +import { + areThereEntriesForShownMessageListCategorySelector, + isArchivingDisabledSelector, + isArchivingInProcessingModeSelector +} from "../../store/reducers/archiving"; +import { useIOTabNavigation } from "../../../../navigation/params/AppParamsList"; +import { shownMessageCategorySelector } from "../../store/reducers/allPaginated"; +import I18n from "../../../../i18n"; +import { + resetMessageArchivingAction, + startProcessingMessageArchivingAction +} from "../../store/actions/archiving"; +import { MessageListCategory } from "../../types/messageListCategory"; +import { useHardwareBackButton } from "../../../../hooks/useHardwareBackButton"; + +const styles = StyleSheet.create({ + container: { + flexDirection: "row", + paddingHorizontal: 24, + paddingTop: 8 + }, + endButtonContainer: { + flex: 1, + marginStart: 4 + }, + startButtonContainer: { + flex: 1, + marginEnd: 4 + } +}); + +type ArchiveRestoreCTAsProps = { + category: MessageListCategory; +}; + +export const ArchiveRestoreBar = () => { + const store = useIOStore(); + const tabNavigation = useIOTabNavigation(); + + const isArchivingDisabled = useIOSelector(isArchivingDisabledSelector); + const shownCategory = useIOSelector(shownMessageCategorySelector); + + const androidBackButtonCallback = useCallback(() => { + // Disable Android back button while processing archiving/restoring + const state = store.getState(); + return isArchivingInProcessingModeSelector(state); + }, [store]); + useHardwareBackButton(androidBackButtonCallback); + + useEffect(() => { + tabNavigation.setOptions({ + tabBarStyle: { + display: isArchivingDisabled ? "flex" : "none" + } + }); + }, [isArchivingDisabled, tabNavigation]); + + if (isArchivingDisabled) { + return null; + } + + return ; +}; + +const ArchiveRestoreCTAs = ({ category }: ArchiveRestoreCTAsProps) => { + const safeAreaInsets = useSafeAreaInsets(); + const dispatch = useIODispatch(); + + const archiveRestoreCTAEnabled = useIOSelector(state => + areThereEntriesForShownMessageListCategorySelector(state, category) + ); + const isProcessing = useIOSelector(isArchivingInProcessingModeSelector); + + const rightButtonLabel = I18n.t( + `messages.cta.${category === "ARCHIVE" ? "unarchive" : "archive"}` + ); + return ( + + + dispatch(resetMessageArchivingAction(undefined))} + /> + + + dispatch(startProcessingMessageArchivingAction())} + /> + + + ); +}; diff --git a/ts/features/messages/components/Home/DS/DoubleAvatar.tsx b/ts/features/messages/components/Home/DS/DoubleAvatar.tsx index d5e5eae40e0..3509fa4bcda 100644 --- a/ts/features/messages/components/Home/DS/DoubleAvatar.tsx +++ b/ts/features/messages/components/Home/DS/DoubleAvatar.tsx @@ -175,7 +175,7 @@ export const DoubleAvatar = ({ backgroundLogoUri }: DoubleAvatarProps) => { } ]} > - + diff --git a/ts/features/messages/components/Home/EmptyList.tsx b/ts/features/messages/components/Home/EmptyList.tsx index 5c8748bb4d6..fd203515b51 100644 --- a/ts/features/messages/components/Home/EmptyList.tsx +++ b/ts/features/messages/components/Home/EmptyList.tsx @@ -1,12 +1,19 @@ -import React, { useMemo } from "react"; +import React, { useCallback, useMemo } from "react"; +import { constUndefined, pipe } from "fp-ts/lib/function"; +import * as B from "fp-ts/lib/boolean"; import { ButtonSolidProps, IOPictograms } from "@pagopa/io-app-design-system"; -import { useIODispatch, useIOSelector } from "../../../../store/hooks"; +import { + useIODispatch, + useIOSelector, + useIOStore +} from "../../../../store/hooks"; import { emptyListReasonSelector } from "../../store/reducers/allPaginated"; import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent"; import I18n from "../../../../i18n"; import { reloadAllMessages } from "../../store/actions"; import { pageSize } from "../../../../config"; import { MessageListCategory } from "../../types/messageListCategory"; +import { isArchivingInProcessingModeSelector } from "../../store/reducers/archiving"; export type EmptyListProps = { category: MessageListCategory; @@ -24,9 +31,31 @@ type ScreenDataType = { export const EmptyList = ({ category }: EmptyListProps) => { const dispatch = useIODispatch(); + const store = useIOStore(); + const emptyListReason = useIOSelector(state => emptyListReasonSelector(state, category) ); + const onRetryCallback = useCallback( + () => + pipe( + store.getState(), + isArchivingInProcessingModeSelector, + B.fold( + () => + pipe( + { + pageSize, + filter: { getArchived: category === "ARCHIVE" } + }, + reloadAllMessages.request, + dispatch + ), + constUndefined + ) + ), + [category, dispatch, store] + ); const screenData = useMemo((): ScreenDataType | undefined => { switch (emptyListReason) { @@ -42,13 +71,7 @@ export const EmptyList = ({ category }: EmptyListProps) => { action: { testID: "home_emptyList_retry", label: I18n.t("global.buttons.retry"), - onPress: () => - dispatch( - reloadAllMessages.request({ - pageSize, - filter: { getArchived: category === "ARCHIVE" } - }) - ) + onPress: onRetryCallback }, pictogram: "fatalError", title: I18n.t("messages.loadingErrorTitle") @@ -56,7 +79,7 @@ export const EmptyList = ({ category }: EmptyListProps) => { default: return undefined; } - }, [category, dispatch, emptyListReason]); + }, [category, emptyListReason, onRetryCallback]); if (!screenData) { return null; diff --git a/ts/features/messages/components/Home/MessageList.tsx b/ts/features/messages/components/Home/MessageList.tsx index 7ac4324124e..39a6c265749 100644 --- a/ts/features/messages/components/Home/MessageList.tsx +++ b/ts/features/messages/components/Home/MessageList.tsx @@ -72,20 +72,17 @@ export const MessageList = React.forwardRef( dispatch(reloadAllMessagesAction); } }, [category, dispatch, store]); - const onEndReachedCallback = useCallback( - _ => { - const state = store.getState(); - const loadNextPageMessages = getLoadNextPageMessagesActionIfAllowed( - state, - category, - new Date() - ); - if (loadNextPageMessages) { - dispatch(loadNextPageMessages); - } - }, - [category, dispatch, store] - ); + const onEndReachedCallback = useCallback(() => { + const state = store.getState(); + const loadNextPageMessages = getLoadNextPageMessagesActionIfAllowed( + state, + category, + new Date() + ); + if (loadNextPageMessages) { + dispatch(loadNextPageMessages); + } + }, [category, dispatch, store]); return ( ( /> ); } else { - return ; + return ( + + ); } }} ListFooterComponent={