Skip to content

Commit

Permalink
feat(IT Wallet): [SIW-1029] Add IT Wallet credential preview screen (#…
Browse files Browse the repository at this point in the history
…5841)

> [!WARNING]
> This PR depends on #5832 

## Short description
This PR implements the IT Wallet credential preview screen which allows
the user to see the data of a credential before it will be stored in the
device.

> [!NOTE]
> This screen is currently using mocked **mDL** data and does not
represent the final implementation of the component, which will come in
a later PR.

## List of changes proposed in this pull request
- Added `ItwIssuanceCredentialPreviewScreen` component
- Added required routes, parameters and navigation components
- Added `ItwCredentialPreviewScreenContent` component which renders the
preview of a credential. This component will be used inside bot the eID
and credential preview screen, which are two separate flows but share
the same UI.

## How to test
Navigate to **Profile > Playgrounds > IT Wallet > Credential preview**.
Check that everything is being rendered correctly.

## Previews

| iOS | Android |
| --- | --- |
| <video
src="https://github.com/pagopa/io-app/assets/6160324/aae69f8a-eada-48dd-9332-e47e416ca259"
/> | <video
src="https://github.com/pagopa/io-app/assets/6160324/437a0b4d-863b-4e79-8772-0831d213a5ad"
/>

---------

Co-authored-by: LazyAfternoons <[email protected]>
  • Loading branch information
mastro993 and LazyAfternoons authored Jun 10, 2024
1 parent 01ec4fa commit 47d1dbb
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { IOColors, VSpacer, useIOTheme } from "@pagopa/io-app-design-system";
import React from "react";
import { ColorValue, StyleSheet, View } from "react-native";
import { FooterActions } from "../../../../components/ui/FooterActions";
import I18n from "../../../../i18n";
import { identificationRequest } from "../../../../store/actions/identification";
import { useIODispatch } from "../../../../store/hooks";
import { ItwCredentialClaimsList } from "../../common/components/ItwCredentialClaimList";
import { useItwDismissalDialog } from "../../common/hooks/useItwDismissalDialog";
import { StoredCredential } from "../../common/utils/itwTypesUtils";
import { ItwCredentialCard } from "../../common/components/ItwCredentialCard";
import { CredentialType } from "../../common/utils/itwMocksUtils";

type Props = {
data: StoredCredential;
onStoreSuccess: () => void;
};

export const ItwCredentialPreviewScreenContent = ({
data,
onStoreSuccess
}: Props) => {
const theme = useIOTheme();
const dispatch = useIODispatch();
const dismissDialog = useItwDismissalDialog();

const handleSaveToWallet = () => {
dispatch(
identificationRequest(
false,
true,
undefined,
{
label: I18n.t("global.buttons.cancel"),
onCancel: () => undefined
},
{
onSuccess: onStoreSuccess
}
)
);
};

const backgroundColor: ColorValue = IOColors[theme["appBackground-primary"]];

return (
<>
<View style={styles.preview}>
<ItwCredentialCard
credentialType={data.credentialType as CredentialType}
isPreview={true}
/>
</View>
<View style={styles.dropShadow}>
<VSpacer size={24} />
</View>
<View style={[styles.content, { backgroundColor }]}>
<ItwCredentialClaimsList data={data} />
</View>
<FooterActions
fixed={false}
actions={{
type: "TwoButtons",
primary: {
icon: "add",
iconPosition: "end",
label: I18n.t(
"features.itWallet.issuance.credentialPreview.actions.primary"
),
onPress: handleSaveToWallet
},
secondary: {
label: I18n.t(
"features.itWallet.issuance.credentialPreview.actions.secondary"
),
onPress: dismissDialog.show
}
}}
/>
</>
);
};

