|
1 | 1 | import { usePanelLayoutStore } from "@features/panels/store/panelLayoutStore"; |
2 | 2 | import { useRightSidebarStore } from "@features/right-sidebar"; |
| 3 | +import { useSessions } from "@features/sessions/stores/sessionStore"; |
3 | 4 | import { usePinnedTasksStore } from "@features/sidebar/stores/pinnedTasksStore"; |
4 | 5 | import { useSidebarStore } from "@features/sidebar/stores/sidebarStore"; |
| 6 | +import { useTaskViewedStore } from "@features/sidebar/stores/taskViewedStore"; |
5 | 7 | import { useTasks } from "@features/tasks/hooks/useTasks"; |
6 | 8 | import { useWorkspaceStore } from "@features/workspace/stores/workspaceStore"; |
7 | 9 | import { SHORTCUTS } from "@renderer/constants/keyboard-shortcuts"; |
@@ -43,21 +45,55 @@ export function GlobalEventHandlers({ |
43 | 45 |
|
44 | 46 | const { data: allTasks = [] } = useTasks(); |
45 | 47 | const pinnedTaskIds = usePinnedTasksStore((state) => state.pinnedTaskIds); |
| 48 | + const sessions = useSessions(); |
| 49 | + const lastViewedAt = useTaskViewedStore((state) => state.lastViewedAt); |
| 50 | + const localActivityAt = useTaskViewedStore((state) => state.lastActivityAt); |
46 | 51 |
|
47 | | - // Build ordered task list for CMD+0-9 switching (pinned → active → recent) |
| 52 | + // Build ordered task list for CMD+0-9 switching, matching sidebar visual order: |
| 53 | + // pinned (by activity) → active (by activity) → recent (by creation) |
48 | 54 | const orderedTasks = useMemo((): Task[] => { |
49 | 55 | if (allTasks.length === 0) return []; |
50 | 56 |
|
51 | | - const sortedByActivity = [...allTasks].sort( |
52 | | - (a, b) => |
53 | | - new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime(), |
54 | | - ); |
| 57 | + const getLastActivityAt = (task: Task): number => { |
| 58 | + const apiUpdatedAt = new Date(task.updated_at).getTime(); |
| 59 | + const localActivity = localActivityAt[task.id]; |
| 60 | + return localActivity |
| 61 | + ? Math.max(apiUpdatedAt, localActivity) |
| 62 | + : apiUpdatedAt; |
| 63 | + }; |
| 64 | + |
| 65 | + const isTaskUnread = (task: Task): boolean => { |
| 66 | + const taskLastViewedAt = lastViewedAt[task.id]; |
| 67 | + return ( |
| 68 | + taskLastViewedAt !== undefined && |
| 69 | + getLastActivityAt(task) > taskLastViewedAt |
| 70 | + ); |
| 71 | + }; |
| 72 | + |
| 73 | + const taskNeedsPermission = (task: Task): boolean => { |
| 74 | + const session = Object.values(sessions).find((s) => s.taskId === task.id); |
| 75 | + return (session?.pendingPermissions?.size ?? 0) > 0; |
| 76 | + }; |
| 77 | + |
| 78 | + const pinned = allTasks |
| 79 | + .filter((t) => pinnedTaskIds.has(t.id)) |
| 80 | + .sort((a, b) => getLastActivityAt(b) - getLastActivityAt(a)); |
| 81 | + |
| 82 | + const unpinned = allTasks.filter((t) => !pinnedTaskIds.has(t.id)); |
| 83 | + |
| 84 | + const active = unpinned |
| 85 | + .filter((t) => isTaskUnread(t) || taskNeedsPermission(t)) |
| 86 | + .sort((a, b) => getLastActivityAt(b) - getLastActivityAt(a)); |
55 | 87 |
|
56 | | - const pinned = sortedByActivity.filter((t) => pinnedTaskIds.has(t.id)); |
57 | | - const unpinned = sortedByActivity.filter((t) => !pinnedTaskIds.has(t.id)); |
| 88 | + const recent = unpinned |
| 89 | + .filter((t) => !isTaskUnread(t) && !taskNeedsPermission(t)) |
| 90 | + .sort( |
| 91 | + (a, b) => |
| 92 | + new Date(b.created_at).getTime() - new Date(a.created_at).getTime(), |
| 93 | + ); |
58 | 94 |
|
59 | | - return [...pinned, ...unpinned]; |
60 | | - }, [allTasks, pinnedTaskIds]); |
| 95 | + return [...pinned, ...active, ...recent]; |
| 96 | + }, [allTasks, pinnedTaskIds, sessions, lastViewedAt, localActivityAt]); |
61 | 97 |
|
62 | 98 | const handleSwitchTask = useCallback( |
63 | 99 | (index: number) => { |
|
0 commit comments