From f1fc15b76a1404978010419a9b5a3e410bb98b71 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 10 Dec 2024 11:11:45 +0100 Subject: [PATCH 01/24] chore: add to wallet instance state also revocation props, initialized in itwUpdateWalletInstanceStatus --- .../common/utils/itwAttestationUtils.ts | 14 ++++++ .../saga/checkWalletInstanceStateSaga.ts | 3 ++ .../walletInstance/store/actions/index.ts | 11 +++-- .../walletInstance/store/reducers/index.ts | 48 +++++++++++++++++-- 4 files changed, 69 insertions(+), 7 deletions(-) diff --git a/ts/features/itwallet/common/utils/itwAttestationUtils.ts b/ts/features/itwallet/common/utils/itwAttestationUtils.ts index a28f06739b5..c47e5ade755 100644 --- a/ts/features/itwallet/common/utils/itwAttestationUtils.ts +++ b/ts/features/itwallet/common/utils/itwAttestationUtils.ts @@ -7,6 +7,7 @@ import { import { itwWalletProviderBaseUrl } from "../../../../config"; import { SessionToken } from "../../../../types/SessionToken"; import { createItWalletFetch } from "../../api/client"; +import { RevocationReason } from "../../walletInstance/store/reducers"; import { regenerateCryptoKey, WIA_KEYTAG } from "./itwCryptoContextUtils"; import { generateIntegrityHardwareKeyTag, @@ -91,3 +92,16 @@ export const getWalletInstanceStatus = ( walletProviderBaseUrl: itwWalletProviderBaseUrl, appFetch: createItWalletFetch(itwWalletProviderBaseUrl, sessionToken) }); + +/** + * Map an optional string `revocationReason` to a value of the `RevocationReason` enum. + * @param revocationReason - An optional string representing the reason for revocation. + * @returns A value of the `RevocationReason` enum if the string `revocationReason` + * matches one of the enum values, otherwise `undefined`. + */ +export const toRevocationReason = ( + revocationReason?: string +): RevocationReason | undefined => + Object.values(RevocationReason).includes(revocationReason as RevocationReason) + ? (revocationReason as RevocationReason) + : undefined; diff --git a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts index 779a11a929e..50b25eda00e 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) { @@ -20,6 +21,8 @@ export function* getStatusOrResetWalletInstance(integrityKeyTag: string) { sessionToken ); + yield* put(itwUpdateWalletInstanceStatus(walletInstanceStatus)); + if (walletInstanceStatus.is_revoked) { yield* call(handleWalletInstanceResetSaga); } diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 3d0c05b05c2..5ff435853cf 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -1,3 +1,4 @@ +import { WalletInstanceData } from "@pagopa/io-react-native-wallet/lib/typescript/client/generated/wallet-provider"; import { ActionType, createStandardAction } from "typesafe-actions"; /** @@ -7,6 +8,10 @@ export const itwWalletInstanceAttestationStore = createStandardAction( "ITW_WALLET_INSTANCE_ATTESTATION_STORE" )(); -export type ItwWalletInstanceActions = ActionType< - typeof itwWalletInstanceAttestationStore ->; +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..3efaad5b8b0 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -1,21 +1,37 @@ import * as O from "fp-ts/lib/Option"; -import { flow } from "fp-ts/lib/function"; +import { flow, pipe } 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 { + isWalletInstanceAttestationValid, + toRevocationReason +} from "../../../common/utils/itwAttestationUtils"; import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions"; -import { itwWalletInstanceAttestationStore } from "../actions"; +import { + itwWalletInstanceAttestationStore, + itwUpdateWalletInstanceStatus +} from "../actions"; + +export enum RevocationReason { + CERTIFICATE_REVOKED_BY_ISSUER = "CERTIFICATE_REVOKED_BY_ISSUER", + NEW_WALLET_INSTANCE_CREATED = "NEW_WALLET_INSTANCE_CREATED", + REVOKED_BY_USER = "REVOKED_BY_USER" +} export type ItwWalletInstanceState = { attestation: string | undefined; + isRevoked: boolean; + revocationReason?: RevocationReason; }; export const itwWalletInstanceInitialState: ItwWalletInstanceState = { - attestation: undefined + attestation: undefined, + isRevoked: false, + revocationReason: undefined }; const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1; @@ -27,10 +43,19 @@ const reducer = ( switch (action.type) { case getType(itwWalletInstanceAttestationStore): { return { + ...state, attestation: action.payload }; } + case getType(itwUpdateWalletInstanceStatus): { + return { + ...state, + isRevoked: action.payload.is_revoked, + revocationReason: toRevocationReason(action.payload.revocation_reason) + }; + } + case getType(itwLifecycleStoresReset): return { ...itwWalletInstanceInitialState }; @@ -62,4 +87,19 @@ export const itwIsWalletInstanceAttestationValidSelector = createSelector( ) ); +/* Selector to get the wallet instance status */ +export const itwWalletInstanceStatusSelector = createSelector( + (state: GlobalState) => state.features.itWallet.walletInstance, + walletInstance => + pipe( + O.fromNullable(walletInstance.isRevoked), + O.filter(Boolean), + O.map(() => ({ + isRevoked: true, + revocationReason: walletInstance.revocationReason + })), + O.getOrElse(() => ({ isRevoked: false })) + ) +); + export default persistedReducer; From 50ec51970809b3ee989a353461017aeac2e56efd Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Thu, 12 Dec 2024 11:53:29 +0100 Subject: [PATCH 02/24] chore: add hook to display alert when wi is revoked, add test --- .env.local | 2 + .env.production | 2 + locales/en/index.yml | 14 +++ locales/it/index.yml | 14 +++ ts/config.ts | 5 + .../__snapshots__/index.test.ts.snap | 2 + .../__snapshots__/index.test.ts.snap | 2 + .../saga/checkWalletInstanceStateSaga.ts | 1 + .../useItwWalletInstanceRevocationAlert.ts | 107 ++++++++++++++++++ .../walletInstance/store/actions/index.ts | 3 + .../components/WalletCardsContainer.tsx | 5 + .../__tests__/WalletCardsContainer.test.tsx | 101 ++++++++++++++++- 12 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts diff --git a/.env.local b/.env.local index 6695a3aa390..a8da2ce39e2 100644 --- a/.env.local +++ b/.env.local @@ -101,3 +101,5 @@ ITW_IDP_HINT_TEST=YES 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 Minimum Integrity Requirements FAQ URL +ITW_MINIMUM_INTEGRITY_REQUIREMENTS='https://io.italia.it/documenti-su-io/faq/#n1_12 diff --git a/.env.production b/.env.production index 6b30017f516..730492396d9 100644 --- a/.env.production +++ b/.env.production @@ -101,3 +101,5 @@ ITW_IDP_HINT_TEST=NO 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 Minimum Integrity Requirements FAQ URL +ITW_MINIMUM_INTEGRITY_REQUIREMENTS='https://io.italia.it/documenti-su-io/faq/#n1_12 diff --git a/locales/en/index.yml b/locales/en/index.yml index f40212e068c..d225c1c3a71 100644 --- a/locales/en/index.yml +++ b/locales/en/index.yml @@ -3539,6 +3539,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 05e65c0a703..41811585ca7 100644 --- a/locales/it/index.yml +++ b/locales/it/index.yml @@ -3539,6 +3539,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/ts/config.ts b/ts/config.ts index 8223ea078fc..d76129e2285 100644 --- a/ts/config.ts +++ b/ts/config.ts @@ -259,3 +259,8 @@ export const itwDocumentsOnIOUrl: string = pipe( t.string.decode, E.getOrElse(() => "https://io.italia.it/documenti-su-io") ); +export const itwMinIntegrityReqURL: string = pipe( + Config.ITW_MINIMUM_INTEGRITY_REQUIREMENTS, + t.string.decode, + E.getOrElse(() => "https://io.italia.it/documenti-su-io/faq/#n1_12") +); 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 ac3359243a0..a0163e4e328 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 @@ -138,6 +138,8 @@ exports[`featuresPersistor should match snapshot 1`] = ` "preferences": {}, "walletInstance": { "attestation": undefined, + "isRevoked": false, + "revocationReason": 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 7d855751132..bbd058ef4d4 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 @@ -25,6 +25,8 @@ exports[`itWalletReducer should match snapshot 1`] = ` "preferences": {}, "walletInstance": { "attestation": undefined, + "isRevoked": false, + "revocationReason": undefined, }, } `; diff --git a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts index 50b25eda00e..cc038bccb4a 100644 --- a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts +++ b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts @@ -21,6 +21,7 @@ export function* getStatusOrResetWalletInstance(integrityKeyTag: string) { sessionToken ); + // Update wallet instance status yield* put(itwUpdateWalletInstanceStatus(walletInstanceStatus)); if (walletInstanceStatus.is_revoked) { diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts new file mode 100644 index 00000000000..5c890709fa4 --- /dev/null +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -0,0 +1,107 @@ +import { Alert, AlertButton, Linking } from "react-native"; +import React from "react"; +import { IOToast } from "@pagopa/io-app-design-system"; +import { RevocationReason } from "../store/reducers"; +import I18n from "../../../../i18n"; +import { itwMinIntegrityReqURL } from "../../../../config"; + +const closeButtonText = I18n.t( + "features.itWallet.walletInstanceRevoked.alert.closeButton" +); +const alertCtaText = I18n.t( + "features.itWallet.walletInstanceRevoked.alert.cta" +); + +/** + * Hook to monitor wallet instance status and display alerts if revoked. + * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. + */ +export const useItwWalletInstanceRevocationAlert = (walletInstanceStatus: { + isRevoked: boolean; + revocationReason?: RevocationReason; +}) => { + React.useEffect(() => { + if (walletInstanceStatus.isRevoked) { + showWalletRevocationAlert(walletInstanceStatus.revocationReason); + } + }, [walletInstanceStatus]); +}; + +/** + * Displays an alert based on the revocation reason. + */ +const showWalletRevocationAlert = (revocationReason?: RevocationReason) => { + switch (revocationReason) { + case RevocationReason.CERTIFICATE_REVOKED_BY_ISSUER: + showAlert( + I18n.t( + "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.title" + ), + I18n.t( + "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.content" + ), + [ + { text: closeButtonText }, + { + text: alertCtaText, + onPress: () => { + Linking.openURL(itwMinIntegrityReqURL).catch(() => { + IOToast.error(I18n.t("global.genericError")); + }); + } + } + ] + ); + break; + + case RevocationReason.NEW_WALLET_INSTANCE_CREATED: + showAlert( + I18n.t( + "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.title" + ), + I18n.t( + "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.content" + ), + [ + { text: closeButtonText }, + { + text: alertCtaText, + onPress: () => { + // TODO: Add the correct URL + Linking.openURL("").catch(() => { + IOToast.error(I18n.t("global.genericError")); + }); + } + } + ] + ); + break; + case RevocationReason.REVOKED_BY_USER: + showAlert( + 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; + } +}; + +const showAlert = ( + title: string, + message: string, + buttons: Array = [{ text: closeButtonText }] +) => { + Alert.alert(title, message, buttons); +}; diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 5ff435853cf..1b601dc764d 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -8,6 +8,9 @@ export const itwWalletInstanceAttestationStore = createStandardAction( "ITW_WALLET_INSTANCE_ATTESTATION_STORE" )(); +/** + * This action update the Wallet Instance Status + */ export const itwUpdateWalletInstanceStatus = createStandardAction( "ITW_WALLET_INSTANCE__STATUS_UPDATE" )(); diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index a4f2578c3d0..00aac41a2e2 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -33,6 +33,8 @@ import { import { WalletCardCategoryFilter } from "../types"; import { paymentsWalletUserMethodsSelector } from "../../payments/wallet/store/selectors"; import { cgnDetailSelector } from "../../bonus/cgn/store/reducers/details"; +import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/reducers"; +import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert"; import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer"; import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner"; import { WalletCardSkeleton } from "./WalletCardSkeleton"; @@ -46,6 +48,9 @@ const WalletCardsContainer = () => { const selectedCategory = useIOSelector(selectWalletCategoryFilter); const paymentMethodsStatus = useIOSelector(paymentsWalletUserMethodsSelector); const cgnStatus = useIOSelector(cgnDetailSelector); + const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); + + useItwWalletInstanceRevocationAlert(walletInstanceStatus); if (isLoading && cards.length === 0) { return ( diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx index 9ecb08d55d9..5dbd691be0e 100644 --- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx +++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx @@ -2,6 +2,7 @@ import * as O from "fp-ts/lib/Option"; import _ from "lodash"; import * as React from "react"; import configureMockStore from "redux-mock-store"; +import { Alert } from "react-native"; import { ToolEnum } from "../../../../../definitions/content/AssistanceToolConfig"; import { Config } from "../../../../../definitions/content/Config"; import ROUTES from "../../../../navigation/routes"; @@ -16,6 +17,8 @@ import { WalletCardsState } from "../../store/reducers/cards"; import { WalletPlaceholdersState } from "../../store/reducers/placeholders"; import { WalletCard } from "../../types"; import { WalletCardsContainer } from "../WalletCardsContainer"; +import { RevocationReason } from "../../../itwallet/walletInstance/store/reducers"; +import I18n from "../../../../i18n"; type RenderOptions = { cards?: WalletCardsState; @@ -23,8 +26,11 @@ type RenderOptions = { isItwEnabled?: boolean; isItwValid?: boolean; isWalletEmpty?: boolean; + isRevoked?: boolean; + revocationReason?: RevocationReason; }; +jest.spyOn(Alert, "alert"); jest.mock("react-native-reanimated", () => ({ ...require("react-native-reanimated/mock"), Layout: { @@ -163,6 +169,91 @@ describe("WalletCardsContainer", () => { expect(queryByTestId("itwWalletReadyBannerTestID")).not.toBeNull(); }); + it("should not show alert if not revoked", () => { + renderComponent({ + isRevoked: false + }); + + expect(Alert.alert).not.toHaveBeenCalled(); + }); + + it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => { + renderComponent({ + isRevoked: true, + revocationReason: RevocationReason.NEW_WALLET_INSTANCE_CREATED + }); + + 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", () => { + renderComponent({ + isRevoked: true, + revocationReason: RevocationReason.CERTIFICATE_REVOKED_BY_ISSUER + }); + + 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", () => { + renderComponent({ + isRevoked: true, + revocationReason: RevocationReason.REVOKED_BY_USER + }); + + 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" + ) + } + ] + ); + }); + test.each([ { isItwValid: false }, { isItwValid: true, isWalletEmpty: false } @@ -183,7 +274,9 @@ const renderComponent = ({ isItwEnabled = true, isItwValid = true, isLoading = false, - isWalletEmpty = true + isWalletEmpty = true, + isRevoked = false, + revocationReason = undefined }: RenderOptions) => { const globalState = appReducer(undefined, applicationChangeState("active")); @@ -206,7 +299,11 @@ const renderComponent = ({ ? [] : [O.some({ parsedCredential: {} })] }, - lifecycle: ItwLifecycleState.ITW_LIFECYCLE_VALID + lifecycle: ItwLifecycleState.ITW_LIFECYCLE_VALID, + walletInstance: { + isRevoked, + revocationReason + } }) } }, From 4e3857fe6e269a0ba81c0c05f285c6234659b6b8 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Fri, 13 Dec 2024 08:49:56 +0100 Subject: [PATCH 03/24] refactor: moved update wi status after reset --- .../itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts index cc038bccb4a..07d6dc12719 100644 --- a/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts +++ b/ts/features/itwallet/lifecycle/saga/checkWalletInstanceStateSaga.ts @@ -21,12 +21,12 @@ export function* getStatusOrResetWalletInstance(integrityKeyTag: string) { sessionToken ); - // Update wallet instance status - yield* put(itwUpdateWalletInstanceStatus(walletInstanceStatus)); - if (walletInstanceStatus.is_revoked) { yield* call(handleWalletInstanceResetSaga); } + + // Update wallet instance status + yield* put(itwUpdateWalletInstanceStatus(walletInstanceStatus)); } /** From c7694d0b6568365edf6063b846158df50e044493 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Fri, 13 Dec 2024 08:51:50 +0100 Subject: [PATCH 04/24] refactor: deleted env ITW_MINIMUM_INTEGRITY_REQUIREMENTS and instead use a constant itwMinIntergityReqUrl --- .env.local | 4 +--- .env.production | 2 -- ts/config.ts | 5 ----- .../hook/useItwWalletInstanceRevocationAlert.ts | 5 +++-- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/.env.local b/.env.local index a8da2ce39e2..c3dc056cdde 100644 --- a/.env.local +++ b/.env.local @@ -100,6 +100,4 @@ 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 Minimum Integrity Requirements FAQ URL -ITW_MINIMUM_INTEGRITY_REQUIREMENTS='https://io.italia.it/documenti-su-io/faq/#n1_12 +ITW_DOCUMENTS_ON_IO_URL='https://io.italia.it/documenti-su-io' \ No newline at end of file diff --git a/.env.production b/.env.production index 730492396d9..6b30017f516 100644 --- a/.env.production +++ b/.env.production @@ -101,5 +101,3 @@ ITW_IDP_HINT_TEST=NO 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 Minimum Integrity Requirements FAQ URL -ITW_MINIMUM_INTEGRITY_REQUIREMENTS='https://io.italia.it/documenti-su-io/faq/#n1_12 diff --git a/ts/config.ts b/ts/config.ts index d76129e2285..8223ea078fc 100644 --- a/ts/config.ts +++ b/ts/config.ts @@ -259,8 +259,3 @@ export const itwDocumentsOnIOUrl: string = pipe( t.string.decode, E.getOrElse(() => "https://io.italia.it/documenti-su-io") ); -export const itwMinIntegrityReqURL: string = pipe( - Config.ITW_MINIMUM_INTEGRITY_REQUIREMENTS, - t.string.decode, - E.getOrElse(() => "https://io.italia.it/documenti-su-io/faq/#n1_12") -); diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 5c890709fa4..35a1cb140a0 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -3,7 +3,6 @@ import React from "react"; import { IOToast } from "@pagopa/io-app-design-system"; import { RevocationReason } from "../store/reducers"; import I18n from "../../../../i18n"; -import { itwMinIntegrityReqURL } from "../../../../config"; const closeButtonText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.closeButton" @@ -12,6 +11,8 @@ const alertCtaText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.cta" ); +const itwMinIntergityReqUrl = "https://io.italia.it/documenti-su-io/faq/#n1_12"; + /** * Hook to monitor wallet instance status and display alerts if revoked. * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. @@ -45,7 +46,7 @@ const showWalletRevocationAlert = (revocationReason?: RevocationReason) => { { text: alertCtaText, onPress: () => { - Linking.openURL(itwMinIntegrityReqURL).catch(() => { + Linking.openURL(itwMinIntergityReqUrl).catch(() => { IOToast.error(I18n.t("global.genericError")); }); } From 19d91762ee96d8ac5877ee1cf861126edbdb82d9 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Fri, 13 Dec 2024 09:13:57 +0100 Subject: [PATCH 05/24] refactor: create type WalletInstanceStatus and WalletInstanceRevocationReason instead enum --- .../common/utils/itwAttestationUtils.ts | 14 -------------- .../itwallet/common/utils/itwTypesUtils.ts | 19 ++++++++++++++++++- .../useItwWalletInstanceRevocationAlert.ts | 14 ++++++++------ .../walletInstance/store/actions/index.ts | 6 +++--- .../walletInstance/store/reducers/index.ts | 16 ++++------------ 5 files changed, 33 insertions(+), 36 deletions(-) diff --git a/ts/features/itwallet/common/utils/itwAttestationUtils.ts b/ts/features/itwallet/common/utils/itwAttestationUtils.ts index c47e5ade755..a28f06739b5 100644 --- a/ts/features/itwallet/common/utils/itwAttestationUtils.ts +++ b/ts/features/itwallet/common/utils/itwAttestationUtils.ts @@ -7,7 +7,6 @@ import { import { itwWalletProviderBaseUrl } from "../../../../config"; import { SessionToken } from "../../../../types/SessionToken"; import { createItWalletFetch } from "../../api/client"; -import { RevocationReason } from "../../walletInstance/store/reducers"; import { regenerateCryptoKey, WIA_KEYTAG } from "./itwCryptoContextUtils"; import { generateIntegrityHardwareKeyTag, @@ -92,16 +91,3 @@ export const getWalletInstanceStatus = ( walletProviderBaseUrl: itwWalletProviderBaseUrl, appFetch: createItWalletFetch(itwWalletProviderBaseUrl, sessionToken) }); - -/** - * Map an optional string `revocationReason` to a value of the `RevocationReason` enum. - * @param revocationReason - An optional string representing the reason for revocation. - * @returns A value of the `RevocationReason` enum if the string `revocationReason` - * matches one of the enum values, otherwise `undefined`. - */ -export const toRevocationReason = ( - revocationReason?: string -): RevocationReason | undefined => - Object.values(RevocationReason).includes(revocationReason as RevocationReason) - ? (revocationReason as RevocationReason) - : 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/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 35a1cb140a0..d6909e824da 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -1,8 +1,8 @@ import { Alert, AlertButton, Linking } from "react-native"; import React from "react"; import { IOToast } from "@pagopa/io-app-design-system"; -import { RevocationReason } from "../store/reducers"; import I18n from "../../../../i18n"; +import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils"; const closeButtonText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.closeButton" @@ -19,7 +19,7 @@ const itwMinIntergityReqUrl = "https://io.italia.it/documenti-su-io/faq/#n1_12"; */ export const useItwWalletInstanceRevocationAlert = (walletInstanceStatus: { isRevoked: boolean; - revocationReason?: RevocationReason; + revocationReason?: WalletInstanceRevocationReason; }) => { React.useEffect(() => { if (walletInstanceStatus.isRevoked) { @@ -31,9 +31,11 @@ export const useItwWalletInstanceRevocationAlert = (walletInstanceStatus: { /** * Displays an alert based on the revocation reason. */ -const showWalletRevocationAlert = (revocationReason?: RevocationReason) => { +const showWalletRevocationAlert = ( + revocationReason?: WalletInstanceRevocationReason +) => { switch (revocationReason) { - case RevocationReason.CERTIFICATE_REVOKED_BY_ISSUER: + case "CERTIFICATE_REVOKED_BY_ISSUER": showAlert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.title" @@ -55,7 +57,7 @@ const showWalletRevocationAlert = (revocationReason?: RevocationReason) => { ); break; - case RevocationReason.NEW_WALLET_INSTANCE_CREATED: + case "NEW_WALLET_INSTANCE_CREATED": showAlert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.title" @@ -77,7 +79,7 @@ const showWalletRevocationAlert = (revocationReason?: RevocationReason) => { ] ); break; - case RevocationReason.REVOKED_BY_USER: + case "REVOKED_BY_USER": showAlert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.revokedByUser.title" diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 1b601dc764d..961813b9d01 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -1,5 +1,5 @@ -import { WalletInstanceData } from "@pagopa/io-react-native-wallet/lib/typescript/client/generated/wallet-provider"; import { ActionType, createStandardAction } from "typesafe-actions"; +import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils"; /** * This action stores the Wallet Instance Attestation @@ -12,8 +12,8 @@ export const itwWalletInstanceAttestationStore = createStandardAction( * This action update the Wallet Instance Status */ export const itwUpdateWalletInstanceStatus = createStandardAction( - "ITW_WALLET_INSTANCE__STATUS_UPDATE" -)(); + "ITW_WALLET_INSTANCE_STATUS_UPDATE" +)(); export type ItwWalletInstanceActions = | ActionType diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 3efaad5b8b0..5c600326135 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -6,26 +6,18 @@ 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, - toRevocationReason -} from "../../../common/utils/itwAttestationUtils"; +import { isWalletInstanceAttestationValid } from "../../../common/utils/itwAttestationUtils"; import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions"; import { itwWalletInstanceAttestationStore, itwUpdateWalletInstanceStatus } from "../actions"; - -export enum RevocationReason { - CERTIFICATE_REVOKED_BY_ISSUER = "CERTIFICATE_REVOKED_BY_ISSUER", - NEW_WALLET_INSTANCE_CREATED = "NEW_WALLET_INSTANCE_CREATED", - REVOKED_BY_USER = "REVOKED_BY_USER" -} +import { WalletInstanceRevocationReason } from "../../../common/utils/itwTypesUtils"; export type ItwWalletInstanceState = { attestation: string | undefined; isRevoked: boolean; - revocationReason?: RevocationReason; + revocationReason?: WalletInstanceRevocationReason; }; export const itwWalletInstanceInitialState: ItwWalletInstanceState = { @@ -52,7 +44,7 @@ const reducer = ( return { ...state, isRevoked: action.payload.is_revoked, - revocationReason: toRevocationReason(action.payload.revocation_reason) + revocationReason: action.payload.revocation_reason }; } From 7071c48c8fe1051340ed9fc24fc17201ebccf3ef Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Fri, 13 Dec 2024 09:22:26 +0100 Subject: [PATCH 06/24] chore: fix test --- .../components/__tests__/WalletCardsContainer.test.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx index 5dbd691be0e..63a66ba4dd0 100644 --- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx +++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx @@ -17,8 +17,8 @@ import { WalletCardsState } from "../../store/reducers/cards"; import { WalletPlaceholdersState } from "../../store/reducers/placeholders"; import { WalletCard } from "../../types"; import { WalletCardsContainer } from "../WalletCardsContainer"; -import { RevocationReason } from "../../../itwallet/walletInstance/store/reducers"; import I18n from "../../../../i18n"; +import { WalletInstanceRevocationReason } from "../../../itwallet/common/utils/itwTypesUtils"; type RenderOptions = { cards?: WalletCardsState; @@ -27,7 +27,7 @@ type RenderOptions = { isItwValid?: boolean; isWalletEmpty?: boolean; isRevoked?: boolean; - revocationReason?: RevocationReason; + revocationReason?: WalletInstanceRevocationReason; }; jest.spyOn(Alert, "alert"); @@ -180,7 +180,7 @@ describe("WalletCardsContainer", () => { it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => { renderComponent({ isRevoked: true, - revocationReason: RevocationReason.NEW_WALLET_INSTANCE_CREATED + revocationReason: "NEW_WALLET_INSTANCE_CREATED" }); expect(Alert.alert).toHaveBeenCalledWith( @@ -207,7 +207,7 @@ describe("WalletCardsContainer", () => { it("should show alert for CERTIFICATE_REVOKED_BY_ISSUER", () => { renderComponent({ isRevoked: true, - revocationReason: RevocationReason.CERTIFICATE_REVOKED_BY_ISSUER + revocationReason: "CERTIFICATE_REVOKED_BY_ISSUER" }); expect(Alert.alert).toHaveBeenCalledWith( @@ -234,7 +234,7 @@ describe("WalletCardsContainer", () => { it("should show alert for REVOKED_BY_USER", () => { renderComponent({ isRevoked: true, - revocationReason: RevocationReason.REVOKED_BY_USER + revocationReason: "REVOKED_BY_USER" }); expect(Alert.alert).toHaveBeenCalledWith( From 40bb43cb9a18f9f0bfd72d50d71621831f3e59c9 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Fri, 13 Dec 2024 15:31:06 +0100 Subject: [PATCH 07/24] refactor: create selector folder for wallet instance, add test WalletCardsContainer --- .../checkWalletInstanceStateSaga.test.ts | 2 +- .../itwallet/machine/credential/actions.ts | 2 +- ts/features/itwallet/machine/eid/actions.ts | 2 +- .../itwallet/trustmark/machine/actions.ts | 2 +- .../walletInstance/store/reducers/index.ts | 32 ------ .../walletInstance/store/selectors/index.ts | 34 ++++++ .../components/WalletCardsContainer.tsx | 2 +- .../__tests__/WalletCardsContainer.test.tsx | 106 ++++++++++++++++++ 8 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 ts/features/itwallet/walletInstance/store/selectors/index.ts 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/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 77e92063130..7149ce8ca9e 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/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 5c600326135..439e7dfbdf3 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -1,12 +1,7 @@ -import * as O from "fp-ts/lib/Option"; -import { flow, pipe } 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, @@ -67,31 +62,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) - ) -); - -/* Selector to get the wallet instance status */ -export const itwWalletInstanceStatusSelector = createSelector( - (state: GlobalState) => state.features.itWallet.walletInstance, - walletInstance => - pipe( - O.fromNullable(walletInstance.isRevoked), - O.filter(Boolean), - O.map(() => ({ - isRevoked: true, - revocationReason: walletInstance.revocationReason - })), - O.getOrElse(() => ({ isRevoked: 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..f918041c1f2 --- /dev/null +++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts @@ -0,0 +1,34 @@ +import * as O from "fp-ts/lib/Option"; +import { flow, pipe } 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 = createSelector( + (state: GlobalState) => state.features.itWallet.walletInstance, + walletInstance => + pipe( + O.fromNullable(walletInstance.isRevoked), + O.filter(Boolean), + O.map(() => ({ + isRevoked: true, + revocationReason: walletInstance.revocationReason + })), + O.getOrElse(() => ({ isRevoked: false })) + ) +); diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index 390779c8136..22765c0f3e0 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -28,8 +28,8 @@ import { shouldRenderWalletEmptyStateSelector } from "../store/selectors"; import { WalletCardCategoryFilter } from "../types"; -import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/reducers"; import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert"; +import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/selectors"; import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer"; import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner"; import { WalletCardSkeleton } from "./WalletCardSkeleton"; diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx index a10f9fd4446..cb36b9c5aa5 100644 --- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx +++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx @@ -2,6 +2,7 @@ import * as O from "fp-ts/lib/Option"; import _ from "lodash"; import * as React 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"; @@ -16,6 +17,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"; @@ -24,7 +26,9 @@ import { OtherWalletCardsContainer, WalletCardsContainer } from "../WalletCardsContainer"; +import I18n from "../../../../i18n"; +jest.spyOn(Alert, "alert"); jest.mock("react-native-reanimated", () => ({ ...require("react-native-reanimated/mock"), Layout: { @@ -356,6 +360,108 @@ 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(() => ({ + isRevoked: false, + revocationReason: "" + })); + + renderComponent(WalletCardsContainer); + + expect(Alert.alert).not.toHaveBeenCalled(); + }); + + it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => { + jest + .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") + .mockImplementation(() => ({ + isRevoked: true, + revocationReason: "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(() => ({ + isRevoked: true, + revocationReason: "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(() => ({ + isRevoked: true, + revocationReason: "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: React.ComponentType) => { From e20bee2596246bdc0a49d2eaefe5b39ff20d031a Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Mon, 16 Dec 2024 15:53:21 +0100 Subject: [PATCH 08/24] refactor: removed unused env --- .env.local | 4 +--- .env.production | 4 +--- ts/config.ts | 5 ----- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.env.local b/.env.local index ea35b273a48..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' \ No newline at end of file +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/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") -); From 06939b7637525ef3ab2c01805161f71df59a76ed Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 08:58:13 +0100 Subject: [PATCH 09/24] chore: typo --- .../walletInstance/hook/useItwWalletInstanceRevocationAlert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index d6909e824da..413674e1b4d 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -11,7 +11,7 @@ const alertCtaText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.cta" ); -const itwMinIntergityReqUrl = "https://io.italia.it/documenti-su-io/faq/#n1_12"; +const itwMinIntegrityReqUrl = "https://io.italia.it/documenti-su-io/faq/#n1_12"; /** * Hook to monitor wallet instance status and display alerts if revoked. From 260a57a01890caa62ecd23d73061215c886b7f20 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 08:59:07 +0100 Subject: [PATCH 10/24] chore: typo --- .../walletInstance/hook/useItwWalletInstanceRevocationAlert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 413674e1b4d..1c3251580bb 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -48,7 +48,7 @@ const showWalletRevocationAlert = ( { text: alertCtaText, onPress: () => { - Linking.openURL(itwMinIntergityReqUrl).catch(() => { + Linking.openURL(itwMinIntegrityReqUrl).catch(() => { IOToast.error(I18n.t("global.genericError")); }); } From 1728866dc1995f81a750fa747826b58c7ea37cc6 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 12:59:50 +0100 Subject: [PATCH 11/24] chore: add revocation object in wallet instance global state, including also alert shown that handle only the first visualizazion of the revocation alert --- .../useItwWalletInstanceRevocationAlert.ts | 11 +++++-- .../walletInstance/store/actions/index.ts | 10 +++++- .../walletInstance/store/reducers/index.ts | 33 +++++++++++++++---- .../walletInstance/store/selectors/index.ts | 16 ++++++--- .../components/WalletCardsContainer.tsx | 6 ++-- .../__tests__/WalletCardsContainer.test.tsx | 20 ++++++++--- 6 files changed, 75 insertions(+), 21 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 1c3251580bb..6bef54b11e4 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -3,6 +3,9 @@ import React from "react"; import { IOToast } from "@pagopa/io-app-design-system"; import I18n from "../../../../i18n"; import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils"; +import { useIODispatch, useIOSelector } from "../../../../store/hooks"; +import { itwWalletInstanceAlertShownSelector } from "../store/selectors"; +import { itwWalletInstanceSetAlertShown } from "../store/actions"; const closeButtonText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.closeButton" @@ -21,11 +24,15 @@ export const useItwWalletInstanceRevocationAlert = (walletInstanceStatus: { isRevoked: boolean; revocationReason?: WalletInstanceRevocationReason; }) => { + const dispatch = useIODispatch(); + const alertShown = useIOSelector(itwWalletInstanceAlertShownSelector); + React.useEffect(() => { - if (walletInstanceStatus.isRevoked) { + if (walletInstanceStatus.isRevoked && !alertShown) { showWalletRevocationAlert(walletInstanceStatus.revocationReason); + dispatch(itwWalletInstanceSetAlertShown()); } - }, [walletInstanceStatus]); + }, [walletInstanceStatus, alertShown, dispatch]); }; /** diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 961813b9d01..739f0e9d74b 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -15,6 +15,14 @@ export const itwUpdateWalletInstanceStatus = createStandardAction( "ITW_WALLET_INSTANCE_STATUS_UPDATE" )(); +/** + * This action sets the wallet instance alert shown + */ +export const itwWalletInstanceSetAlertShown = createStandardAction( + "ITW_WALLET_INSTANCE_SET_ALERT_SHOWN" +)(); + export type ItwWalletInstanceActions = | ActionType - | ActionType; + | ActionType + | ActionType; diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 439e7dfbdf3..ba97e93a588 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -5,20 +5,27 @@ import itwCreateSecureStorage from "../../../common/store/storages/itwSecureStor import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions"; import { itwWalletInstanceAttestationStore, - itwUpdateWalletInstanceStatus + itwUpdateWalletInstanceStatus, + itwWalletInstanceSetAlertShown } from "../actions"; import { WalletInstanceRevocationReason } from "../../../common/utils/itwTypesUtils"; export type ItwWalletInstanceState = { attestation: string | undefined; - isRevoked: boolean; - revocationReason?: WalletInstanceRevocationReason; + revocation: { + isRevoked: boolean; + revocationReason?: WalletInstanceRevocationReason; + alertShown: boolean; + }; }; export const itwWalletInstanceInitialState: ItwWalletInstanceState = { attestation: undefined, - isRevoked: false, - revocationReason: undefined + revocation: { + isRevoked: false, + revocationReason: undefined, + alertShown: false + } }; const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1; @@ -38,11 +45,23 @@ const reducer = ( case getType(itwUpdateWalletInstanceStatus): { return { ...state, - isRevoked: action.payload.is_revoked, - revocationReason: action.payload.revocation_reason + revocation: { + ...state.revocation, + isRevoked: action.payload.is_revoked, + revocationReason: action.payload.revocation_reason + } }; } + case getType(itwWalletInstanceSetAlertShown): + return { + ...state, + revocation: { + ...state.revocation, + alertShown: true + } + }; + case getType(itwLifecycleStoresReset): return { ...itwWalletInstanceInitialState }; diff --git a/ts/features/itwallet/walletInstance/store/selectors/index.ts b/ts/features/itwallet/walletInstance/store/selectors/index.ts index f918041c1f2..c4397515279 100644 --- a/ts/features/itwallet/walletInstance/store/selectors/index.ts +++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts @@ -18,16 +18,22 @@ export const itwIsWalletInstanceAttestationValidSelector = createSelector( ) ); +/* Selector to get the alert shown status */ +export const itwWalletInstanceAlertShownSelector = createSelector( + (state: GlobalState) => state.features.itWallet.walletInstance.revocation, + revocation => revocation.alertShown +); + /* Selector to get the wallet instance status */ -export const itwWalletInstanceStatusSelector = createSelector( - (state: GlobalState) => state.features.itWallet.walletInstance, - walletInstance => +export const itwWalletInstanceRevocationStatusSelector = createSelector( + (state: GlobalState) => state.features.itWallet.walletInstance.revocation, + revocation => pipe( - O.fromNullable(walletInstance.isRevoked), + O.fromNullable(revocation.isRevoked), O.filter(Boolean), O.map(() => ({ isRevoked: true, - revocationReason: walletInstance.revocationReason + revocationReason: revocation.revocationReason })), O.getOrElse(() => ({ isRevoked: false })) ) diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index 0766f86a430..d3e37adf1b9 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -29,7 +29,7 @@ import { } from "../store/selectors"; import { WalletCardCategoryFilter } from "../types"; import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert"; -import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/selectors"; +import { itwWalletInstanceRevocationStatusSelector } from "../../itwallet/walletInstance/store/selectors"; import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer"; import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner"; import { WalletCardSkeleton } from "./WalletCardSkeleton"; @@ -49,7 +49,9 @@ const WalletCardsContainer = () => { const shouldRenderEmptyState = useIOSelector( shouldRenderWalletEmptyStateSelector ); - const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); + const walletInstanceStatus = useIOSelector( + itwWalletInstanceRevocationStatusSelector + ); useItwWalletInstanceRevocationAlert(walletInstanceStatus); diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx index 428bfdafaf4..a68f057d260 100644 --- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx +++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx @@ -364,7 +364,10 @@ describe("OtherWalletCardsContainer", () => { it("should not show alert if not revoked", () => { jest - .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") + .spyOn( + itwWalletInstanceSelectors, + "itwWalletInstanceRevocationStatusSelector" + ) .mockImplementation(() => ({ isRevoked: false, revocationReason: "" @@ -377,7 +380,10 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => { jest - .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") + .spyOn( + itwWalletInstanceSelectors, + "itwWalletInstanceRevocationStatusSelector" + ) .mockImplementation(() => ({ isRevoked: true, revocationReason: "NEW_WALLET_INSTANCE_CREATED" @@ -408,7 +414,10 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for CERTIFICATE_REVOKED_BY_ISSUER", () => { jest - .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") + .spyOn( + itwWalletInstanceSelectors, + "itwWalletInstanceRevocationStatusSelector" + ) .mockImplementation(() => ({ isRevoked: true, revocationReason: "CERTIFICATE_REVOKED_BY_ISSUER" @@ -439,7 +448,10 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for REVOKED_BY_USER", () => { jest - .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") + .spyOn( + itwWalletInstanceSelectors, + "itwWalletInstanceRevocationStatusSelector" + ) .mockImplementation(() => ({ isRevoked: true, revocationReason: "REVOKED_BY_USER" From 54168ffa28f0e69552332c95a350c19bb043c941 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 13:11:00 +0100 Subject: [PATCH 12/24] test: update snapshot --- .../reducers/__tests__/__snapshots__/index.test.ts.snap | 7 +++++-- .../reducers/__tests__/__snapshots__/index.test.ts.snap | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) 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 36a9e8a229a..d47dece4ff6 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,8 +132,11 @@ exports[`featuresPersistor should match snapshot 1`] = ` }, "walletInstance": { "attestation": undefined, - "isRevoked": false, - "revocationReason": undefined, + "revocation": { + "isRevoked": false, + "revocationReason": undefined, + "alertShown": false + } }, }, "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 9915efc6727..9c94022d76a 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,8 +19,11 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to }, "walletInstance": { "attestation": undefined, - "isRevoked": false, - "revocationReason": undefined, + "revocation": { + "isRevoked": false, + "revocationReason": undefined, + "alertShown": false + } }, } `; From 9a2fefc13ccfe9780758d83cd04c612c2db9afae Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 13:40:27 +0100 Subject: [PATCH 13/24] refactor: revocation state moved alert shown --- .../reducers/__tests__/__snapshots__/index.test.ts.snap | 4 ++-- .../reducers/__tests__/__snapshots__/index.test.ts.snap | 4 ++-- ts/features/itwallet/walletInstance/store/reducers/index.ts | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) 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 d47dece4ff6..ce798fd3225 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 @@ -134,9 +134,9 @@ exports[`featuresPersistor should match snapshot 1`] = ` "attestation": undefined, "revocation": { "isRevoked": false, + "alertShown": false, "revocationReason": undefined, - "alertShown": false - } + }, }, }, "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 9c94022d76a..479f0755623 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 @@ -21,9 +21,9 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to "attestation": undefined, "revocation": { "isRevoked": false, + "alertShown": false, "revocationReason": undefined, - "alertShown": false - } + }, }, } `; diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index ba97e93a588..f8d3f511acd 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -14,8 +14,8 @@ export type ItwWalletInstanceState = { attestation: string | undefined; revocation: { isRevoked: boolean; - revocationReason?: WalletInstanceRevocationReason; alertShown: boolean; + revocationReason?: WalletInstanceRevocationReason; }; }; @@ -23,8 +23,8 @@ export const itwWalletInstanceInitialState: ItwWalletInstanceState = { attestation: undefined, revocation: { isRevoked: false, - revocationReason: undefined, - alertShown: false + alertShown: false, + revocationReason: undefined } }; From 2c2b0c30cb17d55b3d387144140fddbbe7f5deb8 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 13:47:08 +0100 Subject: [PATCH 14/24] test: update snapshot --- .../store/reducers/__tests__/__snapshots__/index.test.ts.snap | 2 +- .../store/reducers/__tests__/__snapshots__/index.test.ts.snap | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 ce798fd3225..13d7d15f6eb 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 @@ -133,8 +133,8 @@ exports[`featuresPersistor should match snapshot 1`] = ` "walletInstance": { "attestation": undefined, "revocation": { - "isRevoked": false, "alertShown": false, + "isRevoked": false, "revocationReason": undefined, }, }, 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 479f0755623..5f76582cba1 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 @@ -20,8 +20,8 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to "walletInstance": { "attestation": undefined, "revocation": { - "isRevoked": false, "alertShown": false, + "isRevoked": false, "revocationReason": undefined, }, }, From c788250a46bf2c9fdfb1a09bdd322f5a2ecac8e0 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Tue, 17 Dec 2024 16:21:55 +0100 Subject: [PATCH 15/24] chore: add FAQ link url for NEW_WALLET_INSTANCE_CREATED alert --- .../hook/useItwWalletInstanceRevocationAlert.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 6bef54b11e4..d76d8e9a561 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -15,6 +15,8 @@ const alertCtaText = I18n.t( ); 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. @@ -77,8 +79,7 @@ const showWalletRevocationAlert = ( { text: alertCtaText, onPress: () => { - // TODO: Add the correct URL - Linking.openURL("").catch(() => { + Linking.openURL(itwDocsOnIOMultipleDevicesUrl).catch(() => { IOToast.error(I18n.t("global.genericError")); }); } From b6fa8d4dc8a492e6aceca6f7875316920dc8ca56 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 10:37:45 +0100 Subject: [PATCH 16/24] refactor: refactor wi revocation alert --- .../__snapshots__/index.test.ts.snap | 7 +--- .../__snapshots__/index.test.ts.snap | 7 +--- .../useItwWalletInstanceRevocationAlert.ts | 18 +++++---- .../walletInstance/store/actions/index.ts | 2 +- .../walletInstance/store/reducers/index.ts | 27 ++++--------- .../walletInstance/store/selectors/index.ts | 22 ++++------ .../components/WalletCardsContainer.tsx | 6 +-- .../__tests__/WalletCardsContainer.test.tsx | 40 ++++++++----------- 8 files changed, 47 insertions(+), 82 deletions(-) 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 13d7d15f6eb..8df578af206 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,11 +132,8 @@ exports[`featuresPersistor should match snapshot 1`] = ` }, "walletInstance": { "attestation": undefined, - "revocation": { - "alertShown": false, - "isRevoked": false, - "revocationReason": undefined, - }, + "revocationAlertShown": false, + "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 5f76582cba1..3af6974e9ae 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,11 +19,8 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to }, "walletInstance": { "attestation": undefined, - "revocation": { - "alertShown": false, - "isRevoked": false, - "revocationReason": undefined, - }, + "revocationAlertShown": false, + "status": undefined, }, } `; diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index d76d8e9a561..162b8433e66 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -2,7 +2,10 @@ import { Alert, AlertButton, Linking } from "react-native"; import React from "react"; import { IOToast } from "@pagopa/io-app-design-system"; import I18n from "../../../../i18n"; -import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils"; +import { + WalletInstanceRevocationReason, + WalletInstanceStatus +} from "../../common/utils/itwTypesUtils"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { itwWalletInstanceAlertShownSelector } from "../store/selectors"; import { itwWalletInstanceSetAlertShown } from "../store/actions"; @@ -22,17 +25,16 @@ const itwDocsOnIOMultipleDevicesUrl = * Hook to monitor wallet instance status and display alerts if revoked. * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. */ -export const useItwWalletInstanceRevocationAlert = (walletInstanceStatus: { - isRevoked: boolean; - revocationReason?: WalletInstanceRevocationReason; -}) => { +export const useItwWalletInstanceRevocationAlert = ( + walletInstanceStatus: WalletInstanceStatus | undefined +) => { const dispatch = useIODispatch(); const alertShown = useIOSelector(itwWalletInstanceAlertShownSelector); React.useEffect(() => { - if (walletInstanceStatus.isRevoked && !alertShown) { - showWalletRevocationAlert(walletInstanceStatus.revocationReason); - dispatch(itwWalletInstanceSetAlertShown()); + if (walletInstanceStatus?.is_revoked && !alertShown) { + showWalletRevocationAlert(walletInstanceStatus.revocation_reason); + dispatch(itwWalletInstanceSetAlertShown(true)); } }, [walletInstanceStatus, alertShown, dispatch]); }; diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 739f0e9d74b..0179f7155d4 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -20,7 +20,7 @@ export const itwUpdateWalletInstanceStatus = createStandardAction( */ export const itwWalletInstanceSetAlertShown = createStandardAction( "ITW_WALLET_INSTANCE_SET_ALERT_SHOWN" -)(); +)(); export type ItwWalletInstanceActions = | ActionType diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index f8d3f511acd..5dfe4e7119e 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -8,24 +8,18 @@ import { itwUpdateWalletInstanceStatus, itwWalletInstanceSetAlertShown } from "../actions"; -import { WalletInstanceRevocationReason } from "../../../common/utils/itwTypesUtils"; +import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils"; export type ItwWalletInstanceState = { attestation: string | undefined; - revocation: { - isRevoked: boolean; - alertShown: boolean; - revocationReason?: WalletInstanceRevocationReason; - }; + status: WalletInstanceStatus | undefined; + revocationAlertShown: boolean; }; export const itwWalletInstanceInitialState: ItwWalletInstanceState = { attestation: undefined, - revocation: { - isRevoked: false, - alertShown: false, - revocationReason: undefined - } + status: undefined, + revocationAlertShown: false }; const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1; @@ -45,21 +39,14 @@ const reducer = ( case getType(itwUpdateWalletInstanceStatus): { return { ...state, - revocation: { - ...state.revocation, - isRevoked: action.payload.is_revoked, - revocationReason: action.payload.revocation_reason - } + status: action.payload }; } case getType(itwWalletInstanceSetAlertShown): return { ...state, - revocation: { - ...state.revocation, - alertShown: true - } + revocationAlertShown: action.payload }; case getType(itwLifecycleStoresReset): diff --git a/ts/features/itwallet/walletInstance/store/selectors/index.ts b/ts/features/itwallet/walletInstance/store/selectors/index.ts index c4397515279..2c92272a5db 100644 --- a/ts/features/itwallet/walletInstance/store/selectors/index.ts +++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts @@ -1,5 +1,5 @@ import * as O from "fp-ts/lib/Option"; -import { flow, pipe } from "fp-ts/lib/function"; +import { flow } from "fp-ts/lib/function"; import { createSelector } from "reselect"; import { GlobalState } from "../../../../../store/reducers/types"; import { isWalletInstanceAttestationValid } from "../../../common/utils/itwAttestationUtils"; @@ -20,21 +20,13 @@ export const itwIsWalletInstanceAttestationValidSelector = createSelector( /* Selector to get the alert shown status */ export const itwWalletInstanceAlertShownSelector = createSelector( - (state: GlobalState) => state.features.itWallet.walletInstance.revocation, - revocation => revocation.alertShown + (state: GlobalState) => + state.features.itWallet.walletInstance.revocationAlertShown, + revocationAlertShown => revocationAlertShown ); /* Selector to get the wallet instance status */ -export const itwWalletInstanceRevocationStatusSelector = createSelector( - (state: GlobalState) => state.features.itWallet.walletInstance.revocation, - revocation => - pipe( - O.fromNullable(revocation.isRevoked), - O.filter(Boolean), - O.map(() => ({ - isRevoked: true, - revocationReason: revocation.revocationReason - })), - O.getOrElse(() => ({ isRevoked: false })) - ) +export const itwWalletInstanceStatusSelector = createSelector( + (state: GlobalState) => state.features.itWallet.walletInstance.status, + status => status ); diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index d3e37adf1b9..0766f86a430 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -29,7 +29,7 @@ import { } from "../store/selectors"; import { WalletCardCategoryFilter } from "../types"; import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert"; -import { itwWalletInstanceRevocationStatusSelector } from "../../itwallet/walletInstance/store/selectors"; +import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/selectors"; import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer"; import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner"; import { WalletCardSkeleton } from "./WalletCardSkeleton"; @@ -49,9 +49,7 @@ const WalletCardsContainer = () => { const shouldRenderEmptyState = useIOSelector( shouldRenderWalletEmptyStateSelector ); - const walletInstanceStatus = useIOSelector( - itwWalletInstanceRevocationStatusSelector - ); + const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); useItwWalletInstanceRevocationAlert(walletInstanceStatus); diff --git a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx index a68f057d260..c364c9f9a4f 100644 --- a/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx +++ b/ts/features/wallet/components/__tests__/WalletCardsContainer.test.tsx @@ -364,13 +364,11 @@ describe("OtherWalletCardsContainer", () => { it("should not show alert if not revoked", () => { jest - .spyOn( - itwWalletInstanceSelectors, - "itwWalletInstanceRevocationStatusSelector" - ) + .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") .mockImplementation(() => ({ - isRevoked: false, - revocationReason: "" + id: "39cc62ab-1df0-4a9d-974d-4c58173a1750", + is_revoked: false, + revocation_reason: undefined })); renderComponent(WalletCardsContainer); @@ -380,13 +378,11 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for NEW_WALLET_INSTANCE_CREATED", () => { jest - .spyOn( - itwWalletInstanceSelectors, - "itwWalletInstanceRevocationStatusSelector" - ) + .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") .mockImplementation(() => ({ - isRevoked: true, - revocationReason: "NEW_WALLET_INSTANCE_CREATED" + id: "39cc62ab-1df0-4a9d-974d-4c58173a1750", + is_revoked: true, + revocation_reason: "NEW_WALLET_INSTANCE_CREATED" })); renderComponent(WalletCardsContainer); @@ -414,13 +410,11 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for CERTIFICATE_REVOKED_BY_ISSUER", () => { jest - .spyOn( - itwWalletInstanceSelectors, - "itwWalletInstanceRevocationStatusSelector" - ) + .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") .mockImplementation(() => ({ - isRevoked: true, - revocationReason: "CERTIFICATE_REVOKED_BY_ISSUER" + id: "39cc62ab-1df0-4a9d-974d-4c58173a1750", + is_revoked: true, + revocation_reason: "CERTIFICATE_REVOKED_BY_ISSUER" })); renderComponent(WalletCardsContainer); @@ -448,13 +442,11 @@ describe("OtherWalletCardsContainer", () => { it("should show alert for REVOKED_BY_USER", () => { jest - .spyOn( - itwWalletInstanceSelectors, - "itwWalletInstanceRevocationStatusSelector" - ) + .spyOn(itwWalletInstanceSelectors, "itwWalletInstanceStatusSelector") .mockImplementation(() => ({ - isRevoked: true, - revocationReason: "REVOKED_BY_USER" + id: "39cc62ab-1df0-4a9d-974d-4c58173a1750", + is_revoked: true, + revocation_reason: "REVOKED_BY_USER" })); renderComponent(WalletCardsContainer); From 8ddd9ed0966b1f7cc94bc7a4623734b7a093b0bf Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 10:53:46 +0100 Subject: [PATCH 17/24] chore: not persist wallet instance status --- ts/features/itwallet/walletInstance/store/reducers/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 5dfe4e7119e..feca76f4030 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -60,7 +60,8 @@ const reducer = ( const itwWalletInstancePersistConfig: PersistConfig = { key: "itwWalletInstance", storage: itwCreateSecureStorage(), - version: CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION + version: CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION, + blacklist: ["status"] }; const persistedReducer = persistReducer( From 70ec8cbe477c173170fae9650676c0bee147327d Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 12:16:15 +0100 Subject: [PATCH 18/24] chore: persist wallet instance status, deleted itwWalletInstanceSetAlertShown, set undefined wi status rendering wi revocation alert --- .../__snapshots__/index.test.ts.snap | 1 - .../__snapshots__/index.test.ts.snap | 1 - .../useItwWalletInstanceRevocationAlert.ts | 29 ++++++++++--------- .../walletInstance/store/actions/index.ts | 12 ++------ .../walletInstance/store/reducers/index.ts | 12 +------- .../walletInstance/store/selectors/index.ts | 7 ----- .../components/WalletCardsContainer.tsx | 4 +-- 7 files changed, 19 insertions(+), 47 deletions(-) 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 8df578af206..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,7 +132,6 @@ exports[`featuresPersistor should match snapshot 1`] = ` }, "walletInstance": { "attestation": undefined, - "revocationAlertShown": false, "status": undefined, }, }, 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 3af6974e9ae..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,7 +19,6 @@ exports[`itWalletReducer should match snapshot [if this test fails, remember to }, "walletInstance": { "attestation": undefined, - "revocationAlertShown": false, "status": undefined, }, } diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 162b8433e66..eaecbc2a652 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -1,14 +1,14 @@ import { Alert, AlertButton, Linking } from "react-native"; -import React from "react"; +import { useCallback } from "react"; import { IOToast } from "@pagopa/io-app-design-system"; import I18n from "../../../../i18n"; import { WalletInstanceRevocationReason, - WalletInstanceStatus } from "../../common/utils/itwTypesUtils"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; -import { itwWalletInstanceAlertShownSelector } from "../store/selectors"; -import { itwWalletInstanceSetAlertShown } from "../store/actions"; +import { itwWalletInstanceStatusSelector } from "../store/selectors"; +import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; +import { itwUpdateWalletInstanceStatus } from "../store/actions"; const closeButtonText = I18n.t( "features.itWallet.walletInstanceRevoked.alert.closeButton" @@ -25,18 +25,19 @@ const itwDocsOnIOMultipleDevicesUrl = * Hook to monitor wallet instance status and display alerts if revoked. * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. */ -export const useItwWalletInstanceRevocationAlert = ( - walletInstanceStatus: WalletInstanceStatus | undefined -) => { +export const useItwWalletInstanceRevocationAlert = () => { + + const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); const dispatch = useIODispatch(); - const alertShown = useIOSelector(itwWalletInstanceAlertShownSelector); - React.useEffect(() => { - if (walletInstanceStatus?.is_revoked && !alertShown) { - showWalletRevocationAlert(walletInstanceStatus.revocation_reason); - dispatch(itwWalletInstanceSetAlertShown(true)); - } - }, [walletInstanceStatus, alertShown, dispatch]); + useOnFirstRender( + useCallback(() => { + if (walletInstanceStatus?.is_revoked) { + showWalletRevocationAlert(walletInstanceStatus.revocation_reason); + dispatch(itwUpdateWalletInstanceStatus(undefined)); + } + }, [walletInstanceStatus]) + ); }; /** diff --git a/ts/features/itwallet/walletInstance/store/actions/index.ts b/ts/features/itwallet/walletInstance/store/actions/index.ts index 0179f7155d4..cbf6db80535 100644 --- a/ts/features/itwallet/walletInstance/store/actions/index.ts +++ b/ts/features/itwallet/walletInstance/store/actions/index.ts @@ -13,16 +13,8 @@ export const itwWalletInstanceAttestationStore = createStandardAction( */ export const itwUpdateWalletInstanceStatus = createStandardAction( "ITW_WALLET_INSTANCE_STATUS_UPDATE" -)(); - -/** - * This action sets the wallet instance alert shown - */ -export const itwWalletInstanceSetAlertShown = createStandardAction( - "ITW_WALLET_INSTANCE_SET_ALERT_SHOWN" -)(); +)(); export type ItwWalletInstanceActions = | ActionType - | ActionType - | ActionType; + | ActionType; diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index feca76f4030..9cbea2ef648 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -6,20 +6,17 @@ import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions"; import { itwWalletInstanceAttestationStore, itwUpdateWalletInstanceStatus, - itwWalletInstanceSetAlertShown } from "../actions"; import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils"; export type ItwWalletInstanceState = { attestation: string | undefined; status: WalletInstanceStatus | undefined; - revocationAlertShown: boolean; }; export const itwWalletInstanceInitialState: ItwWalletInstanceState = { attestation: undefined, status: undefined, - revocationAlertShown: false }; const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1; @@ -43,12 +40,6 @@ const reducer = ( }; } - case getType(itwWalletInstanceSetAlertShown): - return { - ...state, - revocationAlertShown: action.payload - }; - case getType(itwLifecycleStoresReset): return { ...itwWalletInstanceInitialState }; @@ -60,8 +51,7 @@ const reducer = ( const itwWalletInstancePersistConfig: PersistConfig = { key: "itwWalletInstance", storage: itwCreateSecureStorage(), - version: CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION, - blacklist: ["status"] + version: CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION }; const persistedReducer = persistReducer( diff --git a/ts/features/itwallet/walletInstance/store/selectors/index.ts b/ts/features/itwallet/walletInstance/store/selectors/index.ts index 2c92272a5db..bfb1f0bc520 100644 --- a/ts/features/itwallet/walletInstance/store/selectors/index.ts +++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts @@ -18,13 +18,6 @@ export const itwIsWalletInstanceAttestationValidSelector = createSelector( ) ); -/* Selector to get the alert shown status */ -export const itwWalletInstanceAlertShownSelector = createSelector( - (state: GlobalState) => - state.features.itWallet.walletInstance.revocationAlertShown, - revocationAlertShown => revocationAlertShown -); - /* Selector to get the wallet instance status */ export const itwWalletInstanceStatusSelector = createSelector( (state: GlobalState) => state.features.itWallet.walletInstance.status, diff --git a/ts/features/wallet/components/WalletCardsContainer.tsx b/ts/features/wallet/components/WalletCardsContainer.tsx index 0766f86a430..9d08e1c2a8b 100644 --- a/ts/features/wallet/components/WalletCardsContainer.tsx +++ b/ts/features/wallet/components/WalletCardsContainer.tsx @@ -29,7 +29,6 @@ import { } from "../store/selectors"; import { WalletCardCategoryFilter } from "../types"; import { useItwWalletInstanceRevocationAlert } from "../../itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert"; -import { itwWalletInstanceStatusSelector } from "../../itwallet/walletInstance/store/selectors"; import { WalletCardsCategoryContainer } from "./WalletCardsCategoryContainer"; import { WalletCardsCategoryRetryErrorBanner } from "./WalletCardsCategoryRetryErrorBanner"; import { WalletCardSkeleton } from "./WalletCardSkeleton"; @@ -49,9 +48,8 @@ const WalletCardsContainer = () => { const shouldRenderEmptyState = useIOSelector( shouldRenderWalletEmptyStateSelector ); - const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); - useItwWalletInstanceRevocationAlert(walletInstanceStatus); + useItwWalletInstanceRevocationAlert(); // Loading state is only displayed if there is the initial loading and there are no cards or // placeholders in the wallet From fe44716922b9ce07dd194bf8b3faf77b8c860749 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 12:29:48 +0100 Subject: [PATCH 19/24] fix: lint --- .../hook/useItwWalletInstanceRevocationAlert.ts | 6 ++---- ts/features/itwallet/walletInstance/store/reducers/index.ts | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index eaecbc2a652..0ceacb5c7d6 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -2,9 +2,7 @@ import { Alert, AlertButton, Linking } from "react-native"; import { useCallback } from "react"; import { IOToast } from "@pagopa/io-app-design-system"; import I18n from "../../../../i18n"; -import { - WalletInstanceRevocationReason, -} from "../../common/utils/itwTypesUtils"; +import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils"; import { useIODispatch, useIOSelector } from "../../../../store/hooks"; import { itwWalletInstanceStatusSelector } from "../store/selectors"; import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender"; @@ -36,7 +34,7 @@ export const useItwWalletInstanceRevocationAlert = () => { showWalletRevocationAlert(walletInstanceStatus.revocation_reason); dispatch(itwUpdateWalletInstanceStatus(undefined)); } - }, [walletInstanceStatus]) + }, [walletInstanceStatus, dispatch]) ); }; diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 9cbea2ef648..886b2dfb40e 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -5,7 +5,7 @@ import itwCreateSecureStorage from "../../../common/store/storages/itwSecureStor import { itwLifecycleStoresReset } from "../../../lifecycle/store/actions"; import { itwWalletInstanceAttestationStore, - itwUpdateWalletInstanceStatus, + itwUpdateWalletInstanceStatus } from "../actions"; import { WalletInstanceStatus } from "../../../common/utils/itwTypesUtils"; @@ -16,7 +16,7 @@ export type ItwWalletInstanceState = { export const itwWalletInstanceInitialState: ItwWalletInstanceState = { attestation: undefined, - status: undefined, + status: undefined }; const CURRENT_REDUX_ITW_WALLET_INSTANCE_STORE_VERSION = -1; From 2ee86b0d5d22bf3cb0fb73285862d49f1ff806df Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 12:38:30 +0100 Subject: [PATCH 20/24] refactor: prettify --- .../walletInstance/hook/useItwWalletInstanceRevocationAlert.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 0ceacb5c7d6..c6a2c335329 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -24,7 +24,6 @@ const itwDocsOnIOMultipleDevicesUrl = * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. */ export const useItwWalletInstanceRevocationAlert = () => { - const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); const dispatch = useIODispatch(); From dfa16d8f6a9991cc25ff3e8caf3c5c56ea163dd0 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Wed, 18 Dec 2024 14:25:20 +0100 Subject: [PATCH 21/24] refactor: openWebUrl instead linking --- .../hook/useItwWalletInstanceRevocationAlert.ts | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index c6a2c335329..2b05d10337e 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -1,12 +1,12 @@ -import { Alert, AlertButton, Linking } from "react-native"; +import { Alert, AlertButton } from "react-native"; import { useCallback } from "react"; -import { IOToast } from "@pagopa/io-app-design-system"; 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" @@ -56,11 +56,7 @@ const showWalletRevocationAlert = ( { text: closeButtonText }, { text: alertCtaText, - onPress: () => { - Linking.openURL(itwMinIntegrityReqUrl).catch(() => { - IOToast.error(I18n.t("global.genericError")); - }); - } + onPress: () => openWebUrl(itwMinIntegrityReqUrl) } ] ); @@ -78,11 +74,7 @@ const showWalletRevocationAlert = ( { text: closeButtonText }, { text: alertCtaText, - onPress: () => { - Linking.openURL(itwDocsOnIOMultipleDevicesUrl).catch(() => { - IOToast.error(I18n.t("global.genericError")); - }); - } + onPress: () => openWebUrl(itwDocsOnIOMultipleDevicesUrl) } ] ); From 1261408e038d2c8de0038693776194f4b285c7f3 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Thu, 19 Dec 2024 12:25:06 +0100 Subject: [PATCH 22/24] refactor --- .../hook/useItwWalletInstanceRevocationAlert.ts | 1 - ts/features/itwallet/walletInstance/store/reducers/index.ts | 2 +- .../itwallet/walletInstance/store/selectors/index.ts | 6 ++---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 2b05d10337e..89cd6d7b281 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -21,7 +21,6 @@ const itwDocsOnIOMultipleDevicesUrl = /** * Hook to monitor wallet instance status and display alerts if revoked. - * @param walletInstanceStatus - The status of the wallet instance, including whether it is revoked and the reason for revocation. */ export const useItwWalletInstanceRevocationAlert = () => { const walletInstanceStatus = useIOSelector(itwWalletInstanceStatusSelector); diff --git a/ts/features/itwallet/walletInstance/store/reducers/index.ts b/ts/features/itwallet/walletInstance/store/reducers/index.ts index 886b2dfb40e..778d134d4fb 100644 --- a/ts/features/itwallet/walletInstance/store/reducers/index.ts +++ b/ts/features/itwallet/walletInstance/store/reducers/index.ts @@ -28,7 +28,7 @@ const reducer = ( switch (action.type) { case getType(itwWalletInstanceAttestationStore): { return { - ...state, + status: undefined, attestation: action.payload }; } diff --git a/ts/features/itwallet/walletInstance/store/selectors/index.ts b/ts/features/itwallet/walletInstance/store/selectors/index.ts index bfb1f0bc520..85a98084d61 100644 --- a/ts/features/itwallet/walletInstance/store/selectors/index.ts +++ b/ts/features/itwallet/walletInstance/store/selectors/index.ts @@ -19,7 +19,5 @@ export const itwIsWalletInstanceAttestationValidSelector = createSelector( ); /* Selector to get the wallet instance status */ -export const itwWalletInstanceStatusSelector = createSelector( - (state: GlobalState) => state.features.itWallet.walletInstance.status, - status => status -); +export const itwWalletInstanceStatusSelector = (state: GlobalState) => + state.features.itWallet.walletInstance.status; From 120131f2e27150666cadf7aebf17717bbc825205 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Thu, 19 Dec 2024 15:16:58 +0100 Subject: [PATCH 23/24] refactor: removed showAlert function --- .../useItwWalletInstanceRevocationAlert.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index 89cd6d7b281..cdd058aa91f 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -1,4 +1,4 @@ -import { Alert, AlertButton } from "react-native"; +import { Alert } from "react-native"; import { useCallback } from "react"; import I18n from "../../../../i18n"; import { WalletInstanceRevocationReason } from "../../common/utils/itwTypesUtils"; @@ -44,7 +44,7 @@ const showWalletRevocationAlert = ( ) => { switch (revocationReason) { case "CERTIFICATE_REVOKED_BY_ISSUER": - showAlert( + Alert.alert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.revokedByWalletProvider.title" ), @@ -62,7 +62,7 @@ const showWalletRevocationAlert = ( break; case "NEW_WALLET_INSTANCE_CREATED": - showAlert( + Alert.alert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.newWalletInstanceCreated.title" ), @@ -79,7 +79,7 @@ const showWalletRevocationAlert = ( ); break; case "REVOKED_BY_USER": - showAlert( + Alert.alert( I18n.t( "features.itWallet.walletInstanceRevoked.alert.revokedByUser.title" ), @@ -98,12 +98,4 @@ const showWalletRevocationAlert = ( default: break; } -}; - -const showAlert = ( - title: string, - message: string, - buttons: Array = [{ text: closeButtonText }] -) => { - Alert.alert(title, message, buttons); -}; +}; \ No newline at end of file From 1b9f8cd135a0366d2fb1eab4ed4230e2f099fd81 Mon Sep 17 00:00:00 2001 From: RiccardoMolinari95 Date: Thu, 19 Dec 2024 15:18:24 +0100 Subject: [PATCH 24/24] chore:prettify --- .../walletInstance/hook/useItwWalletInstanceRevocationAlert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts index cdd058aa91f..15698a1baf2 100644 --- a/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts +++ b/ts/features/itwallet/walletInstance/hook/useItwWalletInstanceRevocationAlert.ts @@ -98,4 +98,4 @@ const showWalletRevocationAlert = ( default: break; } -}; \ No newline at end of file +};