const styles = StyleSheet.create({
preview: {
paddingHorizontal: 24
},
dropShadow: {
backgroundColor: IOColors.white,
shadowColor: IOColors.black,
shadowOffset: { width: 0, height: -4 },
shadowRadius: 20,
shadowOpacity: 0.2,
elevation: 5
},
content: {
flex: 1,
paddingHorizontal: 24
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React from "react";
import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent";
import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader";
import I18n from "../../../../i18n";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import {
ItWalletError,
getItwGenericMappedError
} from "../../common/utils/itwErrorsUtils";
import { ItwCredentialsMocks } from "../../common/utils/itwMocksUtils";
import { StoredCredential } from "../../common/utils/itwTypesUtils";
import { ITW_ROUTES } from "../../navigation/routes";
import { ItwCredentialPreviewScreenContent } from "../components/ItwCredentialPreviewScreenContent";

export const ItwIssuanceCredentialPreviewScreen = () => {
const navigation = useIONavigation();
const credentialOption = O.some(ItwCredentialsMocks.mdl);

const handleStoreCredentialSuccess = () => {
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.ISSUANCE.RESULT
});
};

/**
* Renders the content of the screen if the credential is decoded.
* @param credential - the decoded credential
*/
const ContentView = ({ credential }: { credential: StoredCredential }) => {
React.useLayoutEffect(() => {
navigation.setOptions({
headerShown: true
});
}, []);

return (
<IOScrollViewWithLargeHeader
excludeEndContentMargin
title={{
label: I18n.t("features.itWallet.issuance.credentialPreview.title", {
credential: credential.displayData.title
})
}}
>
<ItwCredentialPreviewScreenContent
data={credential}
onStoreSuccess={handleStoreCredentialSuccess}
/>
</IOScrollViewWithLargeHeader>
);
};

/**
* Error view component which currently displays a generic error.
* @param error - optional ItWalletError to be displayed.
*/
const ErrorView = ({ error: _ }: { error?: ItWalletError }) => {
const mappedError = getItwGenericMappedError(() => navigation.goBack());
return <OperationResultScreenContent {...mappedError} />;
};

return pipe(
credentialOption,
O.fold(
() => <ErrorView />,
cred => <ContentView credential={cred} />
)
);
};
Original file line number Diff line number Diff line change
@@ -1,71 +1,40 @@
import { IOColors, VSpacer, useIOTheme } from "@pagopa/io-app-design-system";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React from "react";
import { ColorValue, StyleSheet, View } from "react-native";
import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent";
import { FooterActions } from "../../../../components/ui/FooterActions";
import { IOScrollViewWithLargeHeader } from "../../../../components/ui/IOScrollViewWithLargeHeader";
import I18n from "../../../../i18n";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { identificationRequest } from "../../../../store/actions/identification";
import { useIODispatch } from "../../../../store/hooks";
import { ItwCredentialClaimsList } from "../../common/components/ItwCredentialClaimList";
import { useItwDismissalDialog } from "../../common/hooks/useItwDismissalDialog";
import {
ItWalletError,
getItwGenericMappedError
} from "../../common/utils/itwErrorsUtils";
import {
CredentialType,
ItwCredentialsMocks
} from "../../common/utils/itwMocksUtils";
import { ItwCredentialsMocks } from "../../common/utils/itwMocksUtils";
import { StoredCredential } from "../../common/utils/itwTypesUtils";
import { ITW_ROUTES } from "../../navigation/routes";
import { ItwCredentialCard } from "../../common/components/ItwCredentialCard";
import { ItwCredentialPreviewScreenContent } from "../components/ItwCredentialPreviewScreenContent";

export const ItwIssuanceEidPreviewScreen = () => {
const navigation = useIONavigation();
const eidOption = O.some(ItwCredentialsMocks.eid);

const handleStoreCredentialSuccess = () => {
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.ISSUANCE.RESULT
});
};

/**
* Renders the content of the screen if the PID is decoded.
* @param eid - the decoded eID
*/
const ContentView = ({ eid }: { eid: StoredCredential }) => {
const theme = useIOTheme();
const dispatch = useIODispatch();
const dismissDialog = useItwDismissalDialog();

const backgroundColor: ColorValue =
IOColors[theme["appBackground-primary"]];

React.useLayoutEffect(() => {
navigation.setOptions({
headerShown: true
});
}, []);

const handleSaveToWallet = () => {
dispatch(
identificationRequest(
false,
true,
undefined,
{
label: I18n.t("global.buttons.cancel"),
onCancel: () => undefined
},
{
onSuccess: () =>
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.ISSUANCE.RESULT
})
}
)
);
};

