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

New product training tooltips #54064

Merged
merged 28 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
6915152
Rename PRODUCT_TRAINING_TOOLTIP_DATA to TOOLTIPS and update references
ishpaul777 Dec 12, 2024
d4e601e
Add new tooltips for search filter, bottom navigation inbox, workspac…
ishpaul777 Dec 12, 2024
ef25dd7
update workspace chat tooltip
ishpaul777 Dec 12, 2024
b74ab71
Refactor FloatingActionButton to use useBottomTabIsFocused hook and a…
ishpaul777 Dec 12, 2024
cd4d450
Enhance bottom navigation inbox tooltip with additional content and i…
ishpaul777 Dec 12, 2024
c8b461d
fix typo
ishpaul777 Dec 12, 2024
07d8869
Add educational tooltips for search filter and update bottom navigati…
ishpaul777 Dec 12, 2024
a8c5fab
Remove unnecessary console log
ishpaul777 Dec 12, 2024
490bcf4
Remove beta
ishpaul777 Dec 12, 2024
2e99f8b
use variables
ishpaul777 Dec 12, 2024
ca2a374
Update src/components/LHNOptionsList/OptionRowLHN.tsx
ishpaul777 Dec 12, 2024
0c2c09d
Update src/components/ProductTrainingContext/index.tsx
ishpaul777 Dec 12, 2024
ed735a1
Update src/languages/en.ts
ishpaul777 Dec 12, 2024
b139934
Update src/languages/en.ts
ishpaul777 Dec 12, 2024
64e89d7
Refactor tooltip positioning and variables in FloatingActionButton an…
ishpaul777 Dec 12, 2024
c95b6e8
Remove unused responsive layout hook from FloatingActionButton component
ishpaul777 Dec 12, 2024
569e0cb
Refactor tooltip styles and translations for improved consistency acr…
ishpaul777 Dec 13, 2024
377189c
fix typo
ishpaul777 Dec 13, 2024
749e890
Refactor tooltip positioning and responsiveness in FloatingActionButt…
ishpaul777 Dec 13, 2024
3e6c815
Refactor tooltip positioning and visibility logic for improved respon…
ishpaul777 Dec 13, 2024
db57944
add comments and api changes
ishpaul777 Dec 13, 2024
b63ecf1
Merge branch 'main' into new-product-training-tooltips
ishpaul777 Dec 13, 2024
dc13937
Update src/languages/en.ts
ishpaul777 Dec 13, 2024
192125c
changes as per review
ishpaul777 Dec 13, 2024
4e959b9
Update Spanish translations for tooltips and messages
ishpaul777 Dec 13, 2024
aaaf230
Add border radius to product training tooltip wrapper
ishpaul777 Dec 13, 2024
e2645c7
Refactor tooltip content for improved clarity and emphasis in English…
ishpaul777 Dec 13, 2024
f22bbc2
format
ishpaul777 Dec 13, 2024
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
4 changes: 4 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6413,6 +6413,10 @@ const CONST = {
RENAME_SAVED_SEARCH: 'renameSavedSearch',
QUICK_ACTION_BUTTON: 'quickActionButton',
WORKSAPCE_CHAT_CREATE: 'workspaceChatCreate',
SEARCH_FILTER_BUTTON_TOOLTIP: 'filterButtonTooltip',
BOTTOM_NAV_INBOX_TOOLTIP: 'bottomNavInboxTooltip',
LHN_WORKSPACE_CHAT_TOOLTIP: 'workspaceChatLHNTooltip',
GLOBAL_CREATE_TOOLTIP: 'globalCreateTooltip',
},
} as const;

Expand Down
75 changes: 51 additions & 24 deletions src/components/FloatingActionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@ import type {GestureResponderEvent, Role, Text, View} from 'react-native';
import {Platform} from 'react-native';
import Animated, {createAnimatedPropAdapter, Easing, interpolateColor, processColor, useAnimatedProps, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import Svg, {Path} from 'react-native-svg';
import useBottomTabIsFocused from '@hooks/useBottomTabIsFocused';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getPlatform from '@libs/getPlatform';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import {PressableWithoutFeedback} from './Pressable';
import {useProductTrainingContext} from './ProductTrainingContext';
import EducationalTooltip from './Tooltip/EducationalTooltip';

const AnimatedPath = Animated.createAnimatedComponent(Path);
AnimatedPath.displayName = 'AnimatedPath';
Expand Down Expand Up @@ -56,6 +62,14 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
const styles = useThemeStyles();
const borderRadius = styles.floatingActionButton.borderRadius;
const fabPressable = useRef<HTMLDivElement | View | Text | null>(null);
const {shouldUseNarrowLayout} = useResponsiveLayout();
const platform = getPlatform();
const isNarrowScreenOnWeb = shouldUseNarrowLayout && platform === CONST.PLATFORM.WEB;
const isFocused = useBottomTabIsFocused();
const {renderProductTrainingTooltip, shouldShowProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.GLOBAL_CREATE_TOOLTIP,
isFocused,
);
const sharedValue = useSharedValue(isActive ? 1 : 0);
const buttonRef = ref;

Expand Down Expand Up @@ -97,32 +111,45 @@ function FloatingActionButton({onPress, isActive, accessibilityLabel, role}: Flo
};

return (
<PressableWithoutFeedback
ref={(el) => {
fabPressable.current = el ?? null;
if (buttonRef && 'current' in buttonRef) {
buttonRef.current = el ?? null;
}
<EducationalTooltip
shouldRender={shouldShowProductTrainingTooltip}
anchorAlignment={{
horizontal: isNarrowScreenOnWeb ? CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.CENTER : CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM,
}}
style={[styles.h100, styles.bottomTabBarItem]}
accessibilityLabel={accessibilityLabel}
onPress={toggleFabAction}
onLongPress={() => {}}
role={role}
shouldUseHapticsOnLongPress={false}
shouldUseOverlay
shiftHorizontal={isNarrowScreenOnWeb ? 0 : variables.fabTooltipShiftHorizontal}
renderTooltipContent={renderProductTrainingTooltip}
wrapperStyle={styles.productTrainingTooltipWrapper}
onHideTooltip={hideProductTrainingTooltip}
>
<Animated.View style={[styles.floatingActionButton, animatedStyle]}>
<Svg
width={variables.iconSizeNormal}
height={variables.iconSizeNormal}
>
<AnimatedPath
d="M12,3c0-1.1-0.9-2-2-2C8.9,1,8,1.9,8,3v5H3c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h5v5c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2v-5h5c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2h-5V3z"
animatedProps={animatedProps}
/>
</Svg>
</Animated.View>
</PressableWithoutFeedback>
<PressableWithoutFeedback
ref={(el) => {
fabPressable.current = el ?? null;
if (buttonRef && 'current' in buttonRef) {
buttonRef.current = el ?? null;
}
}}
style={[styles.h100, styles.bottomTabBarItem]}
accessibilityLabel={accessibilityLabel}
onPress={toggleFabAction}
onLongPress={() => {}}
role={role}
shouldUseHapticsOnLongPress={false}
>
<Animated.View style={[styles.floatingActionButton, animatedStyle]}>
<Svg
width={variables.iconSizeNormal}
height={variables.iconSizeNormal}
>
<AnimatedPath
d="M12,3c0-1.1-0.9-2-2-2C8.9,1,8,1.9,8,3v5H3c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2h5v5c0,1.1,0.9,2,2,2c1.1,0,2-0.9,2-2v-5h5c1.1,0,2-0.9,2-2c0-1.1-0.9-2-2-2h-5V3z"
animatedProps={animatedProps}
/>
</Svg>
</Animated.View>
</PressableWithoutFeedback>
</EducationalTooltip>
);
}

Expand Down
33 changes: 18 additions & 15 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useFocusEffect} from '@react-navigation/native';
import React, {useCallback, useRef, useState} from 'react';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import type {GestureResponderEvent, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import {useOnyx} from 'react-native-onyx';
Expand Down Expand Up @@ -47,19 +47,21 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${optionItem?.reportID || -1}`);

const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID);
const isActiveWorkspaceChat = ReportUtils.isPolicyExpenseChat(report) && report?.isOwnPolicyExpenseChat && activePolicyID === report?.policyID;
const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED);
const session = useSession();

// Guides are assigned for the MANAGE_TEAM onboarding action, except for emails that have a '+'.
const isOnboardingGuideAssigned = introSelected?.choice === CONST.ONBOARDING_CHOICES.MANAGE_TEAM && !session?.email?.includes('+');
const shouldShowToooltipOnThisReport = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report);
const shouldShowGetStartedTooltip = isOnboardingGuideAssigned ? ReportUtils.isAdminRoom(report) : ReportUtils.isConciergeChatReport(report);

const shouldShowGetStartedTooltip = shouldShowToooltipOnThisReport && isScreenFocused;
const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(
CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.CONCEIRGE_LHN_GBR,
shouldShowGetStartedTooltip,
);
const {tooltipToRender, shouldShowTooltip} = useMemo(() => {
const tooltip = shouldShowGetStartedTooltip ? CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.CONCEIRGE_LHN_GBR : CONST.PRODUCT_TRAINING_TOOLTIP_NAMES.LHN_WORKSPACE_CHAT_TOOLTIP;

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return {tooltipToRender: tooltip, shouldShowTooltip: shouldUseNarrowLayout ? isScreenFocused : true};
}, [shouldShowGetStartedTooltip, isScreenFocused, shouldUseNarrowLayout]);

const {shouldShowProductTrainingTooltip, renderProductTrainingTooltip, hideProductTrainingTooltip} = useProductTrainingContext(tooltipToRender, shouldShowTooltip);
const {translate} = useLocalize();
const [isContextMenuActive, setIsContextMenuActive] = useState(false);

Expand Down Expand Up @@ -156,17 +158,18 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
needsOffscreenAlphaCompositing
>
<EducationalTooltip
shouldRender={shouldShowProductTrainingTooltip}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
shouldRender={shouldShowProductTrainingTooltip && (isActiveWorkspaceChat || shouldShowGetStartedTooltip)}
renderTooltipContent={renderProductTrainingTooltip}
anchorAlignment={{
horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
horizontal: isActiveWorkspaceChat ? CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT : CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.RIGHT,
vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP,
}}
shouldUseOverlay
shiftHorizontal={variables.gbrTooltipShiftHorizontal}
shiftHorizontal={isActiveWorkspaceChat ? variables.workspaceLHNtooltipShiftHorizontal : variables.gbrTooltipShiftHorizontal}
shiftVertical={isActiveWorkspaceChat ? 0 : variables.composerTooltipShiftVertical}
onHideTooltip={hideProductTrainingTooltip}
shiftVertical={variables.composerTooltipShiftVertical}
wrapperStyle={styles.quickActionTooltipWrapper}
wrapperStyle={styles.productTrainingTooltipWrapper}
>
<View>
<Hoverable>
Expand Down

This file was deleted.

118 changes: 118 additions & 0 deletions src/components/ProductTrainingContext/TOOLTIPS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type {ValueOf} from 'type-fest';
import {dismissProductTraining} from '@libs/actions/Welcome';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';

const {
CONCEIRGE_LHN_GBR,
RENAME_SAVED_SEARCH,
WORKSAPCE_CHAT_CREATE,
QUICK_ACTION_BUTTON,
SEARCH_FILTER_BUTTON_TOOLTIP,
BOTTOM_NAV_INBOX_TOOLTIP,
LHN_WORKSPACE_CHAT_TOOLTIP,
GLOBAL_CREATE_TOOLTIP,
} = CONST.PRODUCT_TRAINING_TOOLTIP_NAMES;

type ProductTrainingTooltipName = ValueOf<typeof CONST.PRODUCT_TRAINING_TOOLTIP_NAMES>;

type ShouldShowConditionProps = {
shouldUseNarrowLayout?: boolean;
};

type TooltipData = {
content: Array<{text: TranslationPaths; isBold: boolean}>;
onHideTooltip: () => void;
name: ProductTrainingTooltipName;
priority: number;
shouldShow: (props: ShouldShowConditionProps) => boolean;
};

const TOOLTIPS: Record<ProductTrainingTooltipName, TooltipData> = {
[CONCEIRGE_LHN_GBR]: {
content: [
{text: 'productTrainingTooltip.conciergeLHNGBR.part1', isBold: false},
{text: 'productTrainingTooltip.conciergeLHNGBR.part2', isBold: true},
],
onHideTooltip: () => dismissProductTraining(CONCEIRGE_LHN_GBR),
name: CONCEIRGE_LHN_GBR,
priority: 1300,
shouldShow: ({shouldUseNarrowLayout}) => !!shouldUseNarrowLayout,
},
[RENAME_SAVED_SEARCH]: {
content: [
{text: 'productTrainingTooltip.saveSearchTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.saveSearchTooltip.part2', isBold: false},
],
onHideTooltip: () => dismissProductTraining(RENAME_SAVED_SEARCH),
name: RENAME_SAVED_SEARCH,
priority: 1250,
shouldShow: ({shouldUseNarrowLayout}) => !shouldUseNarrowLayout,
},
[GLOBAL_CREATE_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.globalCreateTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.globalCreateTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.globalCreateTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(GLOBAL_CREATE_TOOLTIP),
name: GLOBAL_CREATE_TOOLTIP,
priority: 1200,
shouldShow: () => true,
},
[QUICK_ACTION_BUTTON]: {
content: [
{text: 'productTrainingTooltip.quickActionButton.part1', isBold: true},
{text: 'productTrainingTooltip.quickActionButton.part2', isBold: false},
],
onHideTooltip: () => dismissProductTraining(QUICK_ACTION_BUTTON),
name: QUICK_ACTION_BUTTON,
priority: 1150,
shouldShow: () => true,
},
[WORKSAPCE_CHAT_CREATE]: {
content: [
{text: 'productTrainingTooltip.workspaceChatCreate.part1', isBold: true},
{text: 'productTrainingTooltip.workspaceChatCreate.part2', isBold: false},
],
onHideTooltip: () => dismissProductTraining(WORKSAPCE_CHAT_CREATE),
name: WORKSAPCE_CHAT_CREATE,
priority: 1100,
shouldShow: () => true,
},
[SEARCH_FILTER_BUTTON_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.searchFilterButtonTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.searchFilterButtonTooltip.part2', isBold: false},
],
onHideTooltip: () => dismissProductTraining(SEARCH_FILTER_BUTTON_TOOLTIP),
name: SEARCH_FILTER_BUTTON_TOOLTIP,
priority: 1000,
shouldShow: () => true,
},
[BOTTOM_NAV_INBOX_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.bottomNavInboxTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(BOTTOM_NAV_INBOX_TOOLTIP),
name: BOTTOM_NAV_INBOX_TOOLTIP,
priority: 900,
shouldShow: () => true,
},
[LHN_WORKSPACE_CHAT_TOOLTIP]: {
content: [
{text: 'productTrainingTooltip.workspaceChatTooltip.part1', isBold: true},
{text: 'productTrainingTooltip.workspaceChatTooltip.part2', isBold: false},
{text: 'productTrainingTooltip.workspaceChatTooltip.part3', isBold: false},
],
onHideTooltip: () => dismissProductTraining(LHN_WORKSPACE_CHAT_TOOLTIP),
name: LHN_WORKSPACE_CHAT_TOOLTIP,
priority: 800,
shouldShow: () => true,
},
};

export default TOOLTIPS;
export type {ProductTrainingTooltipName};
Loading
Loading