Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
195 changes: 195 additions & 0 deletions src/components/SelectionList/ListItem/UserListItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
import {Str} from 'expensify-common';
import React, {useCallback} from 'react';
import {View} from 'react-native';
import Icon from '@components/Icon';
import * as Expensicons from '@components/Icon/Expensicons';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import ReportActionAvatars from '@components/ReportActionAvatars';
import Text from '@components/Text';
import TextWithTooltip from '@components/TextWithTooltip';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import useStyleUtils from '@hooks/useStyleUtils';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import getButtonState from '@libs/getButtonState';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import BaseListItem from './BaseListItem';
import type {ListItem, UserListItemProps} from './types';

function UserListItem<TItem extends ListItem>({
item,
isFocused,
showTooltip,
isDisabled,
canSelectMultiple,
onSelectRow,
onCheckboxPress,
onDismissError,
shouldPreventEnterKeySubmit,
rightHandSideComponent,
onFocus,
shouldSyncFocus,
wrapperStyle,
pressableStyle,
shouldUseDefaultRightHandSideCheckmark,
forwardedFSClass,
}: UserListItemProps<TItem>) {
const styles = useThemeStyles();
const theme = useTheme();
const StyleUtils = useStyleUtils();
const {translate} = useLocalize();

const focusedBackgroundColor = styles.sidebarLinkActive.backgroundColor;
const subscriptAvatarBorderColor = isFocused ? focusedBackgroundColor : theme.sidebar;
const hoveredBackgroundColor = !!styles.sidebarLinkHover && 'backgroundColor' in styles.sidebarLinkHover ? styles.sidebarLinkHover.backgroundColor : theme.sidebar;

const handleCheckboxPress = useCallback(() => {
if (onCheckboxPress) {
onCheckboxPress(item);
} else {
onSelectRow(item);
}
}, [item, onCheckboxPress, onSelectRow]);

// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const [isReportInOnyx] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`, {
canBeMissing: true,
selector: (report) => !!report,
});

const reportExists = isReportInOnyx && !!item.reportID;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const itemAccountID = Number(item.accountID || item.icons?.at(1)?.id) || 0;

const isThereOnlyWorkspaceIcon = item.icons?.length === 1 && item.icons?.at(0)?.type === CONST.ICON_TYPE_WORKSPACE;
const shouldUseIconPolicyID = !item.reportID && !item.accountID && !item.policyID;
const policyID = isThereOnlyWorkspaceIcon && shouldUseIconPolicyID ? String(item.icons?.at(0)?.id) : item.policyID;

return (
<BaseListItem
item={item}
wrapperStyle={[styles.flex1, styles.justifyContentBetween, styles.sidebarLinkInner, styles.userSelectNone, styles.peopleRow, wrapperStyle]}
isFocused={isFocused}
isDisabled={isDisabled}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
shouldPreventEnterKeySubmit={shouldPreventEnterKeySubmit}
rightHandSideComponent={rightHandSideComponent}
errors={item.errors}
pendingAction={item.pendingAction}
pressableStyle={pressableStyle}
FooterComponent={
item.invitedSecondaryLogin ? (
<Text style={[styles.ml9, styles.ph5, styles.pb3, styles.textLabelSupporting]}>
{translate('workspace.people.invitedBySecondaryLogin', {secondaryLogin: item.invitedSecondaryLogin})}
</Text>
) : undefined
}
keyForList={item.keyForList}
onFocus={onFocus}
shouldSyncFocus={shouldSyncFocus}
>
{(hovered?: boolean) => (
<>
{!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && (
<PressableWithFeedback
accessibilityLabel={item.text ?? ''}
role={CONST.ROLE.BUTTON}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
disabled={isDisabled || item.isDisabledCheckbox}
onPress={handleCheckboxPress}
style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled, styles.mr3]}
>
<View style={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]}>
{!!item.isSelected && (
<Icon
src={Expensicons.Checkmark}
fill={theme.textLight}
height={14}
width={14}
/>
)}
</View>
</PressableWithFeedback>
)}
{(!!reportExists || !!itemAccountID || !!policyID) && (
<ReportActionAvatars
subscriptAvatarBorderColor={hovered && !isFocused ? hoveredBackgroundColor : subscriptAvatarBorderColor}
shouldShowTooltip={showTooltip}
secondaryAvatarContainerStyle={[
StyleUtils.getBackgroundAndBorderStyle(theme.sidebar),
isFocused ? StyleUtils.getBackgroundAndBorderStyle(focusedBackgroundColor) : undefined,
hovered && !isFocused ? StyleUtils.getBackgroundAndBorderStyle(hoveredBackgroundColor) : undefined,
]}
reportID={reportExists ? item.reportID : undefined}
/* eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing */
accountIDs={!reportExists && !!itemAccountID ? [itemAccountID] : []}
policyID={!reportExists && !!policyID ? policyID : undefined}
singleAvatarContainerStyle={[styles.actionAvatar, styles.mr3]}
fallbackDisplayName={item.text ?? item.alternateText ?? undefined}
/>
)}
<View style={[styles.flex1, styles.flexColumn, styles.justifyContentCenter, styles.alignItemsStretch, styles.optionRow]}>
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={Str.removeSMSDomain(item.text ?? '')}
style={[
styles.optionDisplayName,
isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText,
item.isBold !== false && styles.sidebarLinkTextBold,
styles.pre,
item.alternateText ? styles.mb1 : null,
]}
/>
{!!item.alternateText && (
<TextWithTooltip
shouldShowTooltip={showTooltip}
text={Str.removeSMSDomain(item.alternateText ?? '')}
style={[styles.textLabelSupporting, styles.lh16, styles.pre]}
forwardedFSClass={forwardedFSClass}
/>
)}
</View>
{!!item.rightElement && item.rightElement}
{!!item.shouldShowRightIcon && (
<View style={[styles.popoverMenuIcon, styles.pointerEventsAuto, isDisabled && styles.cursorDisabled]}>
<Icon
src={Expensicons.ArrowRight}
fill={StyleUtils.getIconFillColor(getButtonState(hovered, false, false, !!isDisabled, item.isInteractive !== false))}
/>
</View>
)}
{!!shouldUseDefaultRightHandSideCheckmark && !!canSelectMultiple && (
<PressableWithFeedback
accessibilityLabel={item.text ?? ''}
role={CONST.ROLE.BUTTON}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
disabled={isDisabled || item.isDisabledCheckbox}
onPress={handleCheckboxPress}
style={[styles.cursorUnset, StyleUtils.getCheckboxPressableStyle(), item.isDisabledCheckbox && styles.cursorDisabled, styles.ml3]}
>
<View style={[StyleUtils.getCheckboxContainerStyle(20), StyleUtils.getMultiselectListStyles(!!item.isSelected, !!item.isDisabled)]}>
{!!item.isSelected && (
<Icon
src={Expensicons.Checkmark}
fill={theme.textLight}
height={14}
width={14}
/>
)}
</View>
</PressableWithFeedback>
)}
</>
)}
</BaseListItem>
);
}

