ts/screens/onboarding/OnboardingServicesPreferenceScreen.tsx
import {
Banner,
ContentWrapper,
IOStyles,
VSpacer,
useIOToast
} from "@pagopa/io-app-design-system";
import * as pot from "@pagopa/ts-commons/lib/pot";
import React, { ReactElement, useCallback, useEffect, useState } from "react";
import { SafeAreaView } from "react-native";
import { ServicesPreferencesModeEnum } from "../../../definitions/backend/ServicesPreferencesMode";
import LoadingSpinnerOverlay from "../../components/LoadingSpinnerOverlay";
import I18n from "../../i18n";
import {
IOStackNavigationRouteProps,
useIONavigation
} from "../../navigation/params/AppParamsList";
import { OnboardingParamsList } from "../../navigation/params/OnboardingParamsList";
import ROUTES from "../../navigation/routes";
import { servicesOptinCompleted } from "../../store/actions/onboarding";
import { profileUpsert } from "../../store/actions/profile";
import { useIODispatch, useIOSelector, useIOStore } from "../../store/hooks";
import {
isServicesPreferenceModeSet,
profileSelector,
profileServicePreferencesModeSelector
} from "../../store/reducers/profile";
import { getFlowType } from "../../utils/analytics";
import { emptyContextualHelp } from "../../utils/emptyContextualHelp";
import { useOnFirstRender } from "../../utils/hooks/useOnFirstRender";
import { usePrevious } from "../../utils/hooks/usePrevious";
import {
trackServiceConfiguration,
trackServiceConfigurationScreen
} from "../profile/analytics";
import { useManualConfigBottomSheet } from "../profile/components/services/ManualConfigBottomSheet";
import ServicesContactComponent from "../profile/components/services/ServicesContactComponent";
import { IOScrollViewWithLargeHeader } from "../../components/ui/IOScrollViewWithLargeHeader";
import useContentWithFF from "../profile/useContentWithFF";
export type OnboardingServicesPreferenceScreenNavigationParams = {
isFirstOnboarding: boolean;
};
type Props = IOStackNavigationRouteProps<
OnboardingParamsList,
"ONBOARDING_SERVICES_PREFERENCE"
>;
const OnboardingServicesPreferenceScreen = (props: Props): ReactElement => {
const dispatch = useIODispatch();
const navigation = useIONavigation();
const toast = useIOToast();
const isFirstOnboarding = props.route.params.isFirstOnboarding;
const store = useIOStore();
const profile = useIOSelector(profileSelector);
const prevProfile = usePrevious(profile);
const isLoading = pot.isUpdating(profile) || pot.isLoading(profile);
const profileServicePreferenceMode = useIOSelector(
profileServicePreferencesModeSelector
);
const prevMode = usePrevious(profileServicePreferenceMode);
// if the user is not new and he/she hasn't a preference set, pre-set with AUTO mode
const mode = profileServicePreferenceMode;
const content = useContentWithFF("services.optIn.preferences.banner");
const [modeSelected, setModeSelected] = useState<
ServicesPreferencesModeEnum | undefined
>(mode);
const dispatchServicesOptinCompleted = useCallback(
() => dispatch(servicesOptinCompleted()),
[dispatch]
);
const onServicePreferenceSelected = useCallback(
(mode: ServicesPreferencesModeEnum) =>
dispatch(
profileUpsert.request({ service_preferences_settings: { mode } })
),
[dispatch]
);
const navigateToOnboardingServicePreferenceComplete = useCallback(() => {
navigation.navigate(ROUTES.ONBOARDING, {
screen: ROUTES.ONBOARDING_SERVICES_PREFERENCE_COMPLETE
});
}, [navigation]);
const onContinue = useCallback(
(isFirstOnboarding: boolean) =>
// if the user is not new, navigate to the thank-you screen
!isFirstOnboarding
? navigateToOnboardingServicePreferenceComplete()
: dispatchServicesOptinCompleted(),
[
dispatchServicesOptinCompleted,
navigateToOnboardingServicePreferenceComplete
]
);
const handleOnContinue = useCallback(() => {
void trackServiceConfiguration(
profileServicePreferenceMode,
getFlowType(true, isFirstOnboarding),
store.getState()
);
onContinue(isFirstOnboarding);
toast.hideAll();
}, [
isFirstOnboarding,
onContinue,
profileServicePreferenceMode,
store,
toast
]);
const selectCurrentMode = useCallback(
(mode: ServicesPreferencesModeEnum) => {
onServicePreferenceSelected(mode);
},
[onServicePreferenceSelected]
);
const { present: confirmManualConfig, manualConfigBottomSheet } =
useManualConfigBottomSheet(() => {
selectCurrentMode(ServicesPreferencesModeEnum.MANUAL);
});
const handleOnSelectMode = useCallback(
(mode: ServicesPreferencesModeEnum) => {
// if user's choice is 'manual', open bottom sheet to ask confirmation
if (mode === ServicesPreferencesModeEnum.MANUAL) {
confirmManualConfig();
return;
}
selectCurrentMode(mode);
},
[confirmManualConfig, selectCurrentMode]
);
useOnFirstRender(() => {
trackServiceConfigurationScreen(getFlowType(true, isFirstOnboarding));
});
useEffect(() => {
// show error toast only when the profile updating fails
// otherwise, if the profile is in error state,
// the toast will be shown immediately without any updates
if (prevProfile && !pot.isError(prevProfile) && pot.isError(profile)) {
toast.error(I18n.t("global.genericError"));
return;
}
// if profile preferences are updated correctly
// the button is selected
// and the success banner is shown
if (
prevProfile &&
pot.isUpdating(prevProfile) &&
pot.isSome(profile) &&
profileServicePreferenceMode !== prevMode
) {
setModeSelected(profileServicePreferenceMode);
toast.hideAll();
toast.success(
profileServicePreferenceMode === ServicesPreferencesModeEnum.MANUAL
? I18n.t("services.optIn.preferences.manualConfig.successAlert")
: I18n.t("services.optIn.preferences.quickConfig.successAlert")
);
}
}, [prevMode, prevProfile, profile, profileServicePreferenceMode, toast]);
// show a badge when the user is not new
// As explained in this comment (https://pagopa.atlassian.net/browse/IOPID-1511?focusedCommentId=126354)
// a future feature will be need this value,
// so I didn't delete it even though it is not used
const showBadge = !isFirstOnboarding;
return (
<LoadingSpinnerOverlay isLoading={isLoading}>
<IOScrollViewWithLargeHeader
title={{
label: I18n.t("services.optIn.preferences.title")
}}
canGoback={false}
description={I18n.t("services.optIn.preferences.body")}
headerActionsProp={{ showHelp: true }}
contextualHelp={emptyContextualHelp}
actions={{
type: "SingleButton",
primary: {
label: I18n.t("global.buttons.confirm"),
onPress: handleOnContinue,
accessibilityLabel: I18n.t("global.buttons.confirm"),
disabled: !isServicesPreferenceModeSet(modeSelected)
}
}}
>
<SafeAreaView style={IOStyles.flex}>
<ContentWrapper>
<ServicesContactComponent
mode={modeSelected}
onSelectMode={handleOnSelectMode}
showBadge={showBadge}
/>
<VSpacer size={16} />
<Banner
size="small"
color="neutral"
pictogramName="settings"
content={content}
/>
</ContentWrapper>
{manualConfigBottomSheet}
</SafeAreaView>
</IOScrollViewWithLargeHeader>
</LoadingSpinnerOverlay>
);
};
export default OnboardingServicesPreferenceScreen;