Skip to content

Make tooltips dismissible with 'X' #59577

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

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions src/components/ProductTrainingContext/TOOLTIPS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ type ShouldShowConditionProps = {

type TooltipData = {
content: Array<{text: TranslationPaths; isBold: boolean}>;
onHideTooltip: () => void;
onHideTooltip: (isDismissedUsingCloseButton?: boolean) => void;
name: ProductTrainingTooltipName;
priority: number;
shouldShow: (props: ShouldShowConditionProps) => boolean;
Expand All @@ -35,7 +35,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.conciergeLHNGBR.part1', isBold: false},
{text: 'productTrainingTooltip.conciergeLHNGBR.part2', isBold: true},
],
onHideTooltip: () => dismissProductTraining(CONCEIRGE_LHN_GBR),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(CONCEIRGE_LHN_GBR, isDismissedUsingCloseButton),
name: CONCEIRGE_LHN_GBR,
priority: 1300,
// TODO: CONCEIRGE_LHN_GBR tooltip will be replaced by a tooltip in the #admins room
Expand All @@ -47,7 +47,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.saveSearchTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.saveSearchTooltip.part2', isBold: false},
],
onHideTooltip: () => dismissProductTraining(RENAME_SAVED_SEARCH),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(RENAME_SAVED_SEARCH, isDismissedUsingCloseButton),
name: RENAME_SAVED_SEARCH,
priority: 1250,
shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout,
Expand All @@ -58,7 +58,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.globalCreateTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.globalCreateTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(GLOBAL_CREATE_TOOLTIP),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(GLOBAL_CREATE_TOOLTIP, isDismissedUsingCloseButton),
name: GLOBAL_CREATE_TOOLTIP,
priority: 1200,
shouldShow: () => true,
Expand All @@ -69,7 +69,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP, isDismissedUsingCloseButton),
name: BOTTOM_NAV_INBOX_TOOLTIP,
priority: 900,
shouldShow: () => true,
Expand All @@ -80,7 +80,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.workspaceChatTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.workspaceChatTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP, isDismissedUsingCloseButton),
name: LHN_WORKSPACE_CHAT_TOOLTIP,
priority: 800,
shouldShow: () => true,
Expand All @@ -102,7 +102,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.scanTestTooltip.part4', isBold: true},
{text: 'productTrainingTooltip.scanTestTooltip.part5', isBold: false},
],
onHideTooltip: () => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(SCAN_TEST_TOOLTIP_MANAGER, isDismissedUsingCloseButton),
name: SCAN_TEST_TOOLTIP_MANAGER,
priority: 1000,
shouldShow: () => true,
Expand All @@ -113,7 +113,7 @@ const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
{text: 'productTrainingTooltip.scanTestTooltip.part7', isBold: true},
{text: 'productTrainingTooltip.scanTestTooltip.part8', isBold: false},
],
onHideTooltip: () => dismissProductTraining(SCAN_TEST_CONFIRMATION),
onHideTooltip: (isDismissedUsingCloseButton = false) => dismissProductTraining(SCAN_TEST_CONFIRMATION, isDismissedUsingCloseButton),
name: SCAN_TEST_CONFIRMATION,
priority: 1100,
shouldShow: () => true,
Expand Down
94 changes: 68 additions & 26 deletions src/components/ProductTrainingContext/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@ import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeedback';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSidePane from '@hooks/useSidePane';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import {parseFSAttributes} from '@libs/Fullstory';
import getPlatform from '@libs/getPlatform';
import {hasCompletedGuidedSetupFlowSelector} from '@libs/onboardingSelectors';
import isProductTrainingElementDismissed from '@libs/TooltipUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
Expand Down Expand Up @@ -98,7 +102,7 @@ function ProductTrainingContextProvider({children}: ChildrenProps) {
return false;
}

const isDismissed = !!dismissedProductTraining?.[tooltipName];
const isDismissed = isProductTrainingElementDismissed(tooltipName, dismissedProductTraining);

