diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 9103648114bb..7a58d40ae4cd 100644 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -8192,6 +8192,8 @@ const CONST = { GPS_TOOLTIP: 'gpsTooltip', HAS_FILTER_NEGATION: 'hasFilterNegation', MILEAGE_RATE_AUTO_UPDATED: 'mileageRateAutoUpdated', + REQUIRE_FIELDS_RULE_RECEIPT_COUPLING_TOOLTIP: 'requireFieldsRuleReceiptCouplingTooltip', + REQUIRE_FIELDS_RULE_ITEMIZED_RECEIPT_COUPLING_TOOLTIP: 'requireFieldsRuleItemizedReceiptCouplingTooltip', }, CHANGE_POLICY_TRAINING_MODAL: 'changePolicyModal', AGENTS_RULES_BANNER: 'agentsRulesBanner', diff --git a/src/components/ProductTrainingContext/TOOLTIPS.ts b/src/components/ProductTrainingContext/TOOLTIPS.ts index 73d19b027ad0..05fca9e89df3 100644 --- a/src/components/ProductTrainingContext/TOOLTIPS.ts +++ b/src/components/ProductTrainingContext/TOOLTIPS.ts @@ -8,7 +8,12 @@ import type {ValueOf} from 'type-fest'; const {CONCIERGE_LHN_GBR, OUTSTANDING_FILTER, ACCOUNT_SWITCHER, SCAN_TEST_DRIVE_CONFIRMATION, GPS_TOOLTIP, HAS_FILTER_NEGATION, MILEAGE_RATE_AUTO_UPDATED} = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES; -type ProductTrainingTooltipName = Exclude, typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.MULTI_SCAN_EDUCATIONAL_MODAL>; +type ProductTrainingTooltipName = Exclude< + ValueOf, + | typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.MULTI_SCAN_EDUCATIONAL_MODAL + | typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.REQUIRE_FIELDS_RULE_RECEIPT_COUPLING_TOOLTIP + | typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.REQUIRE_FIELDS_RULE_ITEMIZED_RECEIPT_COUPLING_TOOLTIP +>; type ShouldShowConditionProps = { shouldUseNarrowLayout: boolean; diff --git a/src/components/RequireFieldsRules/FieldRequirementSettingRow.tsx b/src/components/RequireFieldsRules/FieldRequirementSettingRow.tsx index e36f68b87c23..bd5f8e258eb2 100644 --- a/src/components/RequireFieldsRules/FieldRequirementSettingRow.tsx +++ b/src/components/RequireFieldsRules/FieldRequirementSettingRow.tsx @@ -5,19 +5,29 @@ import EducationalTooltip from '@components/Tooltip/EducationalTooltip'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; +import useOnyx from '@hooks/useOnyx'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getRequireFieldsFieldCouplingTooltipKey, isRequireFieldsFieldCouplingDisabled} from '@libs/RequireFieldsRulesUtils'; +import {dismissProductTraining} from '@libs/actions/Welcome'; +import { + canClearRequireFieldsField, + getRequireFieldsFieldCouplingTooltipKey, + isRequireFieldsFieldCouplingDisabled, + REQUIRE_FIELDS_COUPLING_TOOLTIP_NAMES, +} from '@libs/RequireFieldsRulesUtils'; import type {FieldRequirementsDirection} from '@libs/RequireFieldsRulesUtils'; +import isProductTrainingElementDismissed from '@libs/TooltipUtils'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import type {RequireFieldsRuleForm, RequireFieldsRuleSettingFieldKey} from '@src/types/form/RequireFieldsRuleForm'; import type {PolicyCategory} from '@src/types/onyx'; +import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue'; -import React, {useCallback, useState} from 'react'; +import React, {useCallback} from 'react'; import {View} from 'react-native'; import FieldRequirementsDirectionToggle from './FieldRequirementsDirectionToggle'; @@ -54,16 +64,29 @@ function FieldRequirementSettingRow({ const theme = useTheme(); const {translate} = useLocalize(); const expensifyIcons = useMemoizedLazyExpensifyIcons(['Close', 'Lightbulb']); - const [dismissedCouplingTooltipKey, setDismissedCouplingTooltipKey] = useState(); + const [dismissedProductTraining, dismissedProductTrainingMetadata] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING); const isCouplingDisabled = isRequireFieldsFieldCouplingDisabled(fieldKey, effectiveForm, category, touchedFields, isEditing, clearedFields); const couplingTooltipKey = getRequireFieldsFieldCouplingTooltipKey(fieldKey, effectiveForm, category, touchedFields, isEditing, clearedFields, couplingInteractionFields); const couplingTooltip = couplingTooltipKey ? translate(`workspace.rules.requireFieldsRule.${couplingTooltipKey}`) : undefined; - const shouldDisplayCouplingTooltip = !!couplingTooltip && dismissedCouplingTooltipKey !== couplingTooltipKey; - - const hideCouplingTooltip = useCallback(() => { - setDismissedCouplingTooltipKey(couplingTooltipKey); - }, [couplingTooltipKey]); + const couplingTooltipName = couplingTooltipKey ? REQUIRE_FIELDS_COUPLING_TOOLTIP_NAMES[couplingTooltipKey] : undefined; + // Wait for the NVP so an already-dismissed tooltip doesn't flash before the dismissal arrives. + const shouldDisplayCouplingTooltip = + !!couplingTooltip && + !!couplingTooltipName && + !isLoadingOnyxValue(dismissedProductTrainingMetadata) && + !isProductTrainingElementDismissed(couplingTooltipName, dismissedProductTraining); + + const hideCouplingTooltip = useCallback( + (isDismissedUsingCloseButton = false) => { + if (!couplingTooltipName) { + return; + } + + dismissProductTraining(couplingTooltipName, isDismissedUsingCloseButton); + }, + [couplingTooltipName], + ); const renderCouplingTooltipContent = useCallback(() => { return ( @@ -83,7 +106,7 @@ function FieldRequirementSettingRow({ shouldUseAutoHitSlop accessibilityLabel={translate('common.noThanks')} role={CONST.ROLE.BUTTON} - onPress={hideCouplingTooltip} + onPress={() => hideCouplingTooltip(true)} > @@ -146,7 +170,7 @@ function FieldRequirementSettingRow({ anchorAlignment={{horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM}} shiftHorizontal={variables.mileageRateTooltipShiftHorizontal} shiftVertical={variables.mileageRateTooltipShiftVertical} - onTooltipPress={hideCouplingTooltip} + onTooltipPress={() => hideCouplingTooltip()} shouldHideOnScroll > {rowContent} diff --git a/src/components/RequireFieldsRules/FieldRequirementsDirectionToggle.tsx b/src/components/RequireFieldsRules/FieldRequirementsDirectionToggle.tsx index aba4b6b78dd9..e1b55722012d 100644 --- a/src/components/RequireFieldsRules/FieldRequirementsDirectionToggle.tsx +++ b/src/components/RequireFieldsRules/FieldRequirementsDirectionToggle.tsx @@ -13,20 +13,36 @@ import {View} from 'react-native'; type FieldRequirementsDirectionToggleProps = { direction?: FieldRequirementsDirection; disabled?: boolean; + + /** When false, pressing the selected direction does nothing instead of clearing it. */ + canDeselect?: boolean; onSelect: (direction: FieldRequirementsDirection | undefined) => void; }; -function FieldRequirementsDirectionToggle({direction, disabled = false, onSelect}: FieldRequirementsDirectionToggleProps) { +function FieldRequirementsDirectionToggle({direction, disabled = false, canDeselect = true, onSelect}: FieldRequirementsDirectionToggleProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const isRequireSelected = direction === CONST.FIELD_REQUIREMENTS_DIRECTION.REQUIRE; const isWaiveDirectionSelected = direction === CONST.FIELD_REQUIREMENTS_DIRECTION.DO_NOT_REQUIRE; + const selectDirection = (isSelected: boolean, nextDirection: FieldRequirementsDirection) => { + if (!isSelected) { + onSelect(nextDirection); + return; + } + + if (!canDeselect) { + return; + } + + onSelect(undefined); + }; + return (