authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-07 22:48:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-07 22:48:54-07:00
log12a4c057f3d6880c611630ccdc0c8c7f2cd23f72
tree9f3fc7847db1b955e9fd6b765d651f81d7c5d00d
parent70eeb3996d557a2e553f817c8e123bf79bbd378c
signature Signed by PGP key B5690EEEBB952194

fix(mobile): skip move indexes for empty and single-thread sections (#16115)


2 files changed, 33 insertions(+), 0 deletions(-)

apps/mobile/src/features/threads/threadOrder.ts+5
......@@ -134,6 +134,11 @@ export function computeThreadMoveAvailability(input: {
134134 // A reorder in flight locks the whole list until its receipt lands.
135135 if (input.pendingOrder != null) return result;
136136 const rows = input.ordered;
137 // With no neighbor there is no up/down move, so skip the allThreads indexes.
138 if (rows.length < 2) {
139 for (const row of rows) result.set(rowId(row), { canMoveUp: false, canMoveDown: false });
140 return result;
141 }
137142 const orderedIds = rows.map(rowId);
138143 const indexById = new Map(orderedIds.map((id, index) => [id, index] as const));
139144 const keysById = new Map(
apps/mobile/src/features/threads/threadOrderAvailability.test.ts+28
......@@ -208,6 +208,34 @@ describe("computeThreadMoveAvailability matches the reference planner", () => {
208208 expect(batch.get("env-w:t0")).toEqual({ canMoveUp: false, canMoveDown: false });
209209 });
210210
211 it("answers empty and single-row sections without consulting hidden rows", () => {
212 const hidden = [makeRow("h0", "env-w", "b", true), makeRow("h1", "env-x", null, true)];
213 expect(
214 computeThreadMoveAvailability({
215 ordered: [],
216 allThreads: hidden,
217 section: "pinned",
218 reorderableEnvironmentIds: WRITABLE,
219 }).size,
220 ).toBe(0);
221 for (const environment of ["env-w", "env-x"]) {
222 const ordered = [makeRow("t0", environment, "a", true)];
223 const allThreads = [...ordered, ...hidden];
224 const batch = computeThreadMoveAvailability({
225 ordered,
226 allThreads,
227 section: "pinned",
228 reorderableEnvironmentIds: WRITABLE,
229 });
230 expect(Object.fromEntries(batch)).toEqual({
231 [`${environment}:t0`]: { canMoveUp: false, canMoveDown: false },
232 });
233 expect(Object.fromEntries(batch)).toEqual(
234 Object.fromEntries(referenceAvailability(ordered, allThreads, WRITABLE)),
235 );
236 }
237 });
238
211239 it("denies rows whose section has non-writable neighbours when the fast path fails", () => {
212240 // Keyless neighbors force the section-rewrite fallback; a non-writable
213241 // neighbor makes the rewrite illegal for every row in the section.