9.5 KiB
9.5 KiB
spec, created, status
| spec | created | status |
|---|---|---|
| docs/specs/ui/requirements/task-surface-refresh.md | 2026-07-27 | building |
Implementation Plan: Task Surface Foreground Refresh and Mobile Create Action
Overview
Introduce one frontend foreground lifecycle primitive, migrate task-detail backfills to it, and make Kanban and List expose authoritative refresh callbacks. Add a phone-native pull surface around the existing List and Kanban scroll compositions, then reuse the shipped Kanban FAB geometry for task creation from List. No backend route, WebSocket event, persistence, or public API changes are required.
Frontend
Foreground lifecycle and request coalescing
- Add
apps/web/hooks/use-foreground-refresh.tsto listen for visiblevisibilitychange,window.focus,pageshow, andonline. - Keep the latest callback in a ref, suppress overlapping calls, and deduplicate
the burst of browser events emitted by one foreground transition. The hook
must not depend on
connection.status, because a suspended socket can still appear connected. - Replace the visibility-only listeners in
apps/web/hooks/use-task-sessions.ts,apps/web/hooks/domains/session/use-session-messages.ts, andapps/web/hooks/domains/session/use-queue.tswith the shared lifecycle hook. Preserve each hook's current forced-reload and error behavior. - Refactor the task loader in
apps/web/components/task/task-page-content.tsxinto a stable forced-refresh callback. Foreground recovery reloads the active task without changing the selected task/session and discards a response if navigation changed its task identity before completion.
Kanban and List authoritative refresh
- Refactor
apps/web/hooks/domains/kanban/use-all-workflow-snapshots.tsto expose a promise-returning forced refresh for the current workspace. Keep its existing workspace-generation guard and in-flight task preservation. - When refreshing the workflow selected as the active Kanban workflow, update
both
kanbanMulti.snapshots[workflowId]and the activekanbansnapshot from the same guarded response. This keeps preview/create consumers consistent with the rendered board. - Use the shared foreground hook from the Kanban data owner so Home refreshes whether or not WebSocket status changes. A partial or failed refresh retains prior snapshots and allows retry.
- Extend the List page's existing
fetchTaskscallback inapps/web/app/tasks/tasks-page-client.tsxwith silent foreground failure handling while retaining toast feedback for explicit/manual loads. Reuse its request sequence and workspace guard so stale requests cannot commit. - Invoke the same List callback after a successful List task creation so the new row appears only when it matches the current filters and page.
Mobile pull surface
- Add a small pure gesture-state helper under
apps/web/lib/mobile/pull-to-refresh.tsand a responsive component underapps/web/components/mobile/pull-to-refresh.tsx. - The component activates only for one-touch, primarily vertical downward
movement beginning with the active vertical scroll owner at
scrollTop === 0. It cancels for horizontal movement, multi-touch, task drag interactions, and movement below the threshold. - Keep rendered content mounted during refresh. Show an accessible top indicator for pull progress, release readiness, and refreshing; settle it after success or failure.
- Wrap the List scroll surface and the mobile Kanban content without adding a
second vertical scroll owner. The current List
mainand current Kanban column remain authoritative scroll containers.
Mobile List create action
- Reuse
apps/web/components/kanban/mobile-fab.tsxfromapps/web/app/tasks/tasks-page-client.tsx; do not introduce a second FAB style. - Mount the existing
TaskCreateDialogfor List with the active workspace and workflow context. When no workflow filter is selected, preserve the existing dialog workflow-resolution behavior rather than persisting a new filter. - Keep the control phone-only, fixed above the bottom safe area, at a 56px touch target, with the existing Add task accessible name.
Mobile design contract
- Desktop outcome / mobile entry: foreground focus refresh works on all viewports. Phone users additionally pull the current task surface or tap the bottom-right Add task FAB in List.
- Nearest exemplar:
apps/web/components/kanban/mobile-fab.tsxsupplies FAB geometry and safe-area behavior; the current mobile Kanban column and mobile Listmainsupply scroll ownership. - Hierarchy / primary action: tasks remain the focal content. Create is the persistent thumb-reachable primary action; refresh is a top-edge recovery gesture with visible progress.
- Presentation: inline pull indicator plus the existing fixed FAB. A drawer or full-height surface would add unnecessary navigation for these frequent, shallow actions.
- Scroll and viewport: no new document scroll or nested vertical scroller.
The gesture interrogates the touched view's existing vertical scroll owner,
uses overscroll containment, and leaves the FAB clear of
env(safe-area-inset-bottom). - Shared state: data loaders, filters, workspace/workflow selection, and create behavior remain shared. Only the pull gesture and FAB presentation are phone-specific.
- Mobile proof: Playwright creates a task through List's FAB, pulls List and Kanban to recover dropped updates, checks a mid-scroll/horizontal gesture does not refresh, and asserts no horizontal document overflow.
Tests
- What: foreground events invoke one refresh independent of WebSocket
status; burst events and in-flight requests coalesce; hidden visibility
events do nothing.
File:
apps/web/hooks/use-foreground-refresh.test.ts. How: Vitest hook tests with controlled promises and synthetic browser lifecycle events. - What: sessions, chat messages, and queued messages backfill on window
focus as well as visibility without duplicate concurrent requests.
Files:
apps/web/hooks/use-task-sessions.test.ts,apps/web/hooks/domains/session/use-visibility-backfill.test.ts,apps/web/hooks/domains/session/use-queue.test.ts. How: existing hook fakes extended with focus/burst cases. - What: forced Kanban refresh updates current workflow snapshots, preserves
mutations arriving during an in-flight request, and rejects stale workspace
results.
Files:
apps/web/hooks/domains/kanban/use-all-workflow-snapshots.test.ts,apps/web/hooks/domains/kanban/use-all-workflow-snapshots-inflight.test.ts. How: controlled API promises against the existing store harness. - What: pull state crosses the threshold only for eligible vertical
gestures and resets on cancellation, horizontal movement, multi-touch, and
request settlement.
File:
apps/web/lib/mobile/pull-to-refresh.test.ts. How: table-driven Vitest tests of the pure gesture state transitions.
E2E Tests
- Scenario: mobile List FAB opens the real create flow and a matching task
appears in the current List after creation.
File:
apps/web/e2e/tests/task/mobile-task-surface-refresh.spec.ts. What to verify: 56px FAB, safe viewport containment, dialog context, created row, and no horizontal document overflow. - Scenario: a dropped task update is recovered by pulling mobile List and
mobile Kanban from scroll-top; mid-scroll and primarily horizontal gestures
do not start a refresh.
File:
apps/web/e2e/tests/task/mobile-task-surface-refresh.spec.ts. What to verify: indicator state and authoritative task content after the pull while the current view/workflow/column remains selected. - Scenario: Kanban/Home and List recover a deliberately dropped task event
when the desktop page receives a foreground signal while its WebSocket still
exists.
File:
apps/web/e2e/tests/task/task-surface-foreground-refresh.spec.ts. What to verify: stale row/card becomes current withoutpage.reload(). - Scenario: task-detail chat recovers a deliberately dropped persisted
message on desktop focus.
File:
apps/web/e2e/tests/task/task-surface-foreground-refresh.spec.ts. What to verify: the message appears without navigation/reload and the selected task/session does not change. - Extend
apps/web/e2e/helpers/ws-drop.tsonly as needed to drop named task or message notifications deterministically; the test must assert that the intended frame was actually dropped.
Implementation Waves And Parallel Candidates
The tasks are sequential because Task 02 consumes the foreground primitive from Task 01, and E2E in Task 03 covers both.
Risks
- Browser focus, visibility, page-cache, and online events commonly arrive in a burst; insufficient deduplication would multiply expensive snapshot and message requests.
- Kanban's horizontal Embla gesture and task drag-and-drop share the same touch surface as pull-to-refresh. Direction locking and scroll-top detection must happen before preventing default browser behavior.
- A snapshot refresh can race task creation, task WS updates, or workspace navigation. Existing generation and in-flight mutation guards must remain intact for both the multi-workflow and active snapshots.
- E2E must prove notifications were dropped; otherwise live WebSocket delivery could make foreground recovery tests pass without exercising the new path.