Skip to content

Commit d0be423

Browse files
committed
refactor of rooms list
1 parent e3c5d40 commit d0be423

3 files changed

Lines changed: 79 additions & 179 deletions

File tree

src/components/SelectionList/ListItem/TableListItem.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import type {ListItem, TableListItemProps} from './types';
1414
/**
1515
* A pressable row styled as a table entry with animated highlight, optional avatar, and
1616
* right caret. Used in workspace management lists (e.g. members, categories, tags, taxes).
17+
* The avatar is sourced from `item.accountID` (single-user) or `item.reportID` (chat/room).
1718
* Renders a left-side checkbox when canSelectMultiple is true (multi-select mode) and a
1819
* plain row with no selection button otherwise.
1920
*/
@@ -52,9 +53,10 @@ function TableListItem<TItem extends ListItem>({
5253

5354
const rowContent = (hovered: boolean) => (
5455
<>
55-
{!!item.accountID && (
56+
{(!!item.accountID || !!item.reportID) && (
5657
<ReportActionAvatars
57-
accountIDs={[item.accountID]}
58+
accountIDs={item.accountID ? [item.accountID] : undefined}
59+
reportID={item.reportID}
5860
fallbackDisplayName={item.text ?? item.alternateText ?? undefined}
5961
shouldShowTooltip={showTooltip}
6062
secondaryAvatarContainerStyle={[

src/pages/workspace/rooms/WorkspaceRoomsListItem.tsx

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/pages/workspace/rooms/WorkspaceRoomsPage.tsx

Lines changed: 75 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import {useFocusEffect} from '@react-navigation/native';
22
import {policyChatRoomsSelector} from '@selectors/Report';
3-
import {FlashList} from '@shopify/flash-list';
4-
import type {ListRenderItemInfo} from '@shopify/flash-list';
53
import React from 'react';
64
import {View} from 'react-native';
5+
import Avatar from '@components/Avatar';
76
import Button from '@components/Button';
87
import HeaderWithBackButton from '@components/HeaderWithBackButton';
98
import Icon from '@components/Icon';
109
import {usePersonalDetails} from '@components/OnyxListItemProvider';
1110
import ScreenWrapper from '@components/ScreenWrapper';
11+
import SelectionList from '@components/SelectionList';
12+
import TableListItem from '@components/SelectionList/ListItem/TableListItem';
13+
import type {ListItem} from '@components/SelectionList/types';
1214
import Text from '@components/Text';
1315
import useArchivedReportsIdSet from '@hooks/useArchivedReportsIdSet';
1416
import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
@@ -18,6 +20,7 @@ import usePermissions from '@hooks/usePermissions';
1820
import usePolicy from '@hooks/usePolicy';
1921
import useReportAttributes from '@hooks/useReportAttributes';
2022
import useResponsiveLayout from '@hooks/useResponsiveLayout';
23+
import useStyleUtils from '@hooks/useStyleUtils';
2124
import useTheme from '@hooks/useTheme';
2225
import useThemeStyles from '@hooks/useThemeStyles';
2326
import useWorkspaceDocumentTitle from '@hooks/useWorkspaceDocumentTitle';
@@ -34,70 +37,21 @@ import CONST from '@src/CONST';
3437
import ONYXKEYS from '@src/ONYXKEYS';
3538
import ROUTES from '@src/ROUTES';
3639
import type SCREENS from '@src/SCREENS';
37-
import type {PersonalDetails, Report} from '@src/types/onyx';
38-
import WorkspaceRoomsListItem from './WorkspaceRoomsListItem';
3940

4041
type WorkspaceRoomsPageProps = PlatformStackScreenProps<WorkspaceSplitNavigatorParamList, typeof SCREENS.WORKSPACE.ROOMS>;
4142

42-
/**
43-
* Row data for a single workspace room rendered in the list.
44-
*/
45-
type WorkspaceRoomsRow = {
46-
/** The underlying report representing the room. */
47-
report: Report;
48-
49-
/** Resolved display name of the room. */
50-
roomName: string;
51-
52-
/** Personal details of the room owner, if available. */
53-
ownerDetails: PersonalDetails | undefined;
54-
55-
/** Display name for the room owner, or empty string when missing. */
56-
ownerDisplayName: string;
57-
58-
/** Number of members shown next to the room. */
59-
memberCount: number;
43+
type RoomListItem = ListItem & {
44+
reportID: string;
6045
};
6146

62-
function RowSeparator() {
63-
return <View style={{height: 8}} />;
64-
}
65-
66-
function ListHeader() {
67-
const {translate} = useLocalize();
68-
const styles = useThemeStyles();
69-
const theme = useTheme();
70-
const icons = useMemoizedLazyExpensifyIcons(['Profile']);
71-
72-
return (
73-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.p4, styles.gap3]}>
74-
<View style={[styles.flexRow, styles.alignItemsCenter, styles.gap3, styles.flex3]}>
75-
<View style={[styles.alignItemsCenter, styles.justifyContentCenter, {width: variables.avatarSizeNormal}]}>
76-
<Icon
77-
src={icons.Profile}
78-
width={variables.iconSizeSmall}
79-
height={variables.iconSizeSmall}
80-
fill={theme.icon}
81-
/>
82-
</View>
83-
<Text style={styles.textLabelSupporting}>{translate('common.name')}</Text>
84-
</View>
85-
<View style={styles.flex2}>
86-
<Text style={styles.textLabelSupporting}>{translate('common.createdBy')}</Text>
87-
</View>
88-
<View style={styles.flex1}>
89-
<Text style={styles.textLabelSupporting}>{translate('common.members')}</Text>
90-
</View>
91-
</View>
92-
);
93-
}
94-
9547
function WorkspaceRoomsPage({route}: WorkspaceRoomsPageProps) {
9648
const {translate, localeCompare} = useLocalize();
9749
const styles = useThemeStyles();
50+
const StyleUtils = useStyleUtils();
51+
const theme = useTheme();
9852
const {shouldUseNarrowLayout} = useResponsiveLayout();
9953
const {isBetaEnabled} = usePermissions();
100-
const icons = useMemoizedLazyExpensifyIcons(['Plus']);
54+
const headerIcons = useMemoizedLazyExpensifyIcons(['Plus', 'Profile']);
10155
const illustrations = useMemoizedLazyIllustrations(['Hashtag']);
10256
const policyID = route.params.policyID;
10357
const policy = usePolicy(policyID);
@@ -115,24 +69,73 @@ function WorkspaceRoomsPage({route}: WorkspaceRoomsPageProps) {
11569
[policyID],
11670
);
11771

118-
const rows: WorkspaceRoomsRow[] = (policyReports ?? [])
72+
const data: RoomListItem[] = (policyReports ?? [])
11973
.filter((report) => !archivedReportsIdSet.has(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report.reportID}`) && !isHiddenForCurrentUser(report))
12074
.map((report) => {
12175
const ownerDetails = report.ownerAccountID ? (personalDetails?.[report.ownerAccountID] ?? undefined) : undefined;
76+
const ownerDisplayName = ownerDetails ? getDisplayNameOrDefault(ownerDetails) : '';
77+
const roomName = getReportName(report, reportAttributes);
78+
const memberCount = getParticipantsAccountIDsForDisplay(report, true, false, false, undefined, personalDetails).length;
12279
return {
123-
report,
124-
roomName: getReportName(report, reportAttributes),
125-
ownerDetails,
126-
ownerDisplayName: ownerDetails ? getDisplayNameOrDefault(ownerDetails) : '',
127-
memberCount: getParticipantsAccountIDsForDisplay(report, true, false, false, undefined, personalDetails).length,
80+
keyForList: report.reportID,
81+
text: roomName,
82+
reportID: report.reportID,
83+
rightElement: (
84+
<>
85+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.gap2, styles.flex2]}>
86+
{!!ownerDetails && (
87+
<>
88+
<Avatar
89+
source={ownerDetails.avatar}
90+
avatarID={ownerDetails.accountID}
91+
name={ownerDisplayName}
92+
type={CONST.ICON_TYPE_AVATAR}
93+
size={CONST.AVATAR_SIZE.SMALLER}
94+
/>
95+
<Text
96+
numberOfLines={1}
97+
style={styles.flexShrink1}
98+
>
99+
{ownerDisplayName}
100+
</Text>
101+
</>
102+
)}
103+
</View>
104+
<View style={styles.flex1}>
105+
<Text>{memberCount}</Text>
106+
</View>
107+
</>
108+
),
128109
};
129110
})
130-
.sort((a, b) => localeCompare(a.roomName, b.roomName));
111+
.sort((a, b) => localeCompare(a.text ?? '', b.text ?? ''));
131112

132113
useFocusEffect(() => {
133114
openWorkspaceRoomsPage(policyID);
134115
});
135116

117+
const customListHeader = (
118+
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mh5, styles.ph4, styles.pv3]}>
119+
<View style={[styles.alignItemsCenter, styles.justifyContentCenter, StyleUtils.getAvatarWidthStyle(CONST.AVATAR_SIZE.DEFAULT)]}>
120+
<Icon
121+
src={headerIcons.Profile}
122+
width={variables.iconSizeSmall}
123+
height={variables.iconSizeSmall}
124+
fill={theme.icon}
125+
/>
126+
</View>
127+
<View style={styles.flex1}>
128+
<Text style={styles.textLabelSupporting}>{translate('common.name')}</Text>
129+
</View>
130+
<View style={styles.flex2}>
131+
<Text style={styles.textLabelSupporting}>{translate('common.createdBy')}</Text>
132+
</View>
133+
<View style={[styles.flex1, styles.mr7]}>
134+
<Text style={styles.textLabelSupporting}>{translate('common.members')}</Text>
135+
</View>
136+
</View>
137+
);
138+
136139
return (
137140
<AccessOrNotFoundWrapper
138141
policyID={policyID}
@@ -157,29 +160,19 @@ function WorkspaceRoomsPage({route}: WorkspaceRoomsPageProps) {
157160
success
158161
isDisabled
159162
onPress={() => {}}
160-
icon={icons.Plus}
163+
icon={headerIcons.Plus}
161164
text={translate('common.create')}
162165
/>
163166
</HeaderWithBackButton>
164167

165-
<View style={[styles.flex1, styles.pv3, styles.ph5]}>
166-
<FlashList
167-
data={rows}
168-
keyExtractor={(row) => row.report.reportID}
169-
ItemSeparatorComponent={RowSeparator}
170-
ListHeaderComponent={ListHeader}
171-
renderItem={({item}: ListRenderItemInfo<WorkspaceRoomsRow>) => (
172-
<WorkspaceRoomsListItem
173-
report={item.report}
174-
roomName={item.roomName}
175-
ownerDetails={item.ownerDetails}
176-
ownerDisplayName={item.ownerDisplayName}
177-
memberCount={item.memberCount}
178-
onPress={() => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.report.reportID))}
179-
/>
180-
)}
181-
/>
182-
</View>
168+
<SelectionList<RoomListItem>
169+
data={data}
170+
ListItem={TableListItem}
171+
onSelectRow={(item) => Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(item.reportID))}
172+
customListHeader={customListHeader}
173+
shouldHeaderBeInsideList
174+
shouldShowRightCaret
175+
/>
183176
</ScreenWrapper>
184177
</AccessOrNotFoundWrapper>
185178
);

0 commit comments

Comments
 (0)