From 0c6bf5dd33691513082c69392bb8009fb9c682f8 Mon Sep 17 00:00:00 2001 From: 777genius Date: Fri, 29 May 2026 15:30:27 +0300 Subject: [PATCH] perf: page large team sections --- src/renderer/components/team/TeamListView.tsx | 177 ++++++++++++------ 1 file changed, 123 insertions(+), 54 deletions(-) diff --git a/src/renderer/components/team/TeamListView.tsx b/src/renderer/components/team/TeamListView.tsx index 5a6b9845..07697f4d 100644 --- a/src/renderer/components/team/TeamListView.tsx +++ b/src/renderer/components/team/TeamListView.tsx @@ -93,6 +93,9 @@ const LaunchTeamDialog = lazy(() => import('./dialogs/LaunchTeamDialog').then((m) => ({ default: m.LaunchTeamDialog })) ); +const TEAM_SECTION_INITIAL_VISIBLE_COUNT = 24; +const TEAM_SECTION_PAGE_SIZE = 24; + interface CreateTeamDialogLoadingFallbackProps { readonly isCopy: boolean; readonly onClose: () => void; @@ -515,12 +518,16 @@ const ActiveTeamCard = ({ export const TeamListView = memo(function TeamListView(): React.JSX.Element { const { isLight } = useTheme(); const { t } = useAppTranslation('team'); + const { t: tCommon } = useAppTranslation('common'); const electronMode = isElectronMode(); const [showCreateDialog, setShowCreateDialog] = useState(false); const [copyData, setCopyData] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [filter, setFilter] = useState(EMPTY_TEAM_FILTER); const [aliveTeams, setAliveTeams] = useState([]); + const [teamSectionVisibleCountByKey, setTeamSectionVisibleCountByKey] = useState< + Record + >({}); const { teams, teamsLoading, @@ -1207,10 +1214,17 @@ export const TeamListView = memo(function TeamListView(): React.JSX.Element { const activeFiltered = filteredTeams.filter((t) => !t.deletedAt); const deletedFiltered = filteredTeams.filter((t) => t.deletedAt); + const shouldPageTeamSections = !searchQuery.trim() && !hasActiveFilters; + const selectedProjectSectionKey = currentProjectPath + ? `project:${normalizePath(currentProjectPath)}` + : 'project'; + const otherTeamsSectionKey = currentProjectPath + ? `other:${normalizePath(currentProjectPath)}` + : 'other'; const activeSections = currentProjectPath ? [ { - key: 'project', + key: selectedProjectSectionKey, title: t('list.sections.projectTeams', { project: folderName(currentProjectPath) || t('list.sections.selectedProject'), }), @@ -1219,7 +1233,7 @@ export const TeamListView = memo(function TeamListView(): React.JSX.Element { ), }, { - key: 'other', + key: otherTeamsSectionKey, title: t('list.sections.otherTeams'), teams: activeFiltered.filter( (team) => !teamMatchesProjectSelection(team, currentProjectPath) @@ -1238,58 +1252,113 @@ export const TeamListView = memo(function TeamListView(): React.JSX.Element { <> {activeSections.map((section, sectionIndex) => (
0 ? 'mt-6' : undefined}> - {section.title ? ( -
-

- {section.title} -

- - {section.teams.length} - -
- ) : null} -
- {section.teams.map((team) => { - const status = resolveTeamStatus( - team, - team.teamName, - aliveTeams, - getCurrentProvisioningProgressForTeam(provisioningState, team.teamName), - leadActivityByTeam - ); - const teamColorSet = team.color - ? getTeamColorSet(team.color) - : nameColorSet(team.displayName); - const matchesCurrentProject = currentProjectPath - ? teamMatchesProjectSelection(team, currentProjectPath) - : false; - return ( - - ); - })} -
+ {(() => { + const paged = + shouldPageTeamSections && section.teams.length > TEAM_SECTION_INITIAL_VISIBLE_COUNT; + const requestedVisibleCount = + teamSectionVisibleCountByKey[section.key] ?? TEAM_SECTION_INITIAL_VISIBLE_COUNT; + const visibleCount = paged + ? Math.min(section.teams.length, requestedVisibleCount) + : section.teams.length; + const visibleTeams = section.teams.slice(0, visibleCount); + const canShowMore = paged && visibleCount < section.teams.length; + const canShowLess = paged && visibleCount > TEAM_SECTION_INITIAL_VISIBLE_COUNT; + + return ( + <> + {section.title ? ( +
+

+ {section.title} +

+ + {section.teams.length} + +
+ ) : null} +
+ {visibleTeams.map((team) => { + const status = resolveTeamStatus( + team, + team.teamName, + aliveTeams, + getCurrentProvisioningProgressForTeam(provisioningState, team.teamName), + leadActivityByTeam + ); + const teamColorSet = team.color + ? getTeamColorSet(team.color) + : nameColorSet(team.displayName); + const matchesCurrentProject = currentProjectPath + ? teamMatchesProjectSelection(team, currentProjectPath) + : false; + return ( + + ); + })} +
+ {(canShowMore || canShowLess) && ( +
+ {canShowMore ? ( + + ) : null} + {canShowLess ? ( + + ) : null} +
+ )} + + ); + })()}
))}