if (isDismissed) {
return false;
Expand Down Expand Up @@ -203,6 +207,22 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
*/
useLayoutEffect(parseFSAttributes, []);

const shouldShowProductTrainingTooltip = useMemo(() => {
return shouldShow && shouldRenderTooltip(tooltipName) && !shouldHideToolTip;
}, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]);

const hideTooltip = useCallback(
(isDismissedUsingCloseButton = false) => {
if (!shouldShowProductTrainingTooltip) {
return;
}
const tooltip = TOOLTIPS[tooltipName];
tooltip.onHideTooltip(isDismissedUsingCloseButton);
unregisterTooltip(tooltipName);
},
[tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip],
);

const renderProductTrainingTooltip = useCallback(() => {
const tooltip = TOOLTIPS[tooltipName];
return (
Expand All @@ -218,7 +238,8 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
styles.flexWrap,
styles.textAlignCenter,
styles.gap3,
styles.p2,
styles.pv2,
styles.ph1,
]}
>
<Icon
Expand All @@ -239,6 +260,34 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
);
})}
</Text>
{!tooltip?.shouldRenderActionButtons && (
<PressableWithoutFeedback
onPress={
getPlatform() !== CONST.PLATFORM.ANDROID
? () => {
hideTooltip(true);
}
: undefined
}
onPressIn={
getPlatform() === CONST.PLATFORM.ANDROID
? () => {
hideTooltip(true);
}
: undefined
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please explain why we have to do this in a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done! 👍

shouldUseAutoHitSlop
accessibilityLabel={translate('productTrainingTooltip.scanTestTooltip.noThanks')}
role={CONST.ROLE.BUTTON}
>
<Icon
src={Expensicons.Close}
fill={theme.icon}
width={variables.iconSizeSemiSmall}
height={variables.iconSizeSemiSmall}
/>
</PressableWithoutFeedback>
)}
</View>
{!!tooltip?.shouldRenderActionButtons && (
<View style={[styles.alignItemsCenter, styles.justifyContentBetween, styles.flexRow, styles.ph2, styles.pv2, styles.gap2]}>
Expand All @@ -258,42 +307,35 @@ const useProductTrainingContext = (tooltipName: ProductTrainingTooltipName, shou
</View>
);
}, [
config.onConfirm,
config.onDismiss,
tooltipName,
styles.alignItemsCenter,
styles.flex1,
styles.flexRow,
styles.justifyContentStart,
styles.justifyContentCenter,
styles.flexWrap,
styles.textAlignCenter,
styles.gap3,
styles.justifyContentBetween,
styles.justifyContentCenter,
styles.mw100,
styles.p2,
styles.productTrainingTooltipText,
styles.pv2,
styles.textAlignCenter,
styles.textBold,
styles.ph1,
styles.productTrainingTooltipText,
styles.textWrap,
styles.gap2,
styles.justifyContentStart,
styles.mw100,
styles.flex1,
styles.justifyContentBetween,
styles.ph2,
styles.gap2,
styles.textBold,
theme.tooltipHighlightText,
tooltipName,
theme.icon,
translate,
config.onConfirm,
config.onDismiss,
hideTooltip,
]);

const shouldShowProductTrainingTooltip = useMemo(() => {
return shouldShow && shouldRenderTooltip(tooltipName) && !shouldHideToolTip;
}, [shouldRenderTooltip, tooltipName, shouldShow, shouldHideToolTip]);

const hideProductTrainingTooltip = useCallback(() => {
if (!shouldShowProductTrainingTooltip) {
return;
}
const tooltip = TOOLTIPS[tooltipName];
tooltip.onHideTooltip();
unregisterTooltip(tooltipName);
}, [tooltipName, shouldShowProductTrainingTooltip, unregisterTooltip]);
hideTooltip(false);
}, [hideTooltip]);