return (
<IOScrollViewWithLargeHeader
excludeEndContentMargin
Expand All @@ -75,37 +44,9 @@ export const ItwIssuanceEidPreviewScreen = () => {
})
}}
>
<View style={styles.preview}>
<ItwCredentialCard
credentialType={CredentialType.PID}
isPreview={true}
/>
</View>
<View style={styles.dropShadow}>
<VSpacer size={24} />
</View>
<View style={[styles.content, { backgroundColor }]}>
<ItwCredentialClaimsList data={eid} />
</View>
<FooterActions
fixed={false}
actions={{
type: "TwoButtons",
primary: {
icon: "add",
iconPosition: "end",
label: I18n.t(
"features.itWallet.issuance.credentialPreview.actions.primary"
),
onPress: handleSaveToWallet
},
secondary: {
label: I18n.t(
"features.itWallet.issuance.credentialPreview.actions.secondary"
),
onPress: dismissDialog.show
}
}}
<ItwCredentialPreviewScreenContent
data={eid}
onStoreSuccess={handleStoreCredentialSuccess}
/>
</IOScrollViewWithLargeHeader>
);
Expand All @@ -128,21 +69,3 @@ export const ItwIssuanceEidPreviewScreen = () => {
)
);
};

const styles = StyleSheet.create({
preview: {
paddingHorizontal: 24
},
dropShadow: {
backgroundColor: IOColors.white,
shadowColor: IOColors.black,
shadowOffset: { width: 0, height: -4 },
shadowRadius: 20,
shadowOpacity: 0.2,
elevation: 5
},
content: {
flex: 1,
paddingHorizontal: 24
}
});
1 change: 1 addition & 0 deletions ts/features/itwallet/navigation/ItwParamsList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ export type ItwParamsList = {
[ITW_ROUTES.IDENTIFICATION.IDP_SELECTION]: undefined;
// ISSUANCE
[ITW_ROUTES.ISSUANCE.EID_PREVIEW]: undefined;
[ITW_ROUTES.ISSUANCE.CREDENTIAL_PREVIEW]: undefined;
[ITW_ROUTES.ISSUANCE.RESULT]: undefined;
};
6 changes: 6 additions & 0 deletions ts/features/itwallet/navigation/ItwStackNavigator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ItwDiscoveryInfoScreen } from "../discovery/screens/ItwDiscoveryInfoScr
import { ItwIdentificationIdpSelectionScreen } from "../identification/screens/ItwIdentificationIdpSelectionScreen";
import { ItwIdentificationModeSelectionScreen } from "../identification/screens/ItwIdentificationModeSelectionScreen";
import { ItwIdentificationNfcInstructionsScreen } from "../identification/screens/ItwIdentificationNfcInstructionsScreen";
import { ItwIssuanceCredentialPreviewScreen } from "../issuance/screens/ItwIssuanceCredentialPreviewScreen";
import { ItwIssuanceEidPreviewScreen } from "../issuance/screens/ItwIssuanceEidPreviewScreen";
import { ItwIssuanceEidResultScreen } from "../issuance/screens/ItwIssuanceEidResultScreen";
import { ItwParamsList } from "./ItwParamsList";
Expand Down Expand Up @@ -41,6 +42,11 @@ export const ItwStackNavigator = () => (
component={ItwIssuanceEidPreviewScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name={ITW_ROUTES.ISSUANCE.CREDENTIAL_PREVIEW}
component={ItwIssuanceCredentialPreviewScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name={ITW_ROUTES.ISSUANCE.RESULT}
component={ItwIssuanceEidResultScreen}
Expand Down
1 change: 1 addition & 0 deletions ts/features/itwallet/navigation/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export const ITW_ROUTES = {
} as const,
ISSUANCE: {
EID_PREVIEW: "ITW_ISSUANCE_EID_PREVIEW",
CREDENTIAL_PREVIEW: "ITW_ISSUANCE_CREDENTIAL_PREVIEW",
RESULT: "ITW_ISSUANCE_RESULT"
} as const
};
14 changes: 14 additions & 0 deletions ts/screens/profile/playgrounds/ItwPlayground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ const ItwPlayground = () => {
});
};

const navigateToCredentialPreview = () => {
navigation.navigate(ITW_ROUTES.MAIN, {
screen: ITW_ROUTES.ISSUANCE.CREDENTIAL_PREVIEW
});
};

return (
<ScrollView>
<ContentWrapper>
Expand Down Expand Up @@ -91,6 +97,14 @@ const ItwPlayground = () => {
onPress={() => undefined}
/>
<Divider />
{/* Credential Preview */}
<ListItemNav
value="Credential preview"
accessibilityLabel="Credential preview"
description="Open the credential preview screen"
onPress={navigateToCredentialPreview}
/>
<Divider />
<VSpacer />
<H3>{"IT Wallet markdown preview"}</H3>
{/* Markdown ITW Playground */}
Expand Down

0 comments on commit 47d1dbb

Please sign in to comment.