Skip to content

Commit

Permalink
chore(IT Wallet): [SIW-434] Update presentation flow (#5225)
Browse files Browse the repository at this point in the history
## Short description
This PR updates the presentation flow both for PID and for a generic
credential.
There are a few side changes required to update the design system and
match the flow represented in the FIGMA.
Please notice that the loading screen after the user confirms is missing
from the credential flow FIGMA likely due to a mistake. It's properly
implemented in the app though.

## List of changes proposed in this pull request
- Updates and merge locales;
- Adds a new middle screen for the data presentation between the check
and the result;
- Removes the screen parameters drilling between the different screens
in the flow and replaces them with the redux store;
- Updates the design system of the PID presentation screen; 
- Aligns the generi credential presentation to the same version; 
- Fixes a bug which prevented the loading screen to be shown during the
credential presentation flow (this is extra but needed to align the two
flows).

## How to test
1.  Run `yarn generate:locales`; 
2. Obtain a PID and try a presentation flow with the RP website; 
3. Obtain a credential and test the mocked presentation flow by long
pressing the card in the wallet section.

---------

Co-authored-by: Mario Perrotta <mario.perrotta@pagopa.it>
  • Loading branch information
LazyAfternoons and hevelius committed Nov 16, 2023
1 parent 2a57491 commit f9d7272
Show file tree
Hide file tree
Showing 16 changed files with 523 additions and 575 deletions.
16 changes: 3 additions & 13 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3484,18 +3484,6 @@ features:
confirmData: "Confirm data"
deny: "Deny"
tos: "By continuing, you accept the [Terms and conditions of use]() of the service and confirm that you have read the [Privacy Policy]() of {{relayingParty}}."
resultScreen:
loading:
title: "Presenting your data to {{relayingParty}}"
subTitle: "Wait a few seconds"
error:
title: "Data presentation failed"
subTitle: "Try again in a few minutes. If it happens again, contact support."
success:
title: "Successful login using IT Wallet"
subTitle: "You can now return to the {{relayingParty}} website to continue."
buttons:
continue: "Ok, thanks"
checksScreen:
loading: "We are checking to verify that this organization is verified"
success: "{{organizationName}} is a verified organization"
Expand All @@ -3515,7 +3503,9 @@ features:
secondHeader: "Safety of treatment"
secondBody: "App IO allows the sharing of your data only with verified bodies registered on the lists that guarantee its reliability."
tos: "For more information, read the [Privacy Policy](https://io.italia.it/privacy-policy/) and the [Terms and Conditions](https://io.italia.it/privacy-policy/)"
resultScreenNew:
resultScreen:
loading:
title: "We are sharing your data with {{organizationName}}"
success:
title: "Done!"
subtitle: "Go back to the {{organizationName}} website to continue."
Expand Down
16 changes: 3 additions & 13 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3509,18 +3509,6 @@ features:
confirmData: "Conferma dati"
deny: "Nega"
tos: "Proseguendo accetti i [Termini e condizioni d’uso]() del servizio e confermi di avere letto l’[Informativa Privacy]() di {{relayingParty}}."
resultScreen:
loading:
title: "Sto presentando i tuoi dati a {{relayingParty}}"
subTitle: "Attendi qualche secondo"
error:
title: "Presentazione dei dati non riuscita"
subTitle: "Riprova tra qualche minuto. Se si ripete, contatta l’assistenza."
success:
title: "Accesso eseguito correttamente con IT Wallet"
subTitle: "Ora puoi tornare sul sito di {{relayingParty}} per continuare"
buttons:
continue: "Ok, grazie"
checksScreen:
loading: "Stiamo controllando che questo ente sia verificato"
success: "{{organizationName}} è un ente verificato"
Expand All @@ -3540,7 +3528,9 @@ features:
secondHeader: "I miei dati sono al sicuro?"
secondBody: "I tuoi dati non sono condivisi con altri soggetti e non vengono utilizzati per finalità diverse da quelle indicati. Potrai richiederne la rimozione in qualsiasi momento."
tos: "Per maggiori informazioni leggi l’[Informativa Privacy](https://io.italia.it/privacy-policy/) e i [Termini e condizioni d’uso](https://io.italia.it/privacy-policy/)"
resultScreenNew:
resultScreen:
loading:
title: "Stiamo condividendo i tuoi dati con {{organizationName}}"
success:
title: "Fatto!"
subtitle: "Torna sul sito di {{organizationName}} per continuare."
Expand Down
7 changes: 3 additions & 4 deletions ts/features/it-wallet/navigation/ItwParamsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { ItwCieCardReaderScreenNavigationParams } from "../screens/issuing/cie/I
import { ItwCieConsentDataUsageScreenNavigationParams } from "../screens/issuing/cie/ItwCieConsentDataUsageScreen";
import { ItwCieWrongPinScreenNavigationParams } from "../screens/issuing/cie/ItwCieWrongPinScreen";
import { ItwRpInitScreenNavigationParams } from "../screens/presentation/crossdevice/ItwRpInitScreen";
import { ItwRpResultScreenNavigationParams } from "../screens/presentation/crossdevice/ItwRpPresentationScreen";
import { ITW_ROUTES } from "./ItwRoutes";

export type ItwParamsList = {
Expand All @@ -29,11 +28,11 @@ export type ItwParamsList = {
// PRESENTATION PID
[ITW_ROUTES.PRESENTATION.PID_DETAILS]: undefined;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.INIT]: ItwRpInitScreenNavigationParams;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE
.RESULT]: ItwRpResultScreenNavigationParams;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA]: undefined;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.RESULT]: undefined;
// PRESENTATION CREDENTIALS
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.CHECKS]: undefined;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA]: undefined;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA_NEW]: undefined;
[ITW_ROUTES.PRESENTATION.CROSS_DEVICE.RESULT_NEW]: undefined;
// CREDENTIALS
[ITW_ROUTES.CREDENTIAL.ISSUING.CATALOG]: undefined;
Expand Down
7 changes: 4 additions & 3 deletions ts/features/it-wallet/navigation/ItwRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@ export const ITW_ROUTES = {
PID_DETAILS: "ITW_PRESENTATION_PID_DETAILS",
CROSS_DEVICE: {
CHECKS: "ITW_PRESENTATION_CROSS_DEVICE_CHECKS",
DATA: "ITW_PRESENTATION_CROSS_DEVICE_DATA",
DATA_NEW: "ITW_PRESENTATION_CROSS_DEVICE_DATA_NEW",
RESULT_NEW: "ITW_PRESETATION_CROSS_DEVICE_RESULT_NEW",
INIT: "ITW_PRESENTATION_CROSS_DEVICE_INIT",
RESULT: "ITW_PRESETATION_CROSS_DEVICE_RESULT",
RESULT_NEW: "ITW_PRESETATION_CROSS_DEVICE_RESULT_NEW"
DATA: "ITW_PRESENTATION_CROSS_DEVICE_DATA",
RESULT: "ITW_PRESETATION_CROSS_DEVICE_RESULT"
} as const
},
CREDENTIAL: {
Expand Down
14 changes: 10 additions & 4 deletions ts/features/it-wallet/navigation/ItwStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import ItwDiscoveryProviderInfoScreen from "../screens/discovery/ItwDiscoveryPro
import ItwMissingFeatureScreen from "../screens/generic/ItwMissingFeatureScreen";
import ItwCredentialPreviewScreen from "../screens/credential/issuing/ItwCredentialPreviewScreen";
import ItwCredentialAuthScreen from "../screens/credential/issuing/ItwCredentialAuthScreen";
import ItwChecksScreen from "../screens/presentation/crossdevice/new/ItwPresentationChecksScreen";
import ItwPresentationChecksScreen from "../screens/presentation/crossdevice/new/ItwPresentationChecksScreen";
import ItwPresentationDataScreen from "../screens/presentation/crossdevice/new/ItwPresentationDataScreen";
import ItwPresentationResultScreen from "../screens/presentation/crossdevice/new/ItwPresentationResultScreen";
import ItwCredentialsChecksScreen from "../screens/credential/issuing/ItwCredentialChecksScreen";
import ItwCredentialCatalogScreen from "../screens/credential/issuing/ItwCredentialsCatalogScreen";
import ItwCredentialDetailsScreen from "../screens/credential/ItwCredentialDetailsScreen";
import ItwPidPresentationDataScreen from "../screens/presentation/crossdevice/ItwPidPresentationDataScreen";
import { ItwParamsList } from "./ItwParamsList";
import { ITW_ROUTES } from "./ItwRoutes";

Expand Down Expand Up @@ -86,23 +87,28 @@ export const ItwStackNavigator = () => (
name={ITW_ROUTES.PRESENTATION.PID_DETAILS}
component={ItwCredentialDetails}
/>
{/* PRESENTATION CROSS DEVICE */}
{/* CREDENTIAL PRESENTATION */}
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.CHECKS}
component={ItwChecksScreen}
component={ItwPresentationChecksScreen}
/>
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA}
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA_NEW}
component={ItwPresentationDataScreen}
/>
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.RESULT_NEW}
component={ItwPresentationResultScreen}
/>
{/** PID PRESENTATION */}
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.INIT}
component={ItwRpInitScreen}
/>
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.DATA}
component={ItwPidPresentationDataScreen}
/>
<Stack.Screen
name={ITW_ROUTES.PRESENTATION.CROSS_DEVICE.RESULT}
component={ItwPresentationScreen}
Expand Down
4 changes: 3 additions & 1 deletion ts/features/it-wallet/saga/itwRpInitializationSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@ export function* handleItwRpInitializationSaga(
yield* put(
itwRpInitialization.success({
requestObject,
entity: rpEntityConfiguration
entity: rpEntityConfiguration,
authReqUrl,
clientId
})
);
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import React from "react";
import { useNavigation } from "@react-navigation/native";
import { PidWithToken } from "@pagopa/io-react-native-wallet/lib/typescript/pid/sd-jwt";
import { SafeAreaView } from "react-native-safe-area-context";
import {
Body,
FeatureInfo,
FooterWithButtons,
H1,
H6,
HSpacer,
IOStyles,
Icon,
IconContained,
LabelLink,
VSpacer
} from "@pagopa/io-app-design-system";
import { View } from "native-base";
import { Image, StyleSheet } from "react-native";
import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import { useIOSelector } from "../../../../../store/hooks";
import { itwDecodedPidValueSelector } from "../../../store/reducers/itwPidDecodeReducer";
import { IOStackNavigationProp } from "../../../../../navigation/params/AppParamsList";
import { ItwParamsList } from "../../../navigation/ItwParamsList";
import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent";
import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp";
import I18n from "../../../../../i18n";
import { showCancelAlert } from "../../../utils/alert";
import ItwTextInfo from "../../../components/ItwTextInfo";
import ItwBulletList from "../../../components/ItwBulletList";
import { rpPidMock } from "../../../utils/mocks";
import { ITW_ROUTES } from "../../../navigation/ItwRoutes";
import interno from "../../../../../../img/features/it-wallet/interno.png";
import { useItwInfoBottomSheet } from "../../../hooks/useItwInfoBottomSheet";
import ItwKoView from "../../../components/ItwKoView";
import { getItwGenericMappedError } from "../../../utils/errors/itwErrorsMapping";
import ROUTES from "../../../../../navigation/routes";
import { ForceScrollDownView } from "../../../../../components/ForceScrollDownView";

const ItwPidPresentationDataScreen = () => {
const decodedPid = useIOSelector(itwDecodedPidValueSelector);
const navigation = useNavigation<IOStackNavigationProp<ItwParamsList>>();
const { present, bottomSheet } = useItwInfoBottomSheet({
title: rpPidMock.organizationName,
content: [
{
title: I18n.t(
"features.itWallet.presentation.dataScreen.infoBottomSheet.body.firstHeader"
),
body: I18n.t(
"features.itWallet.presentation.dataScreen.infoBottomSheet.body.firstBody"
)
},
{
title: I18n.t(
"features.itWallet.presentation.dataScreen.infoBottomSheet.body.secondHeader"
),
body: I18n.t(
"features.itWallet.presentation.dataScreen.infoBottomSheet.body.secondBody"
)
}
]
});

/**
* Callback to be used in case of cancel button press alert to navigate to the home screen.
*/
const alertOnPress = () => {
navigation.navigate(ROUTES.MAIN, {
screen: ROUTES.MESSAGES_HOME
});
};

const RpPreviewView = ({ decodedPid }: { decodedPid: PidWithToken }) => (
<BaseScreenComponent
goBack={true}
headerTitle={I18n.t(
"features.itWallet.presentation.pidAttributesScreen.headerTitle"
)}
contextualHelp={emptyContextualHelp}
>
<SafeAreaView style={IOStyles.flex}>
<ForceScrollDownView>
<View style={IOStyles.horizontalContentPadding}>
<VSpacer size={32} />
{/* SECOND HEADER */}
<View style={styles.secondHeader}>
{/* LEFT */}
<View style={styles.secondHeaderLeft}>
<IconContained
icon={"device"}
color={"neutral"}
variant={"tonal"}
/>
<HSpacer size={8} />
<Icon name={"transactions"} color={"grey-450"} size={24} />
<HSpacer size={8} />
<IconContained
icon={"institution"}
color={"neutral"}
variant={"tonal"}
/>
</View>
{/* RIGHT */}
<Image
source={interno}
resizeMode={"contain"}
style={{ width: "100%", height: 32 }}
/>
</View>
<VSpacer size={24} />
{/* TITLE */}
<H1>{I18n.t("features.itWallet.presentation.dataScreen.title")}</H1>
<VSpacer />
{/* BODY */}
<Body>
{I18n.t("features.itWallet.presentation.dataScreen.subtitle", {
organizationName: rpPidMock.organizationName
})}
</Body>
<VSpacer />
{/* INFO LINK */}
<LabelLink onPress={() => present()}>
{I18n.t("features.itWallet.presentation.dataScreen.why")}
</LabelLink>
<VSpacer size={24} />
{/* REQUIRED DATA SECTION */}
<View style={styles.requireDataSection}>
<Icon name="security" color="grey-300" />
<HSpacer size={8} />
<H6 color="grey-700">
{I18n.t(
"features.itWallet.presentation.dataScreen.requiredClaims"
)}
</H6>
</View>
<VSpacer size={24} />
<ItwBulletList data={rpPidMock.requestedClaims(decodedPid)} />
<VSpacer size={24} />
{/* PRIVACY SECTION */}
<FeatureInfo
pictogramName="passcode"
body={I18n.t(
"features.itWallet.presentation.dataScreen.privacy.usage"
)}
/>
<VSpacer />
<FeatureInfo
pictogramName="trash"
body={I18n.t(
"features.itWallet.presentation.dataScreen.privacy.deletion"
)}
/>
<VSpacer size={32} />
{/* TOS SECTION */}
<ItwTextInfo
content={I18n.t("features.itWallet.presentation.dataScreen.tos")}
/>
<VSpacer size={32} />
</View>
<FooterWithButtons
primary={{
type: "Outline",
buttonProps: {
color: "primary",
accessibilityLabel: I18n.t("global.buttons.cancel"),
onPress: () => showCancelAlert(alertOnPress),
label: I18n.t("global.buttons.cancel")
}
}}
secondary={{
type: "Solid",
buttonProps: {
color: "primary",
icon: "security",
iconPosition: "end",
accessibilityLabel: I18n.t("global.buttons.continue"),
onPress: () =>
navigation.navigate(
ITW_ROUTES.PRESENTATION.CROSS_DEVICE.RESULT
),
label: I18n.t("global.buttons.continue")
}
}}
type="TwoButtonsInlineHalf"
/>
</ForceScrollDownView>
</SafeAreaView>
</BaseScreenComponent>
);

const ErrorView = () => {
const onPress = () => navigation.goBack();
const mappedError = getItwGenericMappedError(onPress);
return <ItwKoView {...mappedError} />;
};

const DecodePidOrErrorView = () =>
pipe(
decodedPid,
O.fold(
() => <ErrorView />,
some => <RpPreviewView decodedPid={some} />
)
);

return (
<>
<DecodePidOrErrorView />
{bottomSheet}
</>
);
};

export default ItwPidPresentationDataScreen;

const styles = StyleSheet.create({
secondHeader: {
flexDirection: "row",
alignContent: "center",
alignItems: "center"
},
secondHeaderLeft: {
flexDirection: "row",
alignItems: "center"
},
requireDataSection: {
flexDirection: "row",
alignItems: "center"
}
});
Loading

0 comments on commit f9d7272

Please sign in to comment.