ts/navigation/components/HeaderFirstLevelHandler.tsx
import {
HeaderActionProps,
HeaderFirstLevel
} from "@pagopa/io-app-design-system";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React, { ComponentProps, useCallback, useMemo } from "react";
import { useServicesHomeBottomSheet } from "../../features/services/home/hooks/useServicesHomeBottomSheet";
import { useWalletHomeHeaderBottomSheet } from "../../components/wallet/WalletHomeHeader";
import { MESSAGES_ROUTES } from "../../features/messages/navigation/routes";
import {
SupportRequestParams,
useStartSupportRequest
} from "../../hooks/useStartSupportRequest";
import I18n from "../../i18n";
import { useIODispatch, useIOSelector, useIOStore } from "../../store/hooks";
import { SERVICES_ROUTES } from "../../features/services/common/navigation/routes";
import { MainTabParamsList } from "../params/MainTabParamsList";
import ROUTES from "../routes";
import { useIONavigation } from "../params/AppParamsList";
import {
isNewPaymentSectionEnabledSelector,
isSettingsVisibleAndHideProfileSelector
} from "../../store/reducers/backendStatus";
import * as analytics from "../../features/services/common/analytics";
import {
isArchivingInProcessingModeSelector,
isArchivingInSchedulingModeSelector
} from "../../features/messages/store/reducers/archiving";
import { resetMessageArchivingAction } from "../../features/messages/store/actions/archiving";
import { useStatusAlertProps } from "../../hooks/useStatusAlertProps";
type HeaderFirstLevelProps = ComponentProps<typeof HeaderFirstLevel>;
type TabRoutes = keyof MainTabParamsList;
const headerHelpByRoute: Record<TabRoutes, SupportRequestParams> = {
[MESSAGES_ROUTES.MESSAGES_HOME]: {
faqCategories: ["messages"],
contextualHelpMarkdown: {
title: "messages.contextualHelpTitle",
body: "messages.contextualHelpContent"
}
},
// TODO: delete this route when the showBarcodeScanSection
// and isSettingsVisibleAndHideProfileSelector FF will be deleted
[ROUTES.PROFILE_MAIN]: {
faqCategories: ["profile"],
contextualHelpMarkdown: {
title: "profile.main.contextualHelpTitle",
body: "profile.main.contextualHelpContent"
}
},
[SERVICES_ROUTES.SERVICES_HOME]: {
faqCategories: ["services"],
contextualHelpMarkdown: {
title: "services.contextualHelpTitle",
body: "services.contextualHelpContent"
}
},
[ROUTES.WALLET_HOME]: {
faqCategories: ["wallet", "wallet_methods"],
contextualHelpMarkdown: {
title: "wallet.contextualHelpTitle",
body: "wallet.contextualHelpContent"
}
},
[ROUTES.BARCODE_SCAN]: {},
[ROUTES.PAYMENTS_HOME]: {
faqCategories: ["wallet", "wallet_methods"],
contextualHelpMarkdown: {
title: "wallet.contextualHelpTitle",
body: "wallet.contextualHelpContent"
}
}
};
type Props = {
currentRouteName: TabRoutes;
};
/**
* This Component aims to handle the header of the first level screens. based on the current route
* it will set the header title and the contextual help and the actions related to the screen
* THIS COMPONENT IS NOT MEANT TO BE USED OUTSIDE THE NAVIGATION.
* THIS COMPONENT WILL BE REMOVED ONCE REACT NAVIGATION WILL BE UPGRADED TO V6
*/
export const HeaderFirstLevelHandler = ({ currentRouteName }: Props) => {
const alertProps = useStatusAlertProps(currentRouteName);
const dispatch = useIODispatch();
const navigation = useIONavigation();
const store = useIOStore();
const isNewWalletSectionEnabled = useIOSelector(
isNewPaymentSectionEnabledSelector
);
const isSettingsVisibleAndHideProfile = useIOSelector(
isSettingsVisibleAndHideProfileSelector
);
const {
bottomSheet: ServicesHomeBottomSheet,
present: presentServicesHomeBottomSheet
} = useServicesHomeBottomSheet();
const canNavigateIfIsArchivingCallback = useCallback(() => {
const state = store.getState();
// If the system is busy archiving or restoring messages,
// a new action (e.g. searching or navigating to settings) cannot be initiated
const isProcessingArchiveQueue = isArchivingInProcessingModeSelector(state);
if (isProcessingArchiveQueue) {
return false;
}
// If the user was choosing which messages to archive/restore,
// disable it before starting a new action (e.g. searching or
// navigating to settings), as the tab bar at the bottom is
// hidden and the new action could trigger a navigation flow back
// to another tab on the main screen without this bar being displayed.
const isSchedulingArchiving = isArchivingInSchedulingModeSelector(state);
if (isSchedulingArchiving) {
// Auto-reset does not provide feedback to the user
dispatch(resetMessageArchivingAction(undefined));
}
return true;
}, [dispatch, store]);
const messageSearchCallback = useCallback(() => {
if (canNavigateIfIsArchivingCallback()) {
navigation.navigate(MESSAGES_ROUTES.MESSAGES_SEARCH);
}
}, [canNavigateIfIsArchivingCallback, navigation]);
const handleSearchInstituion = useCallback(() => {
analytics.trackSearchStart({ source: "header_icon" });
navigation.navigate(SERVICES_ROUTES.SEARCH);
}, [navigation]);
const navigateToSettingsOrServicesPreferences = useCallback(() => {
if (isSettingsVisibleAndHideProfile) {
presentServicesHomeBottomSheet();
return;
}
navigation.navigate(ROUTES.PROFILE_NAVIGATOR, {
screen: ROUTES.PROFILE_PREFERENCES_SERVICES
});
}, [
isSettingsVisibleAndHideProfile,
navigation,
presentServicesHomeBottomSheet
]);
const navigateToSettingMainScreen = useCallback(() => {
navigation.navigate(ROUTES.PROFILE_NAVIGATOR, {
screen: ROUTES.SETTINGS_MAIN
});
}, [navigation]);
const navigateToSettingMainScreenFromMessageSection = useCallback(() => {
if (canNavigateIfIsArchivingCallback()) {
navigateToSettingMainScreen();
}
}, [canNavigateIfIsArchivingCallback, navigateToSettingMainScreen]);
const settingsAction: HeaderActionProps = useMemo(
() => ({
icon: "coggle",
accessibilityLabel: I18n.t("global.buttons.settings"),
onPress: navigateToSettingMainScreen
}),
[navigateToSettingMainScreen]
);
const settingsActionInMessageSection: HeaderActionProps = useMemo(
() => ({
icon: "coggle",
accessibilityLabel: I18n.t("global.buttons.settings"),
onPress: navigateToSettingMainScreenFromMessageSection
}),
[navigateToSettingMainScreenFromMessageSection]
);
const settingsActionInServicesSection: HeaderActionProps = useMemo(
() => ({
icon: "coggle",
accessibilityLabel: I18n.t("global.buttons.settings"),
onPress: navigateToSettingsOrServicesPreferences
}),
[navigateToSettingsOrServicesPreferences]
);
const requestParams = useMemo(
() =>
pipe(
headerHelpByRoute[currentRouteName as TabRoutes],
O.fromNullable,
O.getOrElse(() => ({}))
),
[currentRouteName]
);
const startSupportRequest = useStartSupportRequest(requestParams);
const helpAction: HeaderActionProps = useMemo(
() => ({
icon: "help",
accessibilityLabel: I18n.t(
"global.accessibility.contextualHelp.open.label"
),
onPress: startSupportRequest
}),
[startSupportRequest]
);
const {
bottomSheet: WalletHomeHeaderBottomSheet,
present: presentWalletHomeHeaderBottomsheet
} = useWalletHomeHeaderBottomSheet();
const walletAction: HeaderActionProps = useMemo(
() => ({
icon: "add",
accessibilityLabel: I18n.t("wallet.accessibility.addElement"),
onPress: presentWalletHomeHeaderBottomsheet,
testID: "walletAddNewPaymentMethodTestId"
}),
[presentWalletHomeHeaderBottomsheet]
);
const searchMessageAction: HeaderActionProps = useMemo(
() => ({
icon: "search",
accessibilityLabel: I18n.t("global.accessibility.search"),
onPress: messageSearchCallback
}),
[messageSearchCallback]
);
const searchInstitutionAction: HeaderActionProps = useMemo(
() => ({
icon: "search",
accessibilityLabel: I18n.t("global.accessibility.search"),
onPress: handleSearchInstituion
}),
[handleSearchInstituion]
);
const headerProps: HeaderFirstLevelProps = useMemo(() => {
const commonProp = {
ignoreSafeAreaMargin: !!alertProps
};
switch (currentRouteName) {
case SERVICES_ROUTES.SERVICES_HOME:
return {
...commonProp,
title: I18n.t("services.title"),
type: "threeActions",
firstAction: helpAction,
secondAction: settingsActionInServicesSection,
thirdAction: searchInstitutionAction
};
// TODO: delete this route when the showBarcodeScanSection
// and isSettingsVisibleAndHideProfileSelector FF will be deleted
case ROUTES.PROFILE_MAIN:
return {
...commonProp,
title: I18n.t("profile.main.title"),
type: "singleAction",
firstAction: helpAction
};
case ROUTES.WALLET_HOME:
if (isNewWalletSectionEnabled) {
return {
...commonProp,
title: I18n.t("wallet.wallet"),
firstAction: helpAction,
testID: "wallet-home-header-title",
...(isSettingsVisibleAndHideProfile
? {
type: "twoActions",
secondAction: settingsAction
}
: { type: "singleAction" })
};
}
return {
...commonProp,
title: I18n.t("wallet.wallet"),
firstAction: helpAction,
backgroundColor: "dark",
testID: "wallet-home-header-title",
...(isSettingsVisibleAndHideProfile
? {
type: "threeActions",
secondAction: settingsAction,
thirdAction: walletAction
}
: {
type: "twoActions",
secondAction: walletAction
})
};
case ROUTES.PAYMENTS_HOME:
return {
...commonProp,
title: I18n.t("features.payments.title"),
firstAction: helpAction,
...(isSettingsVisibleAndHideProfile
? {
type: "twoActions",
secondAction: settingsAction
}
: { type: "singleAction" })
};
case MESSAGES_ROUTES.MESSAGES_HOME:
default:
return {
...commonProp,
skipHeaderAutofocus: true,
title: I18n.t("messages.contentTitle"),
firstAction: helpAction,
...(isSettingsVisibleAndHideProfile
? {
type: "threeActions",
secondAction: settingsActionInMessageSection,
thirdAction: searchMessageAction
}
: {
type: "twoActions",
secondAction: searchMessageAction
})
};
}
}, [
alertProps,
currentRouteName,
helpAction,
settingsActionInServicesSection,
searchInstitutionAction,
isNewWalletSectionEnabled,
isSettingsVisibleAndHideProfile,
settingsAction,
walletAction,
settingsActionInMessageSection,
searchMessageAction
]);
return (
<>
<HeaderFirstLevel {...headerProps} />
{ServicesHomeBottomSheet}
{WalletHomeHeaderBottomSheet}
</>
);
};