perf(renderer): skip unchanged project task groups
This commit is contained in:
parent
fd3137237d
commit
b6139d8558
1 changed files with 158 additions and 121 deletions
|
|
@ -401,6 +401,15 @@ const TaskRows = memo(function TaskRows({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function areTaskReferencesEqual(prev: readonly GlobalTask[], next: readonly GlobalTask[]): boolean {
|
||||||
|
if (prev === next) return true;
|
||||||
|
if (prev.length !== next.length) return false;
|
||||||
|
for (let i = 0; i < prev.length; i += 1) {
|
||||||
|
if (prev[i] !== next[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
interface ProjectTaskGroupProps {
|
interface ProjectTaskGroupProps {
|
||||||
group: ProjectTaskGroupData;
|
group: ProjectTaskGroupData;
|
||||||
isCollapsed: boolean;
|
isCollapsed: boolean;
|
||||||
|
|
@ -427,131 +436,159 @@ interface ProjectTaskGroupProps {
|
||||||
getOwnerColorName: TaskOwnerColorResolver;
|
getOwnerColorName: TaskOwnerColorResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProjectTaskGroup = memo(function ProjectTaskGroup({
|
const ProjectTaskGroup = memo(
|
||||||
group,
|
function ProjectTaskGroup({
|
||||||
isCollapsed,
|
group,
|
||||||
visibleCount,
|
isCollapsed,
|
||||||
noProjectGroupColor,
|
visibleCount,
|
||||||
showMoreLabel,
|
noProjectGroupColor,
|
||||||
showLessLabel,
|
showMoreLabel,
|
||||||
isPinned,
|
showLessLabel,
|
||||||
isArchived,
|
isPinned,
|
||||||
isNewTask,
|
isArchived,
|
||||||
isTeamOffline,
|
isNewTask,
|
||||||
renamingKey,
|
isTeamOffline,
|
||||||
formatTeamHeader,
|
renamingKey,
|
||||||
onToggleGroup,
|
formatTeamHeader,
|
||||||
onVisibleCountChange,
|
onToggleGroup,
|
||||||
onTogglePin,
|
onVisibleCountChange,
|
||||||
onToggleArchive,
|
onTogglePin,
|
||||||
onMarkUnread,
|
onToggleArchive,
|
||||||
onRename,
|
onMarkUnread,
|
||||||
onDelete,
|
onRename,
|
||||||
onRenameComplete,
|
onDelete,
|
||||||
onRenameCancel,
|
onRenameComplete,
|
||||||
getDisplaySubject,
|
onRenameCancel,
|
||||||
getOwnerColorName,
|
getDisplaySubject,
|
||||||
}: ProjectTaskGroupProps): React.JSX.Element | null {
|
getOwnerColorName,
|
||||||
if (group.tasks.length === 0) return null;
|
}: ProjectTaskGroupProps): React.JSX.Element | null {
|
||||||
|
if (group.tasks.length === 0) return null;
|
||||||
|
|
||||||
const isNoProjectGroup = group.projectKey === NO_PROJECT_KEY;
|
const isNoProjectGroup = group.projectKey === NO_PROJECT_KEY;
|
||||||
const groupColor = isNoProjectGroup ? noProjectGroupColor : projectColor(group.projectLabel);
|
const groupColor = isNoProjectGroup ? noProjectGroupColor : projectColor(group.projectLabel);
|
||||||
const showMoreVisible = canProjectGroupShowMore(visibleCount, group.tasks.length);
|
const showMoreVisible = canProjectGroupShowMore(visibleCount, group.tasks.length);
|
||||||
const showLessVisible = canProjectGroupShowLess(visibleCount, group.tasks.length);
|
const showLessVisible = canProjectGroupShowLess(visibleCount, group.tasks.length);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onToggleGroup(group.projectKey)}
|
onClick={() => onToggleGroup(group.projectKey)}
|
||||||
className="hover:bg-surface-raised/40 sticky top-0 z-10 flex w-full cursor-pointer items-center gap-1.5 p-2 transition-colors"
|
className="hover:bg-surface-raised/40 sticky top-0 z-10 flex w-full cursor-pointer items-center gap-1.5 p-2 transition-colors"
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: 'var(--color-surface-sidebar)',
|
backgroundColor: 'var(--color-surface-sidebar)',
|
||||||
backgroundImage: isNoProjectGroup
|
backgroundImage: isNoProjectGroup
|
||||||
? undefined
|
? undefined
|
||||||
: `linear-gradient(90deg, ${groupColor.glow} 0%, transparent 80%)`,
|
: `linear-gradient(90deg, ${groupColor.glow} 0%, transparent 80%)`,
|
||||||
boxShadow: `inset 2px 0 0 ${groupColor.border}, inset 0 -1px 0 var(--color-border)`,
|
boxShadow: `inset 2px 0 0 ${groupColor.border}, inset 0 -1px 0 var(--color-border)`,
|
||||||
}}
|
}}
|
||||||
>
|
|
||||||
{isCollapsed ? (
|
|
||||||
<ChevronRight className="size-3 shrink-0 text-text-muted" />
|
|
||||||
) : (
|
|
||||||
<ChevronDown className="size-3 shrink-0 text-text-muted" />
|
|
||||||
)}
|
|
||||||
<Folder
|
|
||||||
className="size-3.5 shrink-0"
|
|
||||||
style={{ color: groupColor.icon }}
|
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
className="truncate text-[11px] font-bold leading-none"
|
|
||||||
style={{ color: groupColor.icon }}
|
|
||||||
>
|
>
|
||||||
{group.projectLabel}
|
{isCollapsed ? (
|
||||||
</span>
|
<ChevronRight className="size-3 shrink-0 text-text-muted" />
|
||||||
<span className="ml-auto shrink-0 text-[10px] font-normal text-text-muted">
|
) : (
|
||||||
{group.tasks.length}
|
<ChevronDown className="size-3 shrink-0 text-text-muted" />
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{!isCollapsed && (
|
|
||||||
<TaskRows
|
|
||||||
tasks={group.tasks}
|
|
||||||
visibleCount={visibleCount}
|
|
||||||
isPinned={isPinned}
|
|
||||||
isArchived={isArchived}
|
|
||||||
isNewTask={isNewTask}
|
|
||||||
isTeamOffline={isTeamOffline}
|
|
||||||
hideTeamName
|
|
||||||
hideProjectName
|
|
||||||
showTeamHeader
|
|
||||||
formatTeamHeader={formatTeamHeader}
|
|
||||||
renamingKey={renamingKey}
|
|
||||||
onTogglePin={onTogglePin}
|
|
||||||
onToggleArchive={onToggleArchive}
|
|
||||||
onMarkUnread={onMarkUnread}
|
|
||||||
onRename={onRename}
|
|
||||||
onDelete={onDelete}
|
|
||||||
onRenameComplete={onRenameComplete}
|
|
||||||
onRenameCancel={onRenameCancel}
|
|
||||||
getDisplaySubject={getDisplaySubject}
|
|
||||||
getOwnerColorName={getOwnerColorName}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{!isCollapsed && (showMoreVisible || showLessVisible) && (
|
|
||||||
<div className="flex items-center gap-2 px-3 pb-2 pt-1">
|
|
||||||
{showMoreVisible && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="text-[11px] font-medium text-text-muted transition-colors hover:text-text"
|
|
||||||
onClick={() =>
|
|
||||||
onVisibleCountChange(
|
|
||||||
group.projectKey,
|
|
||||||
getNextProjectGroupVisibleCount(visibleCount, group.tasks.length)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{showMoreLabel}
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
{showLessVisible && (
|
<Folder
|
||||||
<button
|
className="size-3.5 shrink-0"
|
||||||
type="button"
|
style={{ color: groupColor.icon }}
|
||||||
className="text-[11px] font-medium text-text-muted transition-colors hover:text-text"
|
aria-hidden="true"
|
||||||
onClick={() =>
|
/>
|
||||||
onVisibleCountChange(
|
<span
|
||||||
group.projectKey,
|
className="truncate text-[11px] font-bold leading-none"
|
||||||
getPreviousProjectGroupVisibleCount(visibleCount, group.tasks.length)
|
style={{ color: groupColor.icon }}
|
||||||
)
|
>
|
||||||
}
|
{group.projectLabel}
|
||||||
>
|
</span>
|
||||||
{showLessLabel}
|
<span className="ml-auto shrink-0 text-[10px] font-normal text-text-muted">
|
||||||
</button>
|
{group.tasks.length}
|
||||||
)}
|
</span>
|
||||||
</div>
|
</button>
|
||||||
)}
|
{!isCollapsed && (
|
||||||
</div>
|
<TaskRows
|
||||||
);
|
tasks={group.tasks}
|
||||||
});
|
visibleCount={visibleCount}
|
||||||
|
isPinned={isPinned}
|
||||||
|
isArchived={isArchived}
|
||||||
|
isNewTask={isNewTask}
|
||||||
|
isTeamOffline={isTeamOffline}
|
||||||
|
hideTeamName
|
||||||
|
hideProjectName
|
||||||
|
showTeamHeader
|
||||||
|
formatTeamHeader={formatTeamHeader}
|
||||||
|
renamingKey={renamingKey}
|
||||||
|
onTogglePin={onTogglePin}
|
||||||
|
onToggleArchive={onToggleArchive}
|
||||||
|
onMarkUnread={onMarkUnread}
|
||||||
|
onRename={onRename}
|
||||||
|
onDelete={onDelete}
|
||||||
|
onRenameComplete={onRenameComplete}
|
||||||
|
onRenameCancel={onRenameCancel}
|
||||||
|
getDisplaySubject={getDisplaySubject}
|
||||||
|
getOwnerColorName={getOwnerColorName}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!isCollapsed && (showMoreVisible || showLessVisible) && (
|
||||||
|
<div className="flex items-center gap-2 px-3 pb-2 pt-1">
|
||||||
|
{showMoreVisible && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-[11px] font-medium text-text-muted transition-colors hover:text-text"
|
||||||
|
onClick={() =>
|
||||||
|
onVisibleCountChange(
|
||||||
|
group.projectKey,
|
||||||
|
getNextProjectGroupVisibleCount(visibleCount, group.tasks.length)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showMoreLabel}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{showLessVisible && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="text-[11px] font-medium text-text-muted transition-colors hover:text-text"
|
||||||
|
onClick={() =>
|
||||||
|
onVisibleCountChange(
|
||||||
|
group.projectKey,
|
||||||
|
getPreviousProjectGroupVisibleCount(visibleCount, group.tasks.length)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{showLessLabel}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
(prev, next) =>
|
||||||
|
prev.group.projectKey === next.group.projectKey &&
|
||||||
|
prev.group.projectLabel === next.group.projectLabel &&
|
||||||
|
areTaskReferencesEqual(prev.group.tasks, next.group.tasks) &&
|
||||||
|
prev.isCollapsed === next.isCollapsed &&
|
||||||
|
prev.visibleCount === next.visibleCount &&
|
||||||
|
prev.noProjectGroupColor === next.noProjectGroupColor &&
|
||||||
|
prev.showMoreLabel === next.showMoreLabel &&
|
||||||
|
prev.showLessLabel === next.showLessLabel &&
|
||||||
|
prev.isPinned === next.isPinned &&
|
||||||
|
prev.isArchived === next.isArchived &&
|
||||||
|
prev.isNewTask === next.isNewTask &&
|
||||||
|
prev.isTeamOffline === next.isTeamOffline &&
|
||||||
|
prev.renamingKey === next.renamingKey &&
|
||||||
|
prev.formatTeamHeader === next.formatTeamHeader &&
|
||||||
|
prev.onToggleGroup === next.onToggleGroup &&
|
||||||
|
prev.onVisibleCountChange === next.onVisibleCountChange &&
|
||||||
|
prev.onTogglePin === next.onTogglePin &&
|
||||||
|
prev.onToggleArchive === next.onToggleArchive &&
|
||||||
|
prev.onMarkUnread === next.onMarkUnread &&
|
||||||
|
prev.onRename === next.onRename &&
|
||||||
|
prev.onDelete === next.onDelete &&
|
||||||
|
prev.onRenameComplete === next.onRenameComplete &&
|
||||||
|
prev.onRenameCancel === next.onRenameCancel &&
|
||||||
|
prev.getDisplaySubject === next.getDisplaySubject &&
|
||||||
|
prev.getOwnerColorName === next.getOwnerColorName
|
||||||
|
);
|
||||||
|
|
||||||
export const GlobalTaskList = memo(function GlobalTaskList({
|
export const GlobalTaskList = memo(function GlobalTaskList({
|
||||||
hideHeader = false,
|
hideHeader = false,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue