perf: lazy-load TaskDetailDialog, SendMessageDialog, CreateTaskDialog, ChangeReviewDialog
Defers parsing 4575 lines of heavy dialog code until first use, reducing initial bundle parse time for TeamDetailView.
This commit is contained in:
parent
5d6667b23d
commit
5cd890dcdd
1 changed files with 142 additions and 126 deletions
|
|
@ -78,12 +78,9 @@ import {
|
||||||
import { useShallow } from 'zustand/react/shallow';
|
import { useShallow } from 'zustand/react/shallow';
|
||||||
|
|
||||||
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
import { AddMemberDialog } from './dialogs/AddMemberDialog';
|
||||||
import { CreateTaskDialog } from './dialogs/CreateTaskDialog';
|
|
||||||
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
import { EditTeamDialog } from './dialogs/EditTeamDialog';
|
||||||
import type { TeamLaunchDialogMode } from './dialogs/LaunchTeamDialog';
|
import type { TeamLaunchDialogMode } from './dialogs/LaunchTeamDialog';
|
||||||
import { ReviewDialog } from './dialogs/ReviewDialog';
|
import { ReviewDialog } from './dialogs/ReviewDialog';
|
||||||
import { SendMessageDialog } from './dialogs/SendMessageDialog';
|
|
||||||
import { TaskDetailDialog } from './dialogs/TaskDetailDialog';
|
|
||||||
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
import { executeTeamRelaunch } from './dialogs/teamRelaunchFlow';
|
||||||
import { KanbanBoard } from './kanban/KanbanBoard';
|
import { KanbanBoard } from './kanban/KanbanBoard';
|
||||||
import { UNASSIGNED_OWNER } from './kanban/KanbanFilterPopover';
|
import { UNASSIGNED_OWNER } from './kanban/KanbanFilterPopover';
|
||||||
|
|
@ -107,9 +104,20 @@ const TeamGraphOverlay = lazy(() =>
|
||||||
default: m.TeamGraphOverlay,
|
default: m.TeamGraphOverlay,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
const TaskDetailDialog = lazy(() =>
|
||||||
|
import('./dialogs/TaskDetailDialog').then((m) => ({ default: m.TaskDetailDialog }))
|
||||||
|
);
|
||||||
|
const SendMessageDialog = lazy(() =>
|
||||||
|
import('./dialogs/SendMessageDialog').then((m) => ({ default: m.SendMessageDialog }))
|
||||||
|
);
|
||||||
|
const CreateTaskDialog = lazy(() =>
|
||||||
|
import('./dialogs/CreateTaskDialog').then((m) => ({ default: m.CreateTaskDialog }))
|
||||||
|
);
|
||||||
|
const ChangeReviewDialog = lazy(() =>
|
||||||
|
import('./review/ChangeReviewDialog').then((m) => ({ default: m.ChangeReviewDialog }))
|
||||||
|
);
|
||||||
import { MemberList } from './members/MemberList';
|
import { MemberList } from './members/MemberList';
|
||||||
import { MessagesPanel } from './messages/MessagesPanel';
|
import { MessagesPanel } from './messages/MessagesPanel';
|
||||||
import { ChangeReviewDialog } from './review/ChangeReviewDialog';
|
|
||||||
import { ScheduleSection } from './schedule/ScheduleSection';
|
import { ScheduleSection } from './schedule/ScheduleSection';
|
||||||
import { TeamSidebarHost } from './sidebar/TeamSidebarHost';
|
import { TeamSidebarHost } from './sidebar/TeamSidebarHost';
|
||||||
import { TeamSidebarPortalSource } from './sidebar/TeamSidebarPortalSource';
|
import { TeamSidebarPortalSource } from './sidebar/TeamSidebarPortalSource';
|
||||||
|
|
@ -2857,21 +2865,23 @@ export const TeamDetailView = memo(
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CreateTaskDialog
|
<Suspense fallback={null}>
|
||||||
open={createTaskDialog.open}
|
<CreateTaskDialog
|
||||||
teamName={teamName}
|
open={createTaskDialog.open}
|
||||||
members={activeMembers}
|
teamName={teamName}
|
||||||
tasks={data.tasks}
|
members={activeMembers}
|
||||||
isTeamAlive={data.isAlive && !isTeamProvisioning}
|
tasks={data.tasks}
|
||||||
defaultSubject={createTaskDialog.defaultSubject}
|
isTeamAlive={data.isAlive && !isTeamProvisioning}
|
||||||
defaultDescription={createTaskDialog.defaultDescription}
|
defaultSubject={createTaskDialog.defaultSubject}
|
||||||
defaultOwner={createTaskDialog.defaultOwner}
|
defaultDescription={createTaskDialog.defaultDescription}
|
||||||
defaultStartImmediately={createTaskDialog.defaultStartImmediately}
|
defaultOwner={createTaskDialog.defaultOwner}
|
||||||
defaultChip={createTaskDialog.defaultChip}
|
defaultStartImmediately={createTaskDialog.defaultStartImmediately}
|
||||||
onClose={closeCreateTaskDialog}
|
defaultChip={createTaskDialog.defaultChip}
|
||||||
onSubmit={handleCreateTask}
|
onClose={closeCreateTaskDialog}
|
||||||
submitting={creatingTask}
|
onSubmit={handleCreateTask}
|
||||||
/>
|
submitting={creatingTask}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
<EditTeamDialog
|
<EditTeamDialog
|
||||||
open={editDialogOpen}
|
open={editDialogOpen}
|
||||||
|
|
@ -2997,103 +3007,107 @@ export const TeamDetailView = memo(
|
||||||
/>
|
/>
|
||||||
</Suspense>
|
</Suspense>
|
||||||
|
|
||||||
<SendMessageDialog
|
<Suspense fallback={null}>
|
||||||
open={sendDialogOpen}
|
<SendMessageDialog
|
||||||
teamName={teamName}
|
open={sendDialogOpen}
|
||||||
members={activeMembers}
|
teamName={teamName}
|
||||||
defaultRecipient={sendDialogRecipient}
|
members={activeMembers}
|
||||||
defaultText={sendDialogDefaultText}
|
defaultRecipient={sendDialogRecipient}
|
||||||
defaultChip={sendDialogDefaultChip}
|
defaultText={sendDialogDefaultText}
|
||||||
quotedMessage={replyQuote}
|
defaultChip={sendDialogDefaultChip}
|
||||||
isTeamAlive={data.isAlive}
|
quotedMessage={replyQuote}
|
||||||
sending={sendingMessage}
|
isTeamAlive={data.isAlive}
|
||||||
sendError={sendMessageError}
|
sending={sendingMessage}
|
||||||
sendWarning={sendMessageWarning}
|
sendError={sendMessageError}
|
||||||
sendDebugDetails={sendMessageDebugDetails}
|
sendWarning={sendMessageWarning}
|
||||||
lastResult={lastSendMessageResult}
|
sendDebugDetails={sendMessageDebugDetails}
|
||||||
onSend={async (member, text, summary, attachments, actionMode, taskRefs) => {
|
lastResult={lastSendMessageResult}
|
||||||
const sentAtMs = Date.now();
|
onSend={async (member, text, summary, attachments, actionMode, taskRefs) => {
|
||||||
setPendingRepliesByMember((prev) => ({ ...prev, [member]: sentAtMs }));
|
const sentAtMs = Date.now();
|
||||||
try {
|
setPendingRepliesByMember((prev) => ({ ...prev, [member]: sentAtMs }));
|
||||||
const result = await sendTeamMessage(teamName, {
|
try {
|
||||||
member,
|
const result = await sendTeamMessage(teamName, {
|
||||||
text,
|
member,
|
||||||
summary,
|
text,
|
||||||
attachments,
|
summary,
|
||||||
actionMode,
|
attachments,
|
||||||
taskRefs,
|
actionMode,
|
||||||
});
|
taskRefs,
|
||||||
if (
|
});
|
||||||
result?.runtimeDelivery?.attempted === true &&
|
if (
|
||||||
result.runtimeDelivery.delivered === false
|
result?.runtimeDelivery?.attempted === true &&
|
||||||
) {
|
result.runtimeDelivery.delivered === false
|
||||||
|
) {
|
||||||
|
setPendingRepliesByMember((prev) => {
|
||||||
|
if (prev[member] !== sentAtMs) return prev;
|
||||||
|
const next = { ...prev };
|
||||||
|
delete next[member];
|
||||||
|
return next;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
setPendingRepliesByMember((prev) => {
|
setPendingRepliesByMember((prev) => {
|
||||||
if (prev[member] !== sentAtMs) return prev;
|
if (prev[member] !== sentAtMs) return prev;
|
||||||
const next = { ...prev };
|
const next = { ...prev };
|
||||||
delete next[member];
|
delete next[member];
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
return result;
|
}}
|
||||||
} catch (error) {
|
onClose={() => {
|
||||||
setPendingRepliesByMember((prev) => {
|
setSendDialogOpen(false);
|
||||||
if (prev[member] !== sentAtMs) return prev;
|
setReplyQuote(undefined);
|
||||||
const next = { ...prev };
|
setSendDialogDefaultText(undefined);
|
||||||
delete next[member];
|
setSendDialogDefaultChip(undefined);
|
||||||
return next;
|
}}
|
||||||
});
|
/>
|
||||||
throw error;
|
</Suspense>
|
||||||
}
|
|
||||||
}}
|
|
||||||
onClose={() => {
|
|
||||||
setSendDialogOpen(false);
|
|
||||||
setReplyQuote(undefined);
|
|
||||||
setSendDialogDefaultText(undefined);
|
|
||||||
setSendDialogDefaultChip(undefined);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TaskDetailDialog
|
<Suspense fallback={null}>
|
||||||
open={selectedTask !== null}
|
<TaskDetailDialog
|
||||||
task={selectedTask}
|
open={selectedTask !== null}
|
||||||
teamName={teamName}
|
task={selectedTask}
|
||||||
kanbanTaskState={
|
teamName={teamName}
|
||||||
selectedTask ? data?.kanbanState.tasks[selectedTask.id] : undefined
|
kanbanTaskState={
|
||||||
}
|
selectedTask ? data?.kanbanState.tasks[selectedTask.id] : undefined
|
||||||
taskMap={taskMap}
|
|
||||||
members={activeMembers}
|
|
||||||
onClose={() => setSelectedTask(null)}
|
|
||||||
onScrollToTask={(taskId) => {
|
|
||||||
setSelectedTask(null);
|
|
||||||
const el = document.querySelector(`[data-task-id="${taskId}"]`);
|
|
||||||
if (el) {
|
|
||||||
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
||||||
el.classList.remove('kanban-card-focus-pulse');
|
|
||||||
void (el as HTMLElement).offsetWidth;
|
|
||||||
el.classList.add('kanban-card-focus-pulse');
|
|
||||||
el.addEventListener(
|
|
||||||
'animationend',
|
|
||||||
() => el.classList.remove('kanban-card-focus-pulse'),
|
|
||||||
{ once: true }
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}}
|
taskMap={taskMap}
|
||||||
onOwnerChange={(taskId, owner) => {
|
members={activeMembers}
|
||||||
void (async () => {
|
onClose={() => setSelectedTask(null)}
|
||||||
try {
|
onScrollToTask={(taskId) => {
|
||||||
await updateTaskOwner(teamName, taskId, owner);
|
setSelectedTask(null);
|
||||||
} catch {
|
const el = document.querySelector(`[data-task-id="${taskId}"]`);
|
||||||
// error via store
|
if (el) {
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||||
|
el.classList.remove('kanban-card-focus-pulse');
|
||||||
|
void (el as HTMLElement).offsetWidth;
|
||||||
|
el.classList.add('kanban-card-focus-pulse');
|
||||||
|
el.addEventListener(
|
||||||
|
'animationend',
|
||||||
|
() => el.classList.remove('kanban-card-focus-pulse'),
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})();
|
}}
|
||||||
}}
|
onOwnerChange={(taskId, owner) => {
|
||||||
onViewChanges={handleViewChangesForFile}
|
void (async () => {
|
||||||
onOpenInEditor={(filePath) => {
|
try {
|
||||||
const { revealFileInEditor } = useStore.getState();
|
await updateTaskOwner(teamName, taskId, owner);
|
||||||
revealFileInEditor(filePath);
|
} catch {
|
||||||
}}
|
// error via store
|
||||||
onDeleteTask={handleDeleteTask}
|
}
|
||||||
/>
|
})();
|
||||||
|
}}
|
||||||
|
onViewChanges={handleViewChangesForFile}
|
||||||
|
onOpenInEditor={(filePath) => {
|
||||||
|
const { revealFileInEditor } = useStore.getState();
|
||||||
|
revealFileInEditor(filePath);
|
||||||
|
}}
|
||||||
|
onDeleteTask={handleDeleteTask}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
|
||||||
<TrashDialog
|
<TrashDialog
|
||||||
open={trashOpen}
|
open={trashOpen}
|
||||||
|
|
@ -3110,26 +3124,28 @@ export const TeamDetailView = memo(
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ChangeReviewDialog
|
<Suspense fallback={null}>
|
||||||
open={reviewDialogState.open}
|
<ChangeReviewDialog
|
||||||
onOpenChange={(open) =>
|
open={reviewDialogState.open}
|
||||||
setReviewDialogState((prev) => ({
|
onOpenChange={(open) =>
|
||||||
...prev,
|
setReviewDialogState((prev) => ({
|
||||||
open,
|
...prev,
|
||||||
...(open
|
open,
|
||||||
? {}
|
...(open
|
||||||
: { initialFilePath: undefined, taskChangeRequestOptions: undefined }),
|
? {}
|
||||||
}))
|
: { initialFilePath: undefined, taskChangeRequestOptions: undefined }),
|
||||||
}
|
}))
|
||||||
teamName={teamName}
|
}
|
||||||
mode={reviewDialogState.mode}
|
teamName={teamName}
|
||||||
memberName={reviewDialogState.memberName}
|
mode={reviewDialogState.mode}
|
||||||
taskId={reviewDialogState.taskId}
|
memberName={reviewDialogState.memberName}
|
||||||
initialFilePath={reviewDialogState.initialFilePath}
|
taskId={reviewDialogState.taskId}
|
||||||
taskChangeRequestOptions={reviewDialogState.taskChangeRequestOptions}
|
initialFilePath={reviewDialogState.initialFilePath}
|
||||||
projectPath={data.config.projectPath}
|
taskChangeRequestOptions={reviewDialogState.taskChangeRequestOptions}
|
||||||
onEditorAction={handleEditorAction}
|
projectPath={data.config.projectPath}
|
||||||
/>
|
onEditorAction={handleEditorAction}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={setMessagesPanelMountPoint}
|
ref={setMessagesPanelMountPoint}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue