From 0b23af020821ae2e419974cb80942ac1819aec9d Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Tue, 19 Nov 2024 15:59:02 +0800 Subject: [PATCH 1/4] fix new ws member isn't highlighted --- .../SelectionList/BaseSelectionList.tsx | 22 ++++++++++++++----- .../SelectionList/TableListItem.tsx | 20 ++++++++++++++++- src/components/SelectionList/types.ts | 2 +- src/pages/workspace/WorkspaceInvitePage.tsx | 8 +++++-- src/pages/workspace/WorkspaceMembersPage.tsx | 2 +- 5 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index b7bef18896d1..8ca40e14b6bd 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -487,7 +487,8 @@ function BaseSelectionList( const renderItem = ({item, index, section}: SectionListRenderItemInfo>) => { const normalizedIndex = index + (section?.indexOffset ?? 0); const isDisabled = !!section.isDisabled || item.isDisabled; - const isItemFocused = (!isDisabled || item.isSelected) && (focusedIndex === normalizedIndex || itemsToHighlight?.has(item.keyForList ?? '')); + const isItemFocused = (!isDisabled || item.isSelected) && focusedIndex === normalizedIndex; + const isItemHighlighted = !!itemsToHighlight?.has(item.keyForList ?? ''); // We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade. const showTooltip = shouldShowTooltips && normalizedIndex < 10; @@ -495,7 +496,10 @@ function BaseSelectionList( onItemLayout(event, item?.keyForList)}> ( * @param timeout - The timeout in milliseconds before removing the highlight. */ const scrollAndHighlightItem = useCallback( - (items: string[], timeout: number) => { + (items: string[]) => { const newItemsToHighlight = new Set(); items.forEach((item) => { newItemsToHighlight.add(item); }); const index = flattenedSections.allOptions.findIndex((option) => newItemsToHighlight.has(option.keyForList ?? '')); - updateAndScrollToFocusedIndex(index); + scrollToIndex(index); setItemsToHighlight(newItemsToHighlight); if (itemFocusTimeoutRef.current) { clearTimeout(itemFocusTimeoutRef.current); } + const duration = + CONST.ANIMATED_HIGHLIGHT_ENTRY_DELAY + + CONST.ANIMATED_HIGHLIGHT_ENTRY_DURATION + + CONST.ANIMATED_HIGHLIGHT_START_DELAY + + CONST.ANIMATED_HIGHLIGHT_START_DURATION + + CONST.ANIMATED_HIGHLIGHT_END_DELAY + + CONST.ANIMATED_HIGHLIGHT_END_DURATION; itemFocusTimeoutRef.current = setTimeout(() => { - setFocusedIndex(-1); setItemsToHighlight(null); - }, timeout); + }, duration); }, [flattenedSections.allOptions, setFocusedIndex, updateAndScrollToFocusedIndex], ); diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index 0900e49f43ce..8b27ee8a20f8 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -5,6 +5,7 @@ import * as Expensicons from '@components/Icon/Expensicons'; import MultipleAvatars from '@components/MultipleAvatars'; import PressableWithFeedback from '@components/Pressable/PressableWithFeedback'; import TextWithTooltip from '@components/TextWithTooltip'; +import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle'; import useStyleUtils from '@hooks/useStyleUtils'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -30,6 +31,13 @@ function TableListItem({ const theme = useTheme(); const StyleUtils = useStyleUtils(); + const animatedHighlightStyle = useAnimatedHighlightStyle({ + borderRadius: styles.selectionListPressableItemWrapper.borderRadius, + shouldHighlight: !!item.shouldAnimateInHighlight, + highlightColor: theme.messageHighlightBG, + backgroundColor: theme.highlightBG, + }); + const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor; const hoveredBackgroundColor = styles.sidebarLinkHover?.backgroundColor ? styles.sidebarLinkHover.backgroundColor : theme.sidebar; @@ -44,7 +52,17 @@ function TableListItem({ return ( = Partial & { } & TRightHandSideComponent; type SelectionListHandle = { - scrollAndHighlightItem?: (items: string[], timeout: number) => void; + scrollAndHighlightItem?: (items: string[]) => void; clearInputAfterSelect?: () => void; scrollToIndex: (index: number, animated?: boolean) => void; updateAndScrollToFocusedIndex: (newFocusedIndex: number) => void; diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index a259fc7b9ce1..4c0b12dbb6ef 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -1,4 +1,4 @@ -import {useNavigation} from '@react-navigation/native'; +import {useIsFocused, useNavigation} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {SectionListData} from 'react-native'; @@ -49,6 +49,7 @@ function WorkspaceInvitePage({route, policy}: WorkspaceInvitePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); const navigation = useNavigation(); + const isFocused = useIsFocused(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const [selectedOptions, setSelectedOptions] = useState([]); const [personalDetails, setPersonalDetails] = useState([]); @@ -68,12 +69,15 @@ function WorkspaceInvitePage({route, policy}: WorkspaceInvitePageProps) { }); useEffect(() => { + if (!isFocused) { + return; + } const unsubscribe = navigation.addListener('beforeRemove', () => { Member.setWorkspaceInviteMembersDraft(route.params.policyID, {}); }); return unsubscribe; - }, [navigation, route.params.policyID]); + }, [isFocused, navigation, route.params.policyID]); useEffect(() => { Policy.clearErrors(route.params.policyID); diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index 96b6d31e5a2e..f7d56cf56c7e 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -415,7 +415,7 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson return; } const invitedEmails = Object.values(invitedEmailsToAccountIDsDraft).map(String); - selectionListRef.current?.scrollAndHighlightItem?.(invitedEmails, 1500); + selectionListRef.current?.scrollAndHighlightItem?.(invitedEmails); Member.setWorkspaceInviteMembersDraft(route.params.policyID, {}); }, [invitedEmailsToAccountIDsDraft, route.params.policyID, isFocused, accountIDs, prevAccountIDs]); From ea25e263f375a190d5715b13e1eae6c611d43305 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 20 Nov 2024 00:54:15 +0800 Subject: [PATCH 2/4] lint --- src/components/SelectionList/BaseSelectionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index 8ca40e14b6bd..dcf1b8975da5 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -711,7 +711,7 @@ function BaseSelectionList( setItemsToHighlight(null); }, duration); }, - [flattenedSections.allOptions, setFocusedIndex, updateAndScrollToFocusedIndex], + [flattenedSections.allOptions, setFocusedIndex, scrollToIndex], ); /** From 924cf3340ce7e107d665ec1b927d5ad8c6e0b9b8 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Wed, 20 Nov 2024 00:57:14 +0800 Subject: [PATCH 3/4] another lint --- src/components/SelectionList/BaseSelectionList.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SelectionList/BaseSelectionList.tsx b/src/components/SelectionList/BaseSelectionList.tsx index dcf1b8975da5..cb4acce3384d 100644 --- a/src/components/SelectionList/BaseSelectionList.tsx +++ b/src/components/SelectionList/BaseSelectionList.tsx @@ -711,7 +711,7 @@ function BaseSelectionList( setItemsToHighlight(null); }, duration); }, - [flattenedSections.allOptions, setFocusedIndex, scrollToIndex], + [flattenedSections.allOptions, scrollToIndex], ); /** From e99ede55a720b8bbb749912a01ade6f8ebe0cc77 Mon Sep 17 00:00:00 2001 From: Bernhard Owen Josephus Date: Thu, 21 Nov 2024 13:57:02 +0800 Subject: [PATCH 4/4] clears the draft when opening the invite page --- src/pages/workspace/WorkspaceInvitePage.tsx | 16 ---------------- src/pages/workspace/WorkspaceMembersPage.tsx | 11 ++++++++--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/src/pages/workspace/WorkspaceInvitePage.tsx b/src/pages/workspace/WorkspaceInvitePage.tsx index 0bac0381c447..7fef254d3ca9 100644 --- a/src/pages/workspace/WorkspaceInvitePage.tsx +++ b/src/pages/workspace/WorkspaceInvitePage.tsx @@ -1,4 +1,3 @@ -import {useIsFocused, useNavigation} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {SectionListData} from 'react-native'; @@ -16,7 +15,6 @@ import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useThemeStyles from '@hooks/useThemeStyles'; -import * as FormActions from '@libs/actions/FormActions'; import * as ReportActions from '@libs/actions/Report'; import {READ_COMMANDS} from '@libs/API/types'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -49,8 +47,6 @@ type WorkspaceInvitePageProps = WithPolicyAndFullscreenLoadingProps & WithNaviga function WorkspaceInvitePage({route, policy}: WorkspaceInvitePageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const navigation = useNavigation(); - const isFocused = useIsFocused(); const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); const [selectedOptions, setSelectedOptions] = useState([]); const [personalDetails, setPersonalDetails] = useState([]); @@ -69,18 +65,6 @@ function WorkspaceInvitePage({route, policy}: WorkspaceInvitePageProps) { shouldInitialize: didScreenTransitionEnd, }); - useEffect(() => { - if (!isFocused) { - return; - } - const unsubscribe = navigation.addListener('beforeRemove', () => { - Member.setWorkspaceInviteMembersDraft(route.params.policyID, {}); - FormActions.clearDraftValues(ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM); - }); - - return unsubscribe; - }, [isFocused, navigation, route.params.policyID]); - useEffect(() => { Policy.clearErrors(route.params.policyID); openWorkspaceInvitePage(); diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index ecab37df8344..6034e7d493cd 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -195,10 +195,16 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson getWorkspaceMembers(); }, [isOffline, prevIsOffline, getWorkspaceMembers]); + const clearInviteDraft = useCallback(() => { + Member.setWorkspaceInviteMembersDraft(route.params.policyID, {}); + FormActions.clearDraftValues(ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM); + }, [route.params.policyID]); + /** * Open the modal to invite a user */ const inviteUser = () => { + clearInviteDraft(); Navigation.navigate(ROUTES.WORKSPACE_INVITE.getRoute(route.params.policyID)); }; @@ -417,9 +423,8 @@ function WorkspaceMembersPage({personalDetails, route, policy, currentUserPerson } const invitedEmails = Object.values(invitedEmailsToAccountIDsDraft).map(String); selectionListRef.current?.scrollAndHighlightItem?.(invitedEmails); - Member.setWorkspaceInviteMembersDraft(route.params.policyID, {}); - FormActions.clearDraftValues(ONYXKEYS.FORMS.WORKSPACE_INVITE_MESSAGE_FORM); - }, [invitedEmailsToAccountIDsDraft, route.params.policyID, isFocused, accountIDs, prevAccountIDs]); + clearInviteDraft(); + }, [invitedEmailsToAccountIDsDraft, isFocused, accountIDs, prevAccountIDs, clearInviteDraft]); const getHeaderMessage = () => { if (isOfflineAndNoMemberDataAvailable) {