perf(app): limit initial sidebar workspace rendering
Large project, status, and pinned groups now render 20 rows until expanded. Newly discovered workspaces are prepended without disturbing the saved order.
This commit is contained in:
@@ -42,6 +42,8 @@ import {
|
||||
import { useSidebarCollapsedSectionsStore } from "@/stores/sidebar-collapsed-sections-store";
|
||||
import { SidebarWorkspaceMenu } from "@/components/sidebar/sidebar-workspace-menu";
|
||||
import { PinnedSectionHeader } from "@/components/sidebar/pinned-section-header";
|
||||
import { SidebarGroupToggleRow } from "@/components/sidebar/sidebar-group-toggle-row";
|
||||
import { useLimitedSidebarGroup } from "@/components/sidebar/use-limited-sidebar-group";
|
||||
import type { ToggleSidebarWorkspacePin } from "@/hooks/use-sidebar-workspace-pin";
|
||||
|
||||
// Themed icon wrappers
|
||||
@@ -93,6 +95,12 @@ export function SidebarStatusWorkspaceList({
|
||||
const togglePinnedCollapsed = useSidebarCollapsedSectionsStore(
|
||||
(state) => state.togglePinnedCollapsed,
|
||||
);
|
||||
const {
|
||||
visibleItems: visiblePinnedWorkspaces,
|
||||
expanded: pinnedWorkspacesExpanded,
|
||||
canToggle: canTogglePinnedWorkspaces,
|
||||
toggleExpanded: togglePinnedWorkspacesExpanded,
|
||||
} = useLimitedSidebarGroup(pinnedWorkspaces);
|
||||
|
||||
const statusShortcutIndex = showShortcutBadges ? shortcutIndexByWorkspaceKey : new Map();
|
||||
const content = (
|
||||
@@ -100,9 +108,9 @@ export function SidebarStatusWorkspaceList({
|
||||
{pinnedWorkspaces.length > 0 ? (
|
||||
<View style={styles.pinnedSection} testID="sidebar-pinned-section">
|
||||
<PinnedSectionHeader collapsed={pinnedCollapsed} onToggle={togglePinnedCollapsed} />
|
||||
{pinnedCollapsed
|
||||
? null
|
||||
: pinnedWorkspaces.map((workspace) => (
|
||||
{pinnedCollapsed ? null : (
|
||||
<>
|
||||
{visiblePinnedWorkspaces.map((workspace) => (
|
||||
<StatusWorkspaceRow
|
||||
key={workspace.workspaceKey}
|
||||
workspace={workspace}
|
||||
@@ -119,6 +127,15 @@ export function SidebarStatusWorkspaceList({
|
||||
onWorkspacePress={onWorkspacePress}
|
||||
/>
|
||||
))}
|
||||
{canTogglePinnedWorkspaces ? (
|
||||
<SidebarGroupToggleRow
|
||||
expanded={pinnedWorkspacesExpanded}
|
||||
onPress={togglePinnedWorkspacesExpanded}
|
||||
testID="sidebar-pinned-show-more"
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
) : null}
|
||||
{listHeaderComponent}
|
||||
@@ -188,38 +205,92 @@ function StatusGroupList({
|
||||
return (
|
||||
<>
|
||||
{groups.map((group) => (
|
||||
<View key={group.bucket} style={styles.statusGroupBlock}>
|
||||
<StatusGroupHeader group={group} collapsed={collapsedStatusGroupKeys.has(group.bucket)} />
|
||||
{!collapsedStatusGroupKeys.has(group.bucket) ? (
|
||||
<View
|
||||
style={styles.statusWorkspaceListContainer}
|
||||
testID={`sidebar-status-group-rows-${group.bucket}`}
|
||||
>
|
||||
{group.rows.map((workspace) => (
|
||||
<StatusWorkspaceRow
|
||||
key={workspace.workspaceKey}
|
||||
workspace={workspace}
|
||||
subtitle={buildStatusRowSubtitle({
|
||||
projectName: projectNamesByKey.get(workspace.projectKey) ?? "",
|
||||
hostLabel: showHostLabels
|
||||
? (hostLabelByServerId.get(workspace.serverId) ?? workspace.serverId)
|
||||
: null,
|
||||
})}
|
||||
shortcutNumber={shortcutIndex.get(workspace.workspaceKey) ?? null}
|
||||
showShortcutBadge={showShortcutBadges}
|
||||
canPin={supportsPinningByServerId.get(workspace.serverId) === true}
|
||||
onToggleWorkspacePin={onToggleWorkspacePin}
|
||||
onWorkspacePress={onWorkspacePress}
|
||||
/>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
<StatusGroupRows
|
||||
key={group.bucket}
|
||||
group={group}
|
||||
collapsed={collapsedStatusGroupKeys.has(group.bucket)}
|
||||
projectNamesByKey={projectNamesByKey}
|
||||
shortcutIndex={shortcutIndex}
|
||||
showShortcutBadges={showShortcutBadges}
|
||||
onWorkspacePress={onWorkspacePress}
|
||||
hostLabelByServerId={hostLabelByServerId}
|
||||
showHostLabels={showHostLabels}
|
||||
supportsPinningByServerId={supportsPinningByServerId}
|
||||
onToggleWorkspacePin={onToggleWorkspacePin}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function StatusGroupRows({
|
||||
group,
|
||||
collapsed,
|
||||
projectNamesByKey,
|
||||
shortcutIndex,
|
||||
showShortcutBadges,
|
||||
onWorkspacePress,
|
||||
hostLabelByServerId,
|
||||
showHostLabels,
|
||||
supportsPinningByServerId,
|
||||
onToggleWorkspacePin,
|
||||
}: {
|
||||
group: StatusGroup;
|
||||
collapsed: boolean;
|
||||
projectNamesByKey: Map<string, string>;
|
||||
shortcutIndex: Map<string, number>;
|
||||
showShortcutBadges: boolean;
|
||||
onWorkspacePress?: () => void;
|
||||
hostLabelByServerId: ReadonlyMap<string, string>;
|
||||
showHostLabels: boolean;
|
||||
supportsPinningByServerId: ReadonlyMap<string, boolean>;
|
||||
onToggleWorkspacePin: ToggleSidebarWorkspacePin;
|
||||
}) {
|
||||
const {
|
||||
visibleItems: visibleWorkspaces,
|
||||
expanded: workspacesExpanded,
|
||||
canToggle: canToggleWorkspaces,
|
||||
toggleExpanded: toggleWorkspacesExpanded,
|
||||
} = useLimitedSidebarGroup(group.rows);
|
||||
|
||||
return (
|
||||
<View style={styles.statusGroupBlock}>
|
||||
<StatusGroupHeader group={group} collapsed={collapsed} />
|
||||
{!collapsed ? (
|
||||
<View
|
||||
style={styles.statusWorkspaceListContainer}
|
||||
testID={`sidebar-status-group-rows-${group.bucket}`}
|
||||
>
|
||||
{visibleWorkspaces.map((workspace) => (
|
||||
<StatusWorkspaceRow
|
||||
key={workspace.workspaceKey}
|
||||
workspace={workspace}
|
||||
subtitle={buildStatusRowSubtitle({
|
||||
projectName: projectNamesByKey.get(workspace.projectKey) ?? "",
|
||||
hostLabel: showHostLabels
|
||||
? (hostLabelByServerId.get(workspace.serverId) ?? workspace.serverId)
|
||||
: null,
|
||||
})}
|
||||
shortcutNumber={shortcutIndex.get(workspace.workspaceKey) ?? null}
|
||||
showShortcutBadge={showShortcutBadges}
|
||||
canPin={supportsPinningByServerId.get(workspace.serverId) === true}
|
||||
onToggleWorkspacePin={onToggleWorkspacePin}
|
||||
onWorkspacePress={onWorkspacePress}
|
||||
/>
|
||||
))}
|
||||
{canToggleWorkspaces ? (
|
||||
<SidebarGroupToggleRow
|
||||
expanded={workspacesExpanded}
|
||||
onPress={toggleWorkspacesExpanded}
|
||||
testID={`sidebar-status-show-more-${group.bucket}`}
|
||||
/>
|
||||
) : null}
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
// Status mode breaks the project grouping, so the row needs the project name to stay
|
||||
// legible; the host is appended after a middle dot once labels are active.
|
||||
function buildStatusRowSubtitle({
|
||||
|
||||
Reference in New Issue
Block a user