Commit 1c5393a
authored
feat(workspaces): pin workspaces and widen the switcher to six rows (#6397)
* feat(workspaces): pin workspaces and widen the switcher to six rows
Show up to six workspaces in the switcher instead of three, keeping the
search input from six onward so it appears exactly when the list fills.
Pin workspaces to the top of the switcher via the existing row context
menu. Pins are per-user and global, so they live on the user's settings
row rather than in `pinned_item`, which scopes every row to one
workspace. They ride along on the /api/workspaces payload the switcher
already loads, so the server prefetch hydrates them and pinned-first
ordering never re-sorts after hydration.
Drop the seat/workspace-migration disclosure copy from both invitation
accept surfaces. The accept-time disclosure tokens are unchanged, so the
server still verifies the outcome hasn't shifted since the page loaded.
* fix(workspaces): serialize pin writes so a rapid toggle cannot be undone
Each write carries the whole pin list, so two overlapping requests that the
network delivered out of order left the earlier click as the stored state.
Chain them instead, and hold reconciliation until the last queued write
settles — refetching between two writes rendered the server's intermediate
state and bounced the row out of the pinned group and back.
* refactor(workspaces): store workspace pins in pinned_item, not user settings
Workspace pins were a jsonb array on the settings row, replaced wholesale on
every toggle. That shape is what forced the write serialization in 53ee94f:
two overlapping toggles each sent the entire list, so the one that landed last
won regardless of which the user clicked last.
pinned_item is the canonical pinning table and its resource_type is plain text
precisely so kinds can be added without a migration, so `workspace` joins it as
a sixth kind. A pin is now one row: pinning inserts, unpinning deletes, and two
toggles touch different rows and cannot overwrite each other. The serialization,
the outstanding-write counter, the settings column, and its migration all go
away, and deleting a workspace now cascades its pins.
Reads stay on the /api/workspaces payload — the switcher needs the pins *of*
every workspace, not the pins *inside* one — so the sidebar prefetch still
hydrates them and pinned-first ordering is correct on first paint.
* fix(workspaces): serialize same-workspace pin toggles and tolerate replays
Splitting pins into rows removed the lost-update race between *different*
workspaces but not the one on a single row: pin then unpin the same workspace
and the DELETE could overtake its INSERT, delete nothing, and leave the
workspace pinned. A mutation scope serializes them; TanStack runs onMutate
before the scope gate, so the optimistic update is still immediate.
Both duplicate-click replays now resolve to their end state rather than
erroring — a repeat pin answers 409, a repeat unpin 404, and each means the
row is already how the caller wants it. Rollback undoes its own toggle instead
of restoring a snapshot, so a sibling toggle's optimistic state survives.
Also: cap the switcher to the height Radix measured, since six rows can push
the footer actions off a short viewport with nothing able to scroll to them;
drop a dead pinned-item invalidation and a redundant ref; return the pin set
from the hook to match usePinnedIds; and exclude workspace pins from the
unscoped pinned-items listing, where they would read as a resource inside
themselves.
* fix(workspaces): hold pin reconciliation until nothing is still queued
The mutation scope serializes the writes, so an earlier toggle settles while a
later one is still waiting its turn. Invalidating there refetched the server's
intermediate state and bounced the row out of the pinned group and back before
the last write had even left the client.
* fix(workspaces): count outstanding pin toggles off the mutation cache
`hooks/queries/workspace.ts` has no 'use client' directive because server code
imports `workspaceKeys` during SSR, so the `useRef` counter added in 3dc924d
broke the production build — caught by CI, not by typecheck or tests.
`isMutating` answers the same question without a hook: `onSettled` runs before
the mutation leaves `pending`, so it counts itself, and anything above one means
a later toggle is still queued behind the scope.1 parent d317607 commit 1c5393a
22 files changed
Lines changed: 570 additions & 517 deletions
File tree
- apps/sim
- app
- api
- invitations/[id]
- pinned-items
- workspaces
- invite/[id]
- workspace/[workspaceId]/w/components/sidebar
- components/workspace-header
- components/pending-invitations
- hooks
- ee/workspace-forking/hooks
- hooks/queries
- lib
- api/contracts
- invitations
- pinned-items
- users
- workspaces
- packages/db
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
71 | 70 | | |
72 | 71 | | |
73 | | - | |
74 | 72 | | |
75 | 73 | | |
76 | 74 | | |
77 | 75 | | |
78 | | - | |
79 | 76 | | |
80 | 77 | | |
81 | 78 | | |
| |||
85 | 82 | | |
86 | 83 | | |
87 | 84 | | |
88 | | - | |
89 | 85 | | |
90 | 86 | | |
91 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
| 5 | + | |
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
63 | 71 | | |
64 | 72 | | |
65 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
53 | 53 | | |
54 | 54 | | |
55 | 55 | | |
56 | | - | |
| 56 | + | |
57 | 57 | | |
58 | 58 | | |
59 | 59 | | |
60 | | - | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
61 | 66 | | |
62 | 67 | | |
63 | 68 | | |
| |||
100 | 105 | | |
101 | 106 | | |
102 | 107 | | |
| 108 | + | |
103 | 109 | | |
104 | 110 | | |
105 | 111 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | 5 | | |
7 | 6 | | |
8 | 7 | | |
| |||
11 | 10 | | |
12 | 11 | | |
13 | 12 | | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | 13 | | |
20 | 14 | | |
21 | 15 | | |
| |||
25 | 19 | | |
26 | 20 | | |
27 | 21 | | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
28 | 25 | | |
29 | 26 | | |
30 | 27 | | |
| |||
233 | 230 | | |
234 | 231 | | |
235 | 232 | | |
236 | | - | |
237 | 233 | | |
238 | 234 | | |
239 | 235 | | |
| |||
491 | 487 | | |
492 | 488 | | |
493 | 489 | | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
499 | | - | |
500 | | - | |
501 | | - | |
502 | | - | |
503 | | - | |
504 | | - | |
505 | | - | |
506 | | - | |
507 | | - | |
508 | | - | |
509 | | - | |
510 | | - | |
511 | | - | |
512 | | - | |
513 | | - | |
514 | | - | |
515 | | - | |
516 | | - | |
517 | | - | |
518 | | - | |
519 | | - | |
520 | | - | |
521 | | - | |
522 | | - | |
523 | | - | |
524 | | - | |
525 | | - | |
526 | | - | |
527 | | - | |
528 | | - | |
529 | | - | |
530 | | - | |
531 | | - | |
532 | | - | |
533 | | - | |
534 | 490 | | |
535 | 491 | | |
536 | 492 | | |
537 | 493 | | |
538 | 494 | | |
539 | 495 | | |
540 | | - | |
| 496 | + | |
541 | 497 | | |
542 | 498 | | |
543 | 499 | | |
| |||
Lines changed: 25 additions & 67 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
5 | | - | |
6 | 5 | | |
7 | 6 | | |
8 | 7 | | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | 8 | | |
14 | 9 | | |
15 | 10 | | |
| |||
43 | 38 | | |
44 | 39 | | |
45 | 40 | | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | 41 | | |
78 | 42 | | |
79 | 43 | | |
| |||
133 | 97 | | |
134 | 98 | | |
135 | 99 | | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | | - | |
141 | | - | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
155 | | - | |
156 | | - | |
157 | | - | |
158 | | - | |
159 | | - | |
160 | | - | |
161 | | - | |
162 | | - | |
163 | | - | |
164 | | - | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
165 | 107 | | |
166 | | - | |
167 | | - | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
168 | 126 | | |
169 | 127 | | |
170 | 128 | | |
| |||
Lines changed: 8 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
61 | 61 | | |
62 | 62 | | |
63 | 63 | | |
64 | | - | |
| 64 | + | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
71 | 71 | | |
| 72 | + | |
| 73 | + | |
72 | 74 | | |
73 | 75 | | |
74 | 76 | | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
75 | 80 | | |
76 | 81 | | |
77 | 82 | | |
| |||
86 | 91 | | |
87 | 92 | | |
88 | 93 | | |
| 94 | + | |
| 95 | + | |
89 | 96 | | |
90 | 97 | | |
91 | 98 | | |
| |||
0 commit comments