mergePending prepends the pending placeholder while the server's listSources returns in insertion order, so a newly added source appeared at the top during the pending phase and jumped to the bottom once the server confirmed. Sort the merged list by name so the placeholder lands in the same position the canonical row will occupy.
Unified · Split
| @@ -93,16 +93,17 @@ export interface PendingSource { · 92 unmodified lines | |||
| 93 | 93 | ||
| 94 | /** | 94 | /** |
| 95 | * Sidebar/list helper. Reads `sourcesAtom(scopeId)` and merges any pending | 95 | * Sidebar/list helper. Reads `sourcesAtom(scopeId)` and merges any pending |
| 96 | * placeholder rows on top so the UI updates instantly when an add form pushes | 96 | * placeholder rows in, sorting the combined list by name so the placeholder |
| 97 | * a placeholder. | 97 | * lands in the same position the canonical row will occupy — no visual jump |
| 98 | * when the server confirms. | ||
| 98 | */ | 99 | */ |
| 99 | export const useSourcesWithPending = (scopeId: ScopeId) => { | 100 | export const useSourcesWithPending = (scopeId: ScopeId) => { |
| 100 | const result = useAtomValue(sourcesAtom(scopeId)); | 101 | const result = useAtomValue(sourcesAtom(scopeId)); |
| 101 | const { pending } = usePendingResource<PendingSource>(PendingResource.sources); | 102 | const { pending } = usePendingResource<PendingSource>(PendingResource.sources); |
| 102 | return React.useMemo( | 103 | return React.useMemo( |
| 103 | () => | 104 | () => |
| 104 | Result.map(result, (sources) => | 105 | Result.map(result, (sources) => { |
| 105 | mergePending( | 106 | const merged = mergePending( |
| 106 | pending, | 107 | pending, |
| 107 | sources, | 108 | sources, |
| 108 | (s) => s.id, | 109 | (s) => s.id, |
| @@ -117,8 +118,9 @@ export const useSourcesWithPending = (scopeId: ScopeId) => { · 8 unmodified lines | |||
| 117 | canRefresh: false, | 118 | canRefresh: false, |
| 118 | canEdit: false, | 119 | canEdit: false, |
| 119 | }), | 120 | }), |
| 120 | ), | 121 | ); |
| 121 | ), | 122 | return [...merged].sort((a, b) => a.name.localeCompare(b.name)); |
| 123 | }), | ||
| 122 | [result, pending], | 124 | [result, pending], |
| 123 | ); | 125 | ); |
| 124 | }; | 126 | }; |