Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(IT Wallet): [SIW-1665] Hide eID card in wallet #6214

Merged
merged 29 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
f7b1847
refactor: change wallet components to conform to the new design
gispada Sep 24, 2024
42f20e5
feat: add bottom sheet for eID information
gispada Sep 25, 2024
a848270
chore: remove Alert
gispada Sep 26, 2024
e6b8ecd
refactor: improve code readability
gispada Sep 26, 2024
e115a93
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Sep 26, 2024
c480b3f
refactor: footer in WalletCardsCategoryContainer
gispada Sep 26, 2024
8707061
chore: fix ITW tests
gispada Sep 26, 2024
0259b66
chore: add more tests
gispada Sep 26, 2024
d23f9e7
chore: fix type error
gispada Sep 26, 2024
ceed91c
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Sep 27, 2024
6c54084
chore: add comments
gispada Sep 27, 2024
573b178
chore: simplify test cases
gispada Sep 27, 2024
08bac1e
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Sep 30, 2024
cd3320f
chore: add issuedAt and expiration properties with migration
gispada Sep 30, 2024
0424d37
chore: show issuance date in eID bottom sheet
gispada Sep 30, 2024
429d3e0
chore: fix tests
gispada Sep 30, 2024
607ef25
chore: update io-react-native-wallet and store issuance date
gispada Sep 30, 2024
0d2d337
chore: update yarn.lock
gispada Sep 30, 2024
192e51c
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Sep 30, 2024
7fc7d68
chore: default date in getSafeISODate
gispada Sep 30, 2024
27a41d8
refactor: move expiration and issuedAt under jwt key in StoredCredential
gispada Oct 1, 2024
4c6c43c
chore: fix tests
gispada Oct 1, 2024
26e0007
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Oct 1, 2024
1328d01
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Oct 2, 2024
d5223e9
chore: handle undefined issuedAt
gispada Oct 2, 2024
c9d86dc
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Oct 2, 2024
27702fd
Merge branch 'master' into SIW-1665-eid-card-removal
gispada Oct 2, 2024
ba8d1c2
chore: update io-react-native-wallet
gispada Oct 2, 2024
ece3903
Merge branch 'master' into SIW-1665-eid-card-removal
mastro993 Oct 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3310,6 +3310,7 @@ features:
toast: Fatto!
actions:
continue: Aggiungi primo documento
continueAlt: Aggiungi documento
close: Continua più tardi
credentialResult:
toast: Fatto! Hai aggiunto {{credentialName}}
Expand Down Expand Up @@ -3365,6 +3366,13 @@ features:
mdl:
title: "Patente: contesti di verifica"
content: "###### In quali casi posso usare la versione digitale della mia Patente di guida? \n Puoi usare la versione digitale della tua Patente per dimostrare l’idoneità alla guida durante i controlli stradali solo se **accompagnata da un documento di riconoscimento fisico valido** (es. Carta di Identità). \n \n In questa prima fase della funzionalità infatti, la versione digitale della tua Patente **non ha ancora lo stesso valore legale del documento fisico.**"
eidInfo:
title: "Documenti su IO:\nidentità verificata"
contentTop: Con **Documenti su IO** salvi nel Portafoglio di IO le versioni digitali dei tuoi documenti.
contentBottom: "###### Come funziona?\n\nLa tua **identità è verificata** in fase di attivazione tramite SPID o CIE."
triggerLabel: Cos'è?
alert:
valid: L'ultima verifica è del {{issuanceDate}}.
credentialDetails:
flipCard: "Show back"
personalDataTitle: "Personal data"
Expand Down
8 changes: 8 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3310,6 +3310,7 @@ features:
toast: Fatto!
actions:
continue: Aggiungi primo documento
continueAlt: Aggiungi documento
close: Continua più tardi
credentialResult:
toast: Fatto! Hai aggiunto {{credentialName}}
Expand Down Expand Up @@ -3365,6 +3366,13 @@ features:
mdl:
title: "Patente: contesti di verifica"
content: "###### In quali casi posso usare la versione digitale della mia Patente di guida? \n Puoi usare la versione digitale della tua Patente per dimostrare l’idoneità alla guida durante i controlli stradali solo se **accompagnata da un documento di riconoscimento fisico valido** (es. Carta di Identità). \n \n In questa prima fase della funzionalità infatti, la versione digitale della tua Patente **non ha ancora lo stesso valore legale del documento fisico.**"
eidInfo:
title: "Documenti su IO:\nidentità verificata"
contentTop: Con **Documenti su IO** salvi nel Portafoglio di IO le versioni digitali dei tuoi documenti.
contentBottom: "###### Come funziona?\n\nLa tua **identità è verificata** in fase di attivazione tramite SPID o CIE."
triggerLabel: Cos'è?
alert:
valid: L'ultima verifica è del {{issuanceDate}}.
credentialDetails:
flipCard: "Mostra retro"
personalDataTitle: "Dati personali"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
"@pagopa/io-react-native-integrity": "^0.3.0",
"@pagopa/io-react-native-jwt": "^1.2.0",
"@pagopa/io-react-native-login-utils": "1.0.6",
"@pagopa/io-react-native-wallet": "^0.17.1",
"@pagopa/io-react-native-wallet": "^0.18.1",
"@pagopa/io-react-native-zendesk": "^0.3.29",
"@pagopa/react-native-cie": "^1.3.0",
"@pagopa/ts-commons": "^10.15.0",
Expand Down
2 changes: 1 addition & 1 deletion ts/features/design-system/core/DSWallet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const DSWallet = () => {
iconName: "creditCard"
}}
isStacked={isStacked}
footer={
topElement={
<>
<VSpacer size={16} />
<Banner
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ type ItwDiscoveryBannerProps = {
withTitle?: boolean;
ignoreMargins?: boolean;
fallbackComponent?: ReactElement;
closable?: boolean;
};

export const ItwDiscoveryBanner = ({
withTitle = true,
ignoreMargins = false,
fallbackComponent
fallbackComponent,
closable = true
}: ItwDiscoveryBannerProps) => {
const bannerRef = React.createRef<View>();
const navigation = useIONavigation();
Expand Down Expand Up @@ -95,7 +97,7 @@ export const ItwDiscoveryBanner = ({
pictogramName="itWallet"
color="turquoise"
size="big"
onClose={handleOnClose}
onClose={closable ? handleOnClose : undefined}
labelClose={I18n.t("global.buttons.close")}
onPress={handleOnPress}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from "react";
import { View } from "react-native";
import {
Divider,
HStack,
Icon,
IOStyles,
VStack,
H4,
Alert
} from "@pagopa/io-app-design-system";
import * as O from "fp-ts/lib/Option";
import { constNull, pipe } from "fp-ts/lib/function";
import I18n from "../../../../i18n";
import { useIOSelector } from "../../../../store/hooks";
import { itwCredentialsEidSelector } from "../../credentials/store/selectors";
import IOMarkdown from "../../../../components/IOMarkdown";
import { format } from "../../../../utils/dates";
import { parseClaims, WellKnownClaim } from "../utils/itwClaimsUtils";
import { StoredCredential } from "../utils/itwTypesUtils";
import { ItwCredentialClaim } from "./ItwCredentialClaim";

export const ItwEidInfoBottomSheetTitle = () => (
<HStack space={8} style={IOStyles.alignCenter}>
<Icon name="legalValue" color="blueIO-500" />
<H4>
{I18n.t("features.itWallet.presentation.bottomSheets.eidInfo.title")}
</H4>
</HStack>
);

export const ItwEidInfoBottomSheetContent = () => {
const eidOption = useIOSelector(itwCredentialsEidSelector);

const Content = ({ credential }: { credential: StoredCredential }) => {
const claims = parseClaims(credential.parsedCredential, {
exclude: [WellKnownClaim.unique_id, WellKnownClaim.content]
});

return (
<VStack space={24}>
<IOMarkdown
content={I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.contentTop"
)}
/>
<View>
{claims.map((claim, index) => (
<React.Fragment key={index}>
{index !== 0 && <Divider />}
<ItwCredentialClaim claim={claim} isPreview={true} />
</React.Fragment>
))}
</View>
{credential.jwt.issuedAt && (
<Alert
variant="success"
content={I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.alert.valid",
{ issuanceDate: format(credential.jwt.issuedAt, "DD-MM-YYYY") }
)}
/>
)}
<IOMarkdown
content={I18n.t(
"features.itWallet.presentation.bottomSheets.eidInfo.contentBottom"
)}
/>
mastro993 marked this conversation as resolved.
Show resolved Hide resolved
</VStack>
);
};

return pipe(
eidOption,
O.fold(
constNull, // This should never happen
credential => <Content credential={credential} />
)
);
};
40 changes: 40 additions & 0 deletions ts/features/itwallet/common/components/ItwWalletReadyBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from "react";
import { Banner } from "@pagopa/io-app-design-system";
import I18n from "../../../../i18n";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { ITW_ROUTES } from "../../navigation/routes";
import { useIOSelector } from "../../../../store/hooks";
import { itwIsWalletEmptySelector } from "../../credentials/store/selectors";
import { itwLifecycleIsValidSelector } from "../../lifecycle/store/selectors";

export const ItwWalletReadyBanner = () => {
const isItwValid = useIOSelector(itwLifecycleIsValidSelector);
const isWalletEmpty = useIOSelector(itwIsWalletEmptySelector);

const navigation = useIONavigation();

if (!isItwValid || !isWalletEmpty) {
return null;
}

const handleOnPress = () => {
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.ONBOARDING
});
};

return (
<Banner
testID="itwWalletReadyBannerTestID"
title={I18n.t("features.itWallet.issuance.eidResult.success.title")}
content={I18n.t("features.itWallet.issuance.eidResult.success.subtitle")}
action={I18n.t(
"features.itWallet.issuance.eidResult.success.actions.continueAlt"
)}
pictogramName="itWallet"
color="turquoise"
size="big"
onPress={handleOnPress}
/>
);
};
32 changes: 5 additions & 27 deletions ts/features/itwallet/common/store/reducers/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as O from "fp-ts/lib/Option";
import {
createMigrate,
MigrationManifest,
PersistConfig,
PersistPartial,
persistReducer
} from "redux-persist";
import { combineReducers } from "redux";
import { pipe } from "fp-ts/lib/function";
import { Action } from "../../../../../store/actions/types";
import identificationReducer, {
ItwIdentificationState
Expand All @@ -23,8 +20,12 @@ import itwCredentialsReducer, {
ItwCredentialsState
} from "../../../credentials/store/reducers";
import itwCreateCredentialsStorage from "../storages/itwCredentialsStorage";
import { StoredCredential } from "../../utils/itwTypesUtils";
import { isDevEnv } from "../../../../../utils/environment";
import {
CURRENT_REDUX_ITW_CREDENTIALS_STORE_VERSION,
CURRENT_REDUX_ITW_STORE_VERSION,
itwCredentialsStateMigrations
} from "./migrations";

export type ItWalletState = {
identification: ItwIdentificationState;
Expand All @@ -35,29 +36,6 @@ export type ItWalletState = {

export type PersistedItWalletState = ReturnType<typeof persistedReducer>;

const CURRENT_REDUX_ITW_STORE_VERSION = -1;
const CURRENT_REDUX_ITW_CREDENTIALS_STORE_VERSION = 0;
export const itwCredentialsStateMigrations: MigrationManifest = {
"0": (state): ItwCredentialsState & PersistPartial => {
// Version 0
// Add optional `storedStatusAttestation` field
const addStoredStatusAttestation = (
credential: StoredCredential
): StoredCredential => ({
...credential,
storedStatusAttestation: undefined
});
const prevState = state as ItwCredentialsState & PersistPartial;
return {
...prevState,
eid: pipe(prevState.eid, O.map(addStoredStatusAttestation)),
credentials: prevState.credentials.map(credential =>
pipe(credential, O.map(addStoredStatusAttestation))
)
};
}
};

const itwPersistConfig: PersistConfig = {
key: "itWallet",
storage: AsyncStorage,
Expand Down
64 changes: 64 additions & 0 deletions ts/features/itwallet/common/store/reducers/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as O from "fp-ts/lib/Option";
import { MigrationManifest, PersistPartial } from "redux-persist";
import { pipe } from "fp-ts/lib/function";
import { SdJwt } from "@pagopa/io-react-native-wallet";
import { ItwCredentialsState } from "../../../credentials/store/reducers";
import { StoredCredential } from "../../utils/itwTypesUtils";

export const CURRENT_REDUX_ITW_STORE_VERSION = -1;

export const CURRENT_REDUX_ITW_CREDENTIALS_STORE_VERSION = 1;
export const itwCredentialsStateMigrations: MigrationManifest = {
"0": (state): ItwCredentialsState & PersistPartial => {
// Version 0
// Add optional `storedStatusAttestation` field
const addStoredStatusAttestation = (
credential: StoredCredential
): StoredCredential => ({
...credential,
storedStatusAttestation: undefined
});
const prevState = state as ItwCredentialsState & PersistPartial;
return {
...prevState,
eid: pipe(prevState.eid, O.map(addStoredStatusAttestation)),
credentials: prevState.credentials.map(credential =>
pipe(credential, O.map(addStoredStatusAttestation))
)
};
},

"1": (state): ItwCredentialsState & PersistPartial => {
// Version 1
// Add issuance and expiration dates to stored credentials
const addIatExpProperties = (
credential: StoredCredential
): StoredCredential => {
const { disclosures, sdJwt } = SdJwt.decode(credential.credential);
const iatDisclosure = disclosures.find(
({ decoded }) => decoded[1] === "iat"
);
const exp = sdJwt.payload.exp
? new Date(sdJwt.payload.exp * 1000)
: new Date();
return {
...credential,
jwt: {
expiration: exp.toISOString(),
issuedAt:
iatDisclosure && typeof iatDisclosure.decoded[2] === "number"
? new Date(iatDisclosure.decoded[2] * 1000).toISOString()
: undefined
}
};
};
const prevState = state as ItwCredentialsState & PersistPartial;
return {
...prevState,
eid: pipe(prevState.eid, O.map(addIatExpProperties)),
credentials: prevState.credentials.map(credential =>
pipe(credential, O.map(addIatExpProperties))
)
};
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ describe("isStatusAttestationMissingOrExpired", () => {
format: "vc+sd-jwt",
keyTag: "9020c6f8-01be-4236-9b6f-834af9dcbc63",
issuerConf: {} as StoredCredential["issuerConf"],
parsedCredential: {}
parsedCredential: {},
jwt: {
issuedAt: "2024-09-30T07:32:49.000Z",
expiration: "2025-09-30T07:32:50.000Z"
}
};

it("return true when the status attestation is missing", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const obtainCredential = async ({

// Parse and verify the credential. The ignoreMissingAttributes flag must be set to false or omitted in production.

const { parsedCredential } =
const { parsedCredential, issuedAt, expiration } =
await Credential.Issuance.verifyAndParseCredential(
issuerConf,
credential,
Expand All @@ -193,7 +193,11 @@ export const obtainCredential = async ({
credentialType,
format,
issuerConf,
keyTag: credentialKeyTag
keyTag: credentialKeyTag,
jwt: {
expiration: expiration.toISOString(),
issuedAt: issuedAt?.toISOString()
}
};

return {
Expand Down
8 changes: 6 additions & 2 deletions ts/features/itwallet/common/utils/itwIssuanceUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ const getPid = async ({
}
);

const { parsedCredential } =
const { parsedCredential, issuedAt, expiration } =
await Credential.Issuance.verifyAndParseCredential(
issuerConf,
credential,
Expand All @@ -281,7 +281,11 @@ const getPid = async ({
keyTag: credentialKeyTag,
credentialType: CREDENTIAL_TYPE,
format,
credential
credential,
jwt: {
expiration: expiration.toISOString(),
issuedAt: issuedAt?.toISOString()
}
};
};

Expand Down
Loading
Loading