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

feat(IT Wallet): [SIW-2007] Add local FF for offline wallet access #6678

Merged
merged 7 commits into from
Feb 7, 2025
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ exports[`Check the addition for new fields to the persisted store. If one of thi
"isFingerprintEnabled": undefined,
"isIOMarkdownEnabledOnMessagesAndServices": false,
"isIdPayTestEnabled": false,
"isItwOfflineAccessEnabled": false,
"isMixpanelEnabled": null,
"isPagoPATestEnabled": false,
"isPnTestEnabled": false,
Expand Down
13 changes: 12 additions & 1 deletion ts/boot/configureStoreAndPersistor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import { configureReactotron } from "./configureRectotron";
/**
* Redux persist will migrate the store to the current version
*/
const CURRENT_REDUX_STORE_VERSION = 39;
const CURRENT_REDUX_STORE_VERSION = 40;

// see redux-persist documentation:
// https://github.com/rt2zz/redux-persist/blob/master/docs/migrations.md
Expand Down Expand Up @@ -466,6 +466,17 @@ const migrations: MigrationManifest = {
isIOMarkdownEnabledOnMessagesAndServices: false
}
};
},
// Add 'isItwOfflineAccessEnabled' to 'persistedPreferences'
"40": (state: PersistedState) => {
const typedState = state as GlobalState;
return {
...state,
persistedPreferences: {
...typedState.persistedPreferences,
isItwOfflineAccessEnabled: false
}
};
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ListItemInfo,
VSpacer
} from "@pagopa/io-app-design-system";
import { View } from "react-native";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { itwLifecycleWalletReset } from "../../lifecycle/store/actions";
import {
Expand All @@ -31,7 +32,7 @@ export const ItwLifecycleSection = () => {
};

return (
<>
<View>
<ListItemHeader label="Wallet Instance Lifecycle" />
<ListItemInfo label="Current status" value={getLifecycleStateLabel()} />
<VSpacer size={8} />
Expand All @@ -40,6 +41,6 @@ export const ItwLifecycleSection = () => {
label="Reset Wallet Instance"
onPress={resetWalletInstance}
/>
</>
</View>
);
};
Original file line number Diff line number Diff line change
@@ -1,34 +1,36 @@
import { H3, VStack } from "@pagopa/io-app-design-system";
import { ListItemHeader, VStack } from "@pagopa/io-app-design-system";
import { useState } from "react";
import { View } from "react-native";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { ItwSkeumorphicCard } from "../../common/components/ItwSkeumorphicCard";
import { FlipGestureDetector } from "../../common/components/ItwSkeumorphicCard/FlipGestureDetector";
import { getCredentialStatusObject } from "../../common/utils/itwCredentialStatusUtils";
import { ItwStoredCredentialsMocks } from "../../common/utils/itwMocksUtils";
import { StoredCredential } from "../../common/utils/itwTypesUtils";
import { ItwPresentationCredentialCardFlipButton } from "../../presentation/details/components/ItwPresentationCredentialCardFlipButton";
import { getCredentialStatusObject } from "../../common/utils/itwCredentialStatusUtils";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { ITW_ROUTES } from "../../navigation/routes";
import { ItwPresentationCredentialCardFlipButton } from "../../presentation/details/components/ItwPresentationCredentialCardFlipButton";

const credentialsWithCard: ReadonlyArray<string> = [
"MDL",
"EuropeanDisabilityCard"
];

export const ItwSkeumorphicCredentialSection = () => (
<VStack space={16}>
<H3>{"Skeumorphic credential card"}</H3>

{Object.values(ItwStoredCredentialsMocks)
.filter(({ credentialType }) =>
credentialsWithCard.includes(credentialType)
)
.map(credential => (
<ItwSkeumorphicCredentialItem
key={credential.credentialType}
credential={credential}
/>
))}
</VStack>
<View>
<ListItemHeader label="Skeumorphic credential card" />
<VStack space={16}>
{Object.values(ItwStoredCredentialsMocks)
.filter(({ credentialType }) =>
credentialsWithCard.includes(credentialType)
)
.map(credential => (
<ItwSkeumorphicCredentialItem
key={credential.credentialType}
credential={credential}
/>
))}
</VStack>
</View>
);

const ItwSkeumorphicCredentialItem = ({
Expand Down
79 changes: 20 additions & 59 deletions ts/features/itwallet/playgrounds/screens/ItwPlayground.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import {
ContentWrapper,
Divider,
ListItemHeader,
ListItemNav,
VSpacer
ListItemSwitch,
VStack
} from "@pagopa/io-app-design-system";
import { useFocusEffect } from "@react-navigation/native";
import { useCallback } from "react";
import { ScrollView } from "react-native-gesture-handler";
import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel";
import { CredentialType } from "../../common/utils/itwMocksUtils";
import { ItwCredentialIssuanceMachineContext } from "../../machine/provider";
import { setItwOfflineAccessEnabled } from "../../../../store/actions/persistedPreferences";
import { useIODispatch, useIOSelector } from "../../../../store/hooks";
import { isItwOfflineAccessEnabledSelector } from "../../../../store/reducers/persistedPreferences";
import { ItwLifecycleSection } from "../components/ItwLifecycleSection";
import { ItwSkeumorphicCredentialSection } from "../components/ItwSkeumorphicCredentialSection";

Expand All @@ -19,65 +16,29 @@ import { ItwSkeumorphicCredentialSection } from "../components/ItwSkeumorphicCre
* @returns a screen with a list of playgrounds for the ITW
*/
const ItwPlayground = () => {
const credentialMachineRef =
ItwCredentialIssuanceMachineContext.useActorRef();

useFocusEffect(
useCallback(() => {
// Resets the machine in case they were left in s failure state
credentialMachineRef.send({ type: "reset" });
}, [credentialMachineRef])
const dispatch = useIODispatch();
const isOfflineAccessEnabled = useIOSelector(
isItwOfflineAccessEnabledSelector
);

useHeaderSecondLevel({
title: "ITW Playground"
title: "Documenti su IO Playgrounds"
});

const handleStartCredentialIssuance =
(credentialType: CredentialType) => () => {
credentialMachineRef.send({
type: "select-credential",
credentialType
});
};

return (
<ScrollView contentContainerStyle={{ paddingBottom: 64 }}>
<ContentWrapper>
{/* Issuing Playground */}
<ListItemHeader label="Credentials issuing" />
<ListItemNav
value="mDL issuing"
accessibilityLabel={"mDL Issuing"}
description="Start the issuing flow to get your mobile driving license"
onPress={handleStartCredentialIssuance(
CredentialType.DRIVING_LICENSE
)}
/>
<Divider />
<ListItemNav
value="TS issuing"
accessibilityLabel={"TS Issuing"}
description="Start the issuing flow to get your health insurance card"
onPress={handleStartCredentialIssuance(
CredentialType.EUROPEAN_HEALTH_INSURANCE_CARD
)}
/>
<Divider />
<ListItemNav
value="DC issuing"
accessibilityLabel={"DC Issuing"}
description="Start the issuing flow to get your european disability card card"
onPress={handleStartCredentialIssuance(
CredentialType.EUROPEAN_DISABILITY_CARD
)}
/>
<VSpacer size={16} />
<ItwLifecycleSection />
<VSpacer size={16} />
{/* Other Playgrounds */}
<ListItemHeader label="Miscellaneous" />
<ItwSkeumorphicCredentialSection />
<VStack space={8}>
<ListItemSwitch
label="Enable offline access"
value={isOfflineAccessEnabled}
onSwitchValueChange={() => {
dispatch(setItwOfflineAccessEnabled(!isOfflineAccessEnabled));
}}
/>
<ItwLifecycleSection />
<ItwSkeumorphicCredentialSection />
</VStack>
</ContentWrapper>
</ScrollView>
);
Expand Down
5 changes: 5 additions & 0 deletions ts/store/actions/persistedPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ export const setIOMarkdownEnabledOnMessagesAndServices = createStandardAction(
"PREFERENCES_IO_MARKDOWN_ON_MESSAGES_AND_SERVICES_SET"
)<{ enabledOnMessagesAndServices: boolean }>();

export const setItwOfflineAccessEnabled = createStandardAction(
"PREFERENCES_ITW_OFFLINE_ACCESS_ENABLED_SET"
)<boolean>();

export type PersistedPreferencesActions = ActionType<
| typeof preferenceFingerprintIsEnabledSaveSuccess
| typeof preferredCalendarSaveSuccess
Expand All @@ -66,4 +70,5 @@ export type PersistedPreferencesActions = ActionType<
| typeof preferencesIdPayTestSetEnabled
| typeof preferencesDesignSystemSetEnabled
| typeof setIOMarkdownEnabledOnMessagesAndServices
| typeof setItwOfflineAccessEnabled
>;
17 changes: 15 additions & 2 deletions ts/store/reducers/persistedPreferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import {
preferencesPnTestEnvironmentSetEnabled,
preferencesIdPayTestSetEnabled,
preferencesDesignSystemSetEnabled,
setIOMarkdownEnabledOnMessagesAndServices
setIOMarkdownEnabledOnMessagesAndServices,
setItwOfflineAccessEnabled
} from "../actions/persistedPreferences";
import { Action } from "../actions/types";
import { differentProfileLoggedIn } from "../actions/crossSessions";
Expand All @@ -46,6 +47,7 @@ export type PersistedPreferencesState = Readonly<{
// be sure to handle such case when reading and using this value
isDesignSystemEnabled: boolean;
isIOMarkdownEnabledOnMessagesAndServices: boolean;
isItwOfflineAccessEnabled: boolean;
}>;

export const initialPreferencesState: PersistedPreferencesState = {
Expand All @@ -60,7 +62,8 @@ export const initialPreferencesState: PersistedPreferencesState = {
isPnTestEnabled: false,
isIdPayTestEnabled: false,
isDesignSystemEnabled: false,
isIOMarkdownEnabledOnMessagesAndServices: false
isIOMarkdownEnabledOnMessagesAndServices: false,
isItwOfflineAccessEnabled: false
};

export default function preferencesReducer(
Expand Down Expand Up @@ -164,6 +167,13 @@ export default function preferencesReducer(
};
}

if (isActionOf(setItwOfflineAccessEnabled, action)) {
return {
...state,
isItwOfflineAccessEnabled: action.payload
};
}

return state;
}

Expand Down Expand Up @@ -211,6 +221,9 @@ export const isIOMarkdownEnabledLocallySelector = (
): boolean =>
state.persistedPreferences.isIOMarkdownEnabledOnMessagesAndServices;

export const isItwOfflineAccessEnabledSelector = (state: GlobalState) =>
state.persistedPreferences.isItwOfflineAccessEnabled;

// returns the preferred language as an Option from the persisted store
export const preferredLanguageSelector = createSelector<
GlobalState,
Expand Down
Loading