diff --git a/.env.local b/.env.local
index 15b6ff0d3a8..ead00523583 100644
--- a/.env.local
+++ b/.env.local
@@ -97,6 +97,4 @@ ITW_BYPASS_IDENTITY_MATCH=YES
# Use the test environment for the IDP hint for both CIE and SPID
ITW_IDP_HINT_TEST=YES
# IPZS Privacy Policy URL
-ITW_IPZS_PRIVACY_URL='https://io.italia.it/informativa-ipzs'
-# ITW Documents on IO URL
-ITW_DOCUMENTS_ON_IO_URL='https://io.italia.it/documenti-su-io'
+ITW_IPZS_PRIVACY_URL='https://io.italia.it/informativa-ipzs'
\ No newline at end of file
diff --git a/.env.production b/.env.production
index 0d09f9f56db..6d01eebd819 100644
--- a/.env.production
+++ b/.env.production
@@ -97,6 +97,4 @@ ITW_BYPASS_IDENTITY_MATCH=NO
# Use the test environment for the IDP hint for both CIE and SPID
ITW_IDP_HINT_TEST=NO
# IPZS Privacy Policy URL
-ITW_IPZS_PRIVACY_URL='https://io.italia.it/informativa-ipzs'
-# ITW Documents on IO URL
-ITW_DOCUMENTS_ON_IO_URL='https://io.italia.it/documenti-su-io'
+ITW_IPZS_PRIVACY_URL='https://io.italia.it/informativa-ipzs'
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5b90ff95da7..56f31e639a4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,30 @@
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.80.0-rc.6](https://github.com/pagopa/io-app/compare/2.80.0-rc.5...2.80.0-rc.6) (2024-12-19)
+
+
+### Features
+
+* **IT Wallet:** [[SIW-1824](https://pagopa.atlassian.net/browse/SIW-1824)] Show alert if wallet instance is revoked ([#6547](https://github.com/pagopa/io-app/issues/6547)) ([fbe9e87](https://github.com/pagopa/io-app/commit/fbe9e87eb00f576a09867991353b1b82a5f5fea3))
+
+
+### Bug Fixes
+
+* [[IOPID-2547](https://pagopa.atlassian.net/browse/IOPID-2547)] Fix unhandled error on `Linking.openUrl` ([#6554](https://github.com/pagopa/io-app/issues/6554)) ([1ddf587](https://github.com/pagopa/io-app/commit/1ddf587f5c63c6bf5a236da5a96e31764b86955f))
+* [[IOPLT-814](https://pagopa.atlassian.net/browse/IOPLT-814)] Fixes workflow concatenation after changes due to test improvements ([#6572](https://github.com/pagopa/io-app/issues/6572)) ([d8a8e66](https://github.com/pagopa/io-app/commit/d8a8e660bfc5bf9358e2050763ebeeff5b6b9c4c))
+
+
+### Chores
+
+* **Cross:** [[IOAPPX-432](https://pagopa.atlassian.net/browse/IOAPPX-432)] Development Push notifications for Android ([#6416](https://github.com/pagopa/io-app/issues/6416)) ([8cf64a9](https://github.com/pagopa/io-app/commit/8cf64a942a0ac538c0f6b493dccd1d8695bc29d5))
+* **Cross:** [[IOAPPX-448](https://pagopa.atlassian.net/browse/IOAPPX-448)] Add missing components to the Design System section, remove legacy ones + Change `ListItemMessage` component API ([#6541](https://github.com/pagopa/io-app/issues/6541)) ([323996f](https://github.com/pagopa/io-app/commit/323996f8e0207da8a60766ff38042e9a85d9a857))
+* [[IOBP-1076](https://pagopa.atlassian.net/browse/IOBP-1076)] FAQ without CTA with sticky buttons ([#6555](https://github.com/pagopa/io-app/issues/6555)) ([a0d562f](https://github.com/pagopa/io-app/commit/a0d562fc949e40ca7b24b714e42e41f53db56f32))
+* [[IOPLT-798](https://pagopa.atlassian.net/browse/IOPLT-798)] Split test execution using shards ([#6500](https://github.com/pagopa/io-app/issues/6500)) ([7d9df54](https://github.com/pagopa/io-app/commit/7d9df5479cd9784e2679e5a5188d085cb68dbef5))
+* [[IOPLT-813](https://pagopa.atlassian.net/browse/IOPLT-813)] Fix android run script and align Gemfile ([#6571](https://github.com/pagopa/io-app/issues/6571)) ([bbe6980](https://github.com/pagopa/io-app/commit/bbe69800a17a8e0b6b88629da9b58fa7f23f95c4))
+* **IT Wallet:** [[SIW-1793](https://pagopa.atlassian.net/browse/SIW-1793)] Update non-matching identity screen ([#6559](https://github.com/pagopa/io-app/issues/6559)) ([5c04708](https://github.com/pagopa/io-app/commit/5c04708efdf2aa82263c947073eb6c9e489f15f7))
+* [[IOPLT-801](https://pagopa.atlassian.net/browse/IOPLT-801)] Improvements to canary release workflow ([#6468](https://github.com/pagopa/io-app/issues/6468)) ([dc66860](https://github.com/pagopa/io-app/commit/dc668601582efbb029b36c58186e7fb9e098b576))
+
## [2.80.0-rc.5](https://github.com/pagopa/io-app/compare/2.80.0-rc.4...2.80.0-rc.5) (2024-12-18)
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 320024561f8..8e759e032cf 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -119,8 +119,8 @@ android {
applicationId "it.pagopa.io.app"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
- versionCode 100154900
- versionName "2.80.0.5"
+ versionCode 100154901
+ versionName "2.80.0.6"
multiDexEnabled true
// The resConfigs attribute will remove all not required localized resources while building the application,
// including the localized resources from libraries.
diff --git a/ios/ItaliaApp.xcodeproj/project.pbxproj b/ios/ItaliaApp.xcodeproj/project.pbxproj
index 7e4990bdbc0..70e39b1f27b 100644
--- a/ios/ItaliaApp.xcodeproj/project.pbxproj
+++ b/ios/ItaliaApp.xcodeproj/project.pbxproj
@@ -798,7 +798,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 = 6;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = M2X5YQ4BJ7;
ENABLE_BITCODE = NO;
@@ -835,7 +835,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 = 6;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = M2X5YQ4BJ7;
ENABLE_BITCODE = NO;
diff --git a/ios/ItaliaApp/Info.plist b/ios/ItaliaApp/Info.plist
index fffb2ffee8b..fcb81f12f10 100644
--- a/ios/ItaliaApp/Info.plist
+++ b/ios/ItaliaApp/Info.plist
@@ -36,7 +36,7 @@
CFBundleVersion
- 5
+ 6
ITSAppUsesNonExemptEncryption
LSApplicationQueriesSchemes
diff --git a/ios/ItaliaAppTests/Info.plist b/ios/ItaliaAppTests/Info.plist
index 8256df486f4..54df2577125 100644
--- a/ios/ItaliaAppTests/Info.plist
+++ b/ios/ItaliaAppTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 5
+ 6
\ No newline at end of file
diff --git a/locales/en/index.yml b/locales/en/index.yml
index 8c2b33fac22..a10530a05a0 100644
--- a/locales/en/index.yml
+++ b/locales/en/index.yml
@@ -3555,6 +3555,20 @@ features:
title: Dicci cosa ne pensi
content: Raccontaci la tua esperienza con la funzionalità Documenti su IO.
action: Inizia
+ walletInstanceRevoked:
+ alert:
+ cta: Scopri di più
+ closeButton: Chiudi
+ closeButtonAlt: Ho capito
+ revokedByWalletProvider:
+ title: Documenti su IO è stata disattivata
+ content: Per verificare i requisiti richiesti per continuare a usare la funzionalità sul tuo dispositivo, premi "Scopri di più".
+ newWalletInstanceCreated:
+ title: Documenti su IO è stata disattivata su questo dispositivo
+ content: Puoi usare i tuoi documenti su IO su un solo dispositivo alla volta per ragioni di sicurezza.
+ revokedByUser:
+ title: Hai disattivato Documenti su IO
+ content: Se cambi idea, potrai riattivare Documenti su IO in futuro.
support:
ticketList:
noTicket:
diff --git a/locales/it/index.yml b/locales/it/index.yml
index 640a6ab0a72..3a5cbe020ce 100644
--- a/locales/it/index.yml
+++ b/locales/it/index.yml
@@ -3555,6 +3555,20 @@ features:
title: Dicci cosa ne pensi
content: Raccontaci la tua esperienza con la funzionalità Documenti su IO.
action: Inizia
+ walletInstanceRevoked:
+ alert:
+ cta: Scopri di più
+ closeButton: Chiudi
+ closeButtonAlt: Ho capito
+ revokedByWalletProvider:
+ title: Documenti su IO è stata disattivata
+ content: Per verificare i requisiti richiesti per continuare a usare la funzionalità sul tuo dispositivo, premi "Scopri di più".
+ newWalletInstanceCreated:
+ title: Documenti su IO è stata disattivata su questo dispositivo
+ content: Puoi usare i tuoi documenti su IO su un solo dispositivo alla volta per ragioni di sicurezza.
+ revokedByUser:
+ title: Hai disattivato Documenti su IO
+ content: Se cambi idea, potrai riattivare Documenti su IO in futuro.
support:
ticketList:
noTicket:
diff --git a/package.json b/package.json
index bf310534b75..eb641b03094 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "italia-app",
- "version": "2.80.0-rc.5",
+ "version": "2.80.0-rc.6",
"private": true,
"scripts": {
"start": "react-native start",
diff --git a/publiccode.yml b/publiccode.yml
index e9c51e4b7cf..9fb9e0d7e57 100644
--- a/publiccode.yml
+++ b/publiccode.yml
@@ -9,7 +9,7 @@ releaseDate: "2024-11-21"
url: "https://github.com/pagopa/io-app"
applicationSuite: IO
landingURL: "https://io.italia.it/"
-softwareVersion: 2.80.0-rc.5
+softwareVersion: 2.80.0-rc.6
developmentStatus: beta
softwareType: standalone/mobile
roadmap: "https://io.italia.it/"
diff --git a/ts/config.ts b/ts/config.ts
index fa847bbb3ad..505771f19eb 100644
--- a/ts/config.ts
+++ b/ts/config.ts
@@ -252,8 +252,3 @@ export const itwIpzsPrivacyUrl: string = pipe(
t.string.decode,
E.getOrElse(() => "https://io.italia.it/informativa-ipzs")
);
-export const itwDocumentsOnIOUrl: string = pipe(
- Config.ITW_DOCUMENTS_ON_IO_URL,
- t.string.decode,
- E.getOrElse(() => "https://io.italia.it/documenti-su-io")
-);
diff --git a/ts/features/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap b/ts/features/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
index 527cdcd64ce..81dd7b4bcb0 100644
--- a/ts/features/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
+++ b/ts/features/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
@@ -132,6 +132,7 @@ exports[`featuresPersistor should match snapshot 1`] = `
},
"walletInstance": {
"attestation": undefined,
+ "status": undefined,
},
},
"landingBanners": {
diff --git a/ts/features/itwallet/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap b/ts/features/itwallet/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
index 7fb4ec75a32..e6f11fb8d17 100644
--- a/ts/features/itwallet/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
+++ b/ts/features/itwallet/common/store/reducers/__tests__/__snapshots__/index.test.ts.snap
@@ -19,6 +19,7 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to
},
"walletInstance": {
"attestation": undefined,
+ "status": undefined,
},
}
`;
diff --git a/ts/features/itwallet/common/utils/itwTypesUtils.ts b/ts/features/itwallet/common/utils/itwTypesUtils.ts
index fe04304f663..3fab058e345 100644
--- a/ts/features/itwallet/common/utils/itwTypesUtils.ts
+++ b/ts/features/itwallet/common/utils/itwTypesUtils.ts
@@ -1,4 +1,8 @@
-import { Credential, Trust } from "@pagopa/io-react-native-wallet";
+import {
+ Credential,
+ Trust,
+ WalletInstance
+} from "@pagopa/io-react-native-wallet";
/**
* Alias type for the return type of the start issuance flow operation.
@@ -43,6 +47,19 @@ export type ParsedStatusAttestation = Awaited<
ReturnType
>["parsedStatusAttestation"]["payload"];
+/**
+ * Alias for the WalletInstanceStatus type
+ */
+export type WalletInstanceStatus = Awaited<
+ ReturnType
+>;
+
+/**
+ * Alias for the WalletInstanceRevocationReason type
+ */
+export type WalletInstanceRevocationReason =
+ WalletInstanceStatus["revocation_reason"];
+
export type StoredStatusAttestation =
| {
credentialStatus: "valid";
diff --git a/ts/features/itwallet/lifecycle/saga/__tests__/checkWalletInstanceStateSaga.test.ts b/ts/features/itwallet/lifecycle/saga/__tests__/checkWalletInstanceStateSaga.test.ts
index fdf253d364c..574090dea19 100644
--- a/ts/features/itwallet/lifecycle/saga/__tests__/checkWalletInstanceStateSaga.test.ts
+++ b/ts/features/itwallet/lifecycle/saga/__tests__/checkWalletInstanceStateSaga.test.ts
@@ -12,9 +12,9 @@ import { getWalletInstanceStatus } from "../../../common/utils/itwAttestationUti
import { StoredCredential } from "../../../common/utils/itwTypesUtils";
import { sessionTokenSelector } from "../../../../../store/reducers/authentication";
import { handleWalletInstanceResetSaga } from "../handleWalletInstanceResetSaga";
-import { itwIsWalletInstanceAttestationValidSelector } from "../../../walletInstance/store/reducers";
import { ensureIntegrityServiceIsReady } from "../../../common/utils/itwIntegrityUtils";
import { itwIntegrityServiceReadySelector } from "../../../issuance/store/selectors";
+import { itwIsWalletInstanceAttestationValidSelector } from "../../../walletInstance/store/selectors";
jest.mock("@pagopa/io-react-native-crypto", () => ({
deleteKey: jest.fn
diff --git a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts
index 779a11a929e..07d6dc12719 100644
--- a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts
+++ b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts
@@ -8,6 +8,7 @@ import { ensureIntegrityServiceIsReady } from "../../common/utils/itwIntegrityUt
import { itwIntegrityKeyTagSelector } from "../../issuance/store/selectors";
import { itwLifecycleIsOperationalOrValid } from "../store/selectors";
import { itwIntegritySetServiceIsReady } from "../../issuance/store/actions";
+import { itwUpdateWalletInstanceStatus } from "../../walletInstance/store/actions";
import { handleWalletInstanceResetSaga } from "./handleWalletInstanceResetSaga";
export function* getStatusOrResetWalletInstance(integrityKeyTag: string) {
@@ -23,6 +24,9 @@ export function* getStatusOrResetWalletInstance(integrityKeyTag: string) {
if (walletInstanceStatus.is_revoked) {
yield* call(handleWalletInstanceResetSaga);
}
+
+ // Update wallet instance status
+ yield* put(itwUpdateWalletInstanceStatus(walletInstanceStatus));
}
/**
diff --git a/ts/features/itwallet/machine/credential/actions.ts b/ts/features/itwallet/machine/credential/actions.ts
index f5f1eaf9990..ee43befc3b9 100644
--- a/ts/features/itwallet/machine/credential/actions.ts
+++ b/ts/features/itwallet/machine/credential/actions.ts
@@ -19,7 +19,7 @@ import {
import { itwCredentialsStore } from "../../credentials/store/actions";
import { ITW_ROUTES } from "../../navigation/routes";
import { itwWalletInstanceAttestationStore } from "../../walletInstance/store/actions";
-import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/reducers";
+import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/selectors";
import { Context } from "./context";
import { CredentialIssuanceEvents } from "./events";
diff --git a/ts/features/itwallet/machine/eid/actions.ts b/ts/features/itwallet/machine/eid/actions.ts
index d7581cc2b7a..9b84d7a1e23 100644
--- a/ts/features/itwallet/machine/eid/actions.ts
+++ b/ts/features/itwallet/machine/eid/actions.ts
@@ -22,8 +22,8 @@ import {
trackSaveCredentialSuccess,
updateITWStatusAndIDProperties
} from "../../analytics";
-import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/reducers";
import { itwIntegrityKeyTagSelector } from "../../issuance/store/selectors";
+import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/selectors";
import { Context } from "./context";
import { EidIssuanceEvents } from "./events";
diff --git a/ts/features/itwallet/trustmark/machine/actions.ts b/ts/features/itwallet/trustmark/machine/actions.ts
index 929d2d32104..145a4ba00fd 100644
--- a/ts/features/itwallet/trustmark/machine/actions.ts
+++ b/ts/features/itwallet/trustmark/machine/actions.ts
@@ -4,7 +4,7 @@ import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { checkCurrentSession } from "../../../../store/actions/authentication";
import { useIOStore } from "../../../../store/hooks";
import { itwCredentialByTypeSelector } from "../../credentials/store/selectors";
-import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/reducers";
+import { itwWalletInstanceAttestationSelector } from "../../walletInstance/store/selectors";
import { Context } from "./context";
export const createItwTrustmarkActionsImplementation = (
diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts
new file mode 100644
index 00000000000..15698a1baf2
--- /dev/null
+++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts
@@ -0,0 +1,101 @@
+import { Alert } from "react-native";
+import { useCallback } from "react";
+import I18n from "../../../../i18n";
+import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils";
+import { useIODispatch, useIOSelector } from "../../../../store/hooks";
+import { itwWalletInstanceStatusSelector } from "../store/selectors";
+import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender";
+import { itwUpdateWalletInstanceStatus } from "../store/actions";
+import { openWebUrl } from "../../../../utils/url";
+
+const closeButtonText = I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.closeButton"
+);
+const alertCtaText = I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.cta"
+);
+
+const itwMinIntegrityReqUrl = "https://io.italia.it/documenti-su-io/faq/#n1_12";
+const itwDocsOnIOMultipleDevicesUrl =
+ "https://io.italia.it/documenti-su-io/faq/#n1_14";
+
+/**
+ * Hook to monitor wallet instance status and display alerts if revoked.
+ */
+export const useItwWalletInstanceRevocationAlert = () => {
+ const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector);
+ const dispatch = useIODispatch();
+
+ useOnFirstRender(
+ useCallback(() => {
+ if (walletInstanceStatus?.is_revoked) {
+ showWalletRevocationAlert(walletInstanceStatus.revocation_reason);
+ dispatch(itwUpdateWalletInstanceStatus(undefined));
+ }
+ }, [walletInstanceStatus, dispatch])
+ );
+};
+
+/**
+ * Displays an alert based on the revocation reason.
+ */
+const showWalletRevocationAlert = (
+ revocationReason?: WalletInstanceRevocationReason
+) => {
+ switch (revocationReason) {
+ case "CERTIFICATE_REVOKED_BY_ISSUER":
+ Alert.alert(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.content"
+ ),
+ [
+ { text: closeButtonText },
+ {
+ text: alertCtaText,
+ onPress: () => openWebUrl(itwMinIntegrityReqUrl)
+ }
+ ]
+ );
+ break;
+
+ case "NEW_WALLET_INSTANCE_CREATED":
+ Alert.alert(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.content"
+ ),
+ [
+ { text: closeButtonText },
+ {
+ text: alertCtaText,
+ onPress: () => openWebUrl(itwDocsOnIOMultipleDevicesUrl)
+ }
+ ]
+ );
+ break;
+ case "REVOKED_BY_USER":
+ Alert.alert(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByUser.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByUser.content"
+ ),
+ [
+ {
+ text: I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.closeButtonAlt"
+ )
+ }
+ ]
+ );
+ break;
+ default:
+ break;
+ }
+};
diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts
index 3d0c05b05c2..cbf6db80535 100644
--- a/ts/features/itwallet/walletInstance/store/actions/index.ts
+++ b/ts/features/itwallet/walletInstance/store/actions/index.ts
@@ -1,4 +1,5 @@
import { ActionType, createStandardAction } from "typesafe-actions";
+import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils";
/**
* This action stores the Wallet Instance Attestation
@@ -7,6 +8,13 @@ export const itwWalletInstanceAttestationStore = createStandardAction(
"ITW_WALLET_INSTANCE_ATTESTATION_STORE"
)();
-export type ItwWalletInstanceActions = ActionType<
- typeof itwWalletInstanceAttestationStore
->;
+/**
+ * This action update the Wallet Instance Status
+ */
+export const itwUpdateWalletInstanceStatus = createStandardAction(
+ "ITW_WALLET_INSTANCE_STATUS_UPDATE"
+)();
+
+export type ItwWalletInstanceActions =
+ | ActionType
+ | ActionType;
diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts
index 2494833ff35..778d134d4fb 100644
--- a/ts/features/itwallet/walletInstance/store/reducers/index.ts
+++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts
@@ -1,21 +1,22 @@
-import * as O from "fp-ts/lib/Option";
-import { flow } from "fp-ts/lib/function";
import { PersistConfig, persistReducer } from "redux-persist";
-import { createSelector } from "reselect";
import { getType } from "typesafe-actions";
import { Action } from "../../../../../store/actions/types";
-import { GlobalState } from "../../../../../store/reducers/types";
import itwCreateSecureStorage from "../../../common/store/storages/itwSecureStorage";
-import { isWalletInstanceAttestationValid } from "../../../common/utils/itwAttestationUtils";
import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions";
-import { itwWalletInstanceAttestationStore } from "../actions";
+import {
+ itwWalletInstanceAttestationStore,
+ itwUpdateWalletInstanceStatus
+} from "../actions";
+import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils";
export type ItwWalletInstanceState = {
attestation: string | undefined;
+ status: WalletInstanceStatus | undefined;
};
export const itwWalletInstanceInitialState: ItwWalletInstanceState = {
- attestation: undefined
+ attestation: undefined,
+ status: undefined
};
const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1;
@@ -27,10 +28,18 @@ const reducer = (
switch (action.type) {
case getType(itwWalletInstanceAttestationStore): {
return {
+ status: undefined,
attestation: action.payload
};
}
+ case getType(itwUpdateWalletInstanceStatus): {
+ return {
+ ...state,
+ status: action.payload
+ };
+ }
+
case getType(itwLifecycleStoresReset):
return { ...itwWalletInstanceInitialState };
@@ -50,16 +59,4 @@ const persistedReducer = persistReducer(
reducer
);
-export const itwWalletInstanceAttestationSelector = (state: GlobalState) =>
- state.features.itWallet.walletInstance.attestation;
-
-export const itwIsWalletInstanceAttestationValidSelector = createSelector(
- itwWalletInstanceAttestationSelector,
- flow(
- O.fromNullable,
- O.map(isWalletInstanceAttestationValid),
- O.getOrElse(() => false)
- )
-);
-
export default persistedReducer;
diff --git a/ts/features/itwallet/walletInstance/store/selectors/index.ts b/ts/features/itwallet/walletInstance/store/selectors/index.ts
new file mode 100644
index 00000000000..85a98084d61
--- /dev/null
+++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts
@@ -0,0 +1,23 @@
+import * as O from "fp-ts/lib/Option";
+import { flow } from "fp-ts/lib/function";
+import { createSelector } from "reselect";
+import { GlobalState } from "../../../../../store/reducers/types";
+import { isWalletInstanceAttestationValid } from "../../../common/utils/itwAttestationUtils";
+
+/* Selector to get the wallet instance attestation */
+export const itwWalletInstanceAttestationSelector = (state: GlobalState) =>
+ state.features.itWallet.walletInstance.attestation;
+
+/* Selector to check if the attestation is valid */
+export const itwIsWalletInstanceAttestationValidSelector = createSelector(
+ itwWalletInstanceAttestationSelector,
+ flow(
+ O.fromNullable,
+ O.map(isWalletInstanceAttestationValid),
+ O.getOrElse(() => false)
+ )
+);
+
+/* Selector to get the wallet instance status */
+export const itwWalletInstanceStatusSelector = (state: GlobalState) =>
+ state.features.itWallet.walletInstance.status;
diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx
index 898245b9a9b..8b45cb2b996 100644
--- a/ts/features/wallet/components/WalletCardsContainer.tsx
+++ b/ts/features/wallet/components/WalletCardsContainer.tsx
@@ -28,6 +28,7 @@ import {
shouldRenderWalletEmptyStateSelector
} from "../store/selectors";
import { WalletCardCategoryFilter } from "../types";
+import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert";
import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer";
import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner";
import { WalletCardSkeleton } from "./WalletCardSkeleton";
@@ -48,6 +49,8 @@ const WalletCardsContainer = () => {
shouldRenderWalletEmptyStateSelector
);
+ useItwWalletInstanceRevocationAlert();
+
// Loading state is only displayed if there is the initial loading and there are no cards or
// placeholders in the wallet
const shouldRenderLoadingState = isLoading && isWalletEmpty;
diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx
index 9c0b2bd0a05..c78c5bc00e9 100644
--- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx
+++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx
@@ -3,6 +3,7 @@ import _ from "lodash";
import { ComponentType } from "react";
import configureMockStore from "redux-mock-store";
+import { Alert } from "react-native";
import ROUTES from "../../../../navigation/routes";
import { applicationChangeState } from "../../../../store/actions/application";
import { appReducer } from "../../../../store/reducers";
@@ -17,6 +18,7 @@ import {
import { ItwJwtCredentialStatus } from "../../../itwallet/common/utils/itwTypesUtils";
import * as itwCredentialsSelectors from "../../../itwallet/credentials/store/selectors";
import * as itwLifecycleSelectors from "../../../itwallet/lifecycle/store/selectors";
+import * as itwWalletInstanceSelectors from "../../../itwallet/walletInstance/store/selectors";
import { WalletCardsState } from "../../store/reducers/cards";
import * as walletSelectors from "../../store/selectors";
import { WalletCard } from "../../types";
@@ -25,7 +27,9 @@ import {
OtherWalletCardsContainer,
WalletCardsContainer
} from "../WalletCardsContainer";
+import I18n from "../../../../i18n";
+jest.spyOn(Alert, "alert");
jest.mock("react-native-reanimated", () => ({
...require("react-native-reanimated/mock"),
useReducedMotion: jest.fn,
@@ -358,6 +362,112 @@ describe("OtherWalletCardsContainer", () => {
expect(queryByTestId(`walletCardTestID_cgn_cgn_3`)).not.toBeNull();
expect(queryByTestId(`walletCardTestID_itw_placeholder_4`)).not.toBeNull();
});
+
+ it("should not show alert if not revoked", () => {
+ jest
+ .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector")
+ .mockImplementation(() => ({
+ id: "39cc62ab-1df0-4a9d-974d-4c58173a1750",
+ is_revoked: false,
+ revocation_reason: undefined
+ }));
+
+ renderComponent(WalletCardsContainer);
+
+ expect(Alert.alert).not.toHaveBeenCalled();
+ });
+
+ it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => {
+ jest
+ .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector")
+ .mockImplementation(() => ({
+ id: "39cc62ab-1df0-4a9d-974d-4c58173a1750",
+ is_revoked: true,
+ revocation_reason: "NEW_WALLET_INSTANCE_CREATED"
+ }));
+
+ renderComponent(WalletCardsContainer);
+
+ expect(Alert.alert).toHaveBeenCalledWith(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.content"
+ ),
+ [
+ {
+ text: I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.closeButton"
+ )
+ },
+ {
+ text: I18n.t("features.itWallet.walletInstanceRevoked.alert.cta"),
+ onPress: expect.any(Function)
+ }
+ ]
+ );
+ });
+
+ it("should show alert for CERTIFICATE_REVOKED_BY_ISSUER", () => {
+ jest
+ .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector")
+ .mockImplementation(() => ({
+ id: "39cc62ab-1df0-4a9d-974d-4c58173a1750",
+ is_revoked: true,
+ revocation_reason: "CERTIFICATE_REVOKED_BY_ISSUER"
+ }));
+
+ renderComponent(WalletCardsContainer);
+
+ expect(Alert.alert).toHaveBeenCalledWith(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.content"
+ ),
+ [
+ {
+ text: I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.closeButton"
+ )
+ },
+ {
+ text: I18n.t("features.itWallet.walletInstanceRevoked.alert.cta"),
+ onPress: expect.any(Function)
+ }
+ ]
+ );
+ });
+
+ it("should show alert for REVOKED_BY_USER", () => {
+ jest
+ .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector")
+ .mockImplementation(() => ({
+ id: "39cc62ab-1df0-4a9d-974d-4c58173a1750",
+ is_revoked: true,
+ revocation_reason: "REVOKED_BY_USER"
+ }));
+
+ renderComponent(WalletCardsContainer);
+
+ expect(Alert.alert).toHaveBeenCalledWith(
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByUser.title"
+ ),
+ I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.revokedByUser.content"
+ ),
+ [
+ {
+ text: I18n.t(
+ "features.itWallet.walletInstanceRevoked.alert.closeButtonAlt"
+ )
+ }
+ ]
+ );
+ });
});
const renderComponent = (component: ComponentType) => {