return {
renderProductTrainingTooltip,
Expand Down
4 changes: 2 additions & 2 deletions src/hooks/useOnboardingFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {startOnboardingFlow} from '@libs/actions/Welcome/OnboardingFlow';
import Navigation from '@libs/Navigation/Navigation';
import {hasCompletedGuidedSetupFlowSelector, tryNewDotOnyxSelector} from '@libs/onboardingSelectors';
import {buildCannedSearchQuery} from '@libs/SearchQueryUtils';
import isProductTrainingElementDismissed from '@libs/TooltipUtils';
import CONFIG from '@src/CONFIG';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
Expand Down Expand Up @@ -45,8 +46,7 @@ function useOnboardingFlowRouter() {
if (CONFIG.IS_HYBRID_APP && isLoadingOnyxValue(isSingleNewDotEntryMetadata)) {
return;
}

if (hasBeenAddedToNudgeMigration && !dismissedProductTraining?.migratedUserWelcomeModal) {
if (hasBeenAddedToNudgeMigration && !isProductTrainingElementDismissed('migratedUserWelcomeModal', dismissedProductTraining)) {
const defaultCannedQuery = buildCannedSearchQuery();
const query = defaultCannedQuery;
Navigation.navigate(ROUTES.SEARCH_ROOT.getRoute({query}));
Expand Down
1 change: 1 addition & 0 deletions src/libs/API/parameters/DismissProductTraining.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
type DismissProductTrainingParams = {
name: string;
dismissedMethod: 'x' | 'click';
};

export default DismissProductTrainingParams;
8 changes: 8 additions & 0 deletions src/libs/TooltipUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type {OnyxEntry} from 'react-native-onyx';
import type {DismissedProductTraining} from '@src/types/onyx';

function isProductTrainingElementDismissed(elementName: keyof DismissedProductTraining, dismissedProductTraining: OnyxEntry<DismissedProductTraining>) {
return typeof dismissedProductTraining?.[elementName] === 'string' ? !!dismissedProductTraining?.[elementName] : !!dismissedProductTraining?.[elementName]?.timestamp;
}

export default isProductTrainingElementDismissed;
7 changes: 5 additions & 2 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4846,11 +4846,14 @@ function dismissChangePolicyModal() {
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING,
value: {
[CONST.CHANGE_POLICY_TRAINING_MODAL]: DateUtils.getDBTime(date.valueOf()),
[CONST.CHANGE_POLICY_TRAINING_MODAL]: {
timestamp: DateUtils.getDBTime(date.valueOf()),
dismissedMethod: 'click',
},
},
},
];
API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: CONST.CHANGE_POLICY_TRAINING_MODAL}, {optimisticData});
API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: CONST.CHANGE_POLICY_TRAINING_MODAL, dismissedMethod: 'click'}, {optimisticData});
}

/**
Expand Down
10 changes: 7 additions & 3 deletions src/libs/actions/Welcome/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,18 +207,22 @@ function setSelfTourViewed(shouldUpdateOnyxDataOnlyLocally = false) {
API.write(WRITE_COMMANDS.SELF_TOUR_VIEWED, null, {optimisticData});
}

function dismissProductTraining(elementName: string) {
function dismissProductTraining(elementName: string, isDismissedUsingCloseButton = false) {
const date = new Date();
const dismissedMethod = isDismissedUsingCloseButton ? 'x' : 'click';
const optimisticData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING,
value: {
[elementName]: DateUtils.getDBTime(date.valueOf()),
[elementName]: {
timestamp: DateUtils.getDBTime(date.valueOf()),
dismissedMethod,
},
},
},
];
API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: elementName}, {optimisticData});
API.write(WRITE_COMMANDS.DISMISS_PRODUCT_TRAINING, {name: elementName, dismissedMethod}, {optimisticData});
}

export {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ function IOURequestStepScan({
{
onConfirm: setTestReceiptAndNavigate,
onDismiss: () => {
dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP);
dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, true);
},
},
);
Expand Down
2 changes: 1 addition & 1 deletion src/pages/iou/request/step/IOURequestStepScan/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -590,7 +590,7 @@ function IOURequestStepScan({
{
onConfirm: setTestReceiptAndNavigate,
onDismiss: () => {
dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP);
dismissProductTraining(CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.SCAN_TEST_TOOLTIP, true);
},
},
);
Expand Down
Loading