| ... | @@ -336,6 +336,24 @@ export function planSidebarThreadDrop(input: { | ... | @@ -336,6 +336,24 @@ export function planSidebarThreadDrop(input: { |
| 336 | if (input.supportsSettlement === false && (target.section === "settled" || activeSettled)) { | 336 | if (input.supportsSettlement === false && (target.section === "settled" || activeSettled)) { |
| 337 | return { kind: "none" }; | 337 | return { kind: "none" }; |
| 338 | } | 338 | } |
| | 339 | // Rows whose server cannot store an order (an older server, or a machine |
| | 340 | // that is offline) are never written. Keyless ones sort outside the keyed |
| | 341 | // run, so they leave the plan; keyed ones stay as bounds. Before, one keyless row |
| | 342 | // refused every drop that needed fresh keys for its neighbors. |
| | 343 | const arrange = ( |
| | 344 | order: readonly string[], |
| | 345 | keysById: ReadonlyMap<string, string | null | undefined>, |
| | 346 | writable: ReadonlySet<string> | undefined, |
| | 347 | ) => { |
| | 348 | if (!writable) return planPinnedReorder({ orderedIds: order, keysById, movedId: activeKey }); |
| | 349 | if (!writable.has(activeKey)) return null; |
| | 350 | const assignments = planPinnedReorder({ |
| | 351 | orderedIds: order.filter((key) => writable.has(key) || keysById.get(key) != null), |
| | 352 | keysById, |
| | 353 | movedId: activeKey, |
| | 354 | }); |
| | 355 | return assignments.every(({ id }) => writable.has(id)) ? assignments : null; |
| | 356 | }; |
| 339 | switch (target.section) { | 357 | switch (target.section) { |
| 340 | case "active": { | 358 | case "active": { |
| 341 | // Like the settled tail: threads can enter a time-ordered inbox, but | 359 | // Like the settled tail: threads can enter a time-ordered inbox, but |
| ... | @@ -360,14 +378,8 @@ export function planSidebarThreadDrop(input: { | ... | @@ -360,14 +378,8 @@ export function planSidebarThreadDrop(input: { |
| 360 | ) { | 378 | ) { |
| 361 | return { kind: "none" }; | 379 | return { kind: "none" }; |
| 362 | } | 380 | } |
| 363 | const assignments = planPinnedReorder({ | 381 | const assignments = arrange(order, activeKeysById, activeReorderableKeys); |
| 364 | orderedIds: order, | 382 | if (assignments === null) return { kind: "none" }; |
| 365 | keysById: activeKeysById, | | |
| 366 | movedId: activeKey, | | |
| 367 | }); | | |
| 368 | if (activeReorderableKeys && assignments.some(({ id }) => !activeReorderableKeys.has(id))) { | | |
| 369 | return { kind: "none" }; | | |
| 370 | } | | |
| 371 | return { | 383 | return { |
| 372 | kind: "move-active", | 384 | kind: "move-active", |
| 373 | order, | 385 | order, |
| ... | @@ -389,14 +401,8 @@ export function planSidebarThreadDrop(input: { | ... | @@ -389,14 +401,8 @@ export function planSidebarThreadDrop(input: { |
| 389 | ) { | 401 | ) { |
| 390 | return { kind: "none" }; | 402 | return { kind: "none" }; |
| 391 | } | 403 | } |
| 392 | const assignments = planPinnedReorder({ | 404 | const assignments = arrange(order, pinnedKeysById, reorderableKeys); |
| 393 | orderedIds: order, | 405 | if (assignments === null) return { kind: "none" }; |
| 394 | keysById: pinnedKeysById, | | |
| 395 | movedId: activeKey, | | |
| 396 | }); | | |
| 397 | if (reorderableKeys && assignments.some(({ id }) => !reorderableKeys.has(id))) { | | |
| 398 | return { kind: "none" }; | | |
| 399 | } | | |
| 400 | if (activeSection === "pinned") { | 406 | if (activeSection === "pinned") { |
| 401 | return assignments.length === 0 | 407 | return assignments.length === 0 |
| 402 | ? { kind: "none" } | 408 | ? { kind: "none" } |