UserListItem.displayName = 'UserListItem';

export default UserListItem;
4 changes: 4 additions & 0 deletions src/components/SelectionList/ListItem/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type {ReactElement, ReactNode} from 'react';
import type {AccessibilityState, NativeSyntheticEvent, StyleProp, TargetedEvent, TextStyle, ViewStyle} from 'react-native';
import type {AnimatedStyle} from 'react-native-reanimated';
import type {ForwardedFSClassProps} from '@libs/Fullstory/types';
import type {BrickRoad} from '@libs/WorkspacesSettingsUtils';
// eslint-disable-next-line no-restricted-imports
import type CursorStyles from '@styles/utils/cursor/types';
Expand Down Expand Up @@ -256,6 +257,8 @@ type SingleSelectListItemProps<TItem extends ListItem> = ListItemProps<TItem>;

type MultiSelectListItemProps<TItem extends ListItem> = ListItemProps<TItem>;

type UserListItemProps<TItem extends ListItem> = ListItemProps<TItem> & ForwardedFSClassProps;

export type {
BaseListItemProps,
ExtendedTargetedEvent,
Expand All @@ -266,4 +269,5 @@ export type {
ValidListItem,
SingleSelectListItemProps,
MultiSelectListItemProps,
UserListItemProps,
};
10 changes: 5 additions & 5 deletions src/pages/iou/request/step/IOURequestStepSendFrom.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {emailSelector} from '@selectors/Session';
import React, {useMemo} from 'react';
import * as Expensicons from '@components/Icon/Expensicons';
import SelectionList from '@components/SelectionListWithSections';
import type {ListItem} from '@components/SelectionListWithSections/types';
import UserListItem from '@components/SelectionListWithSections/UserListItem';
import SelectionList from '@components/SelectionList';
import UserListItem from '@components/SelectionList/ListItem/UserListItem';
import type {ListItem} from '@components/SelectionList/types';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
import Navigation from '@libs/Navigation/Navigation';
Expand Down Expand Up @@ -89,11 +89,11 @@ function IOURequestStepSendFrom({route, transaction}: IOURequestStepSendFromProp
includeSafeAreaPaddingBottom
>
<SelectionList
sections={[{data: workspaceOptions, title: translate('common.workspaces')}]}
data={workspaceOptions}
onSelectRow={selectWorkspace}
shouldSingleExecuteRowSelect
ListItem={UserListItem}
initiallyFocusedOptionKey={selectedWorkspace?.policyID}
initiallyFocusedItemKey={selectedWorkspace?.policyID}
/>
</StepScreenWrapper>
);
Expand Down
Loading