authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-02 22:36:35-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-02 22:36:35-04:00
log31eeb443305a4e11c8b20a7c8ff2b3f5e841eb0f
treee0f34f12a9f23b1dfef59e69c8639c1b37b55bed
parent9ebbeda5a03fd8fce7be147b65ea396e50721c68
signature Signed by PGP key B5690EEEBB952194

fix(sidebar): collapse settled and snoozed shelves by default (#9314)

Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>

4 files changed, 31 insertions(+), 30 deletions(-)

apps/mobile/src/features/threads/use-thread-list-v2-shelf-preferences.ts+4-4
......@@ -14,9 +14,9 @@ export function useThreadListV2ShelfPreferences() {
1414 const savePreferences = useAtomSet(updateMobilePreferencesAtom);
1515 const loaded = AsyncResult.isSuccess(preferencesResult);
1616 const snoozedShelfExpanded =
17 loaded && preferencesResult.value.threadListV2SnoozedShelfExpanded === true;
17 loaded && preferencesResult.value.threadListSnoozedShelfExpanded === true;
1818 const settledShelfExpanded =
19 !loaded || preferencesResult.value.threadListV2SettledShelfExpanded !== false;
19 loaded && preferencesResult.value.threadListSettledShelfExpanded === true;
2020 const snoozedShelfExpandedRef = useRef(snoozedShelfExpanded);
2121 const settledShelfExpandedRef = useRef(settledShelfExpanded);
2222 snoozedShelfExpandedRef.current = snoozedShelfExpanded;
......@@ -26,13 +26,13 @@ export function useThreadListV2ShelfPreferences() {
2626 if (!loaded) return;
2727 const expanded = !snoozedShelfExpandedRef.current;
2828 snoozedShelfExpandedRef.current = expanded;
29 savePreferences({ threadListV2SnoozedShelfExpanded: expanded });
29 savePreferences({ threadListSnoozedShelfExpanded: expanded });
3030 }, [loaded, savePreferences]);
3131 const toggleSettledShelf = useCallback(() => {
3232 if (!loaded) return;
3333 const expanded = !settledShelfExpandedRef.current;
3434 settledShelfExpandedRef.current = expanded;
35 savePreferences({ threadListV2SettledShelfExpanded: expanded });
35 savePreferences({ threadListSettledShelfExpanded: expanded });
3636 }, [loaded, savePreferences]);
3737
3838 return {
apps/mobile/src/lib/storage.test.ts+14-12
......@@ -213,33 +213,35 @@ describe("mobile connection storage", () => {
213213 expect(fallback.updatedAt).toEqual(expect.any(Number));
214214 });
215215
216 it("persists Thread List v2 shelf expansion preferences", async () => {
216 it("persists thread list shelf expansion preferences", async () => {
217217 await expect(
218218 savePreferencesPatch({
219 threadListV2SettledShelfExpanded: false,
220 threadListV2SnoozedShelfExpanded: true,
219 threadListSettledShelfExpanded: false,
220 threadListSnoozedShelfExpanded: true,
221221 }),
222222 ).resolves.toEqual({
223 threadListV2SettledShelfExpanded: false,
224 threadListV2SnoozedShelfExpanded: true,
223 threadListSettledShelfExpanded: false,
224 threadListSnoozedShelfExpanded: true,
225225 });
226226
227227 await expect(loadPreferences()).resolves.toEqual({
228 threadListV2SettledShelfExpanded: false,
229 threadListV2SnoozedShelfExpanded: true,
228 threadListSettledShelfExpanded: false,
229 threadListSnoozedShelfExpanded: true,
230230 });
231231 expect(JSON.parse(mocks.getPreferencesJson() ?? "")).toEqual({
232 threadListV2SettledShelfExpanded: false,
233 threadListV2SnoozedShelfExpanded: true,
232 threadListSettledShelfExpanded: false,
233 threadListSnoozedShelfExpanded: true,
234234 });
235235 });
236236
237 it("ignores invalid Thread List v2 shelf expansion preference types", async () => {
237 it("drops legacy and invalid thread list shelf expansion preferences", async () => {
238238 mocks.setPreferencesJson(
239239 JSON.stringify({
240240 baseFontSize: 17,
241 threadListV2SettledShelfExpanded: "false",
242 threadListV2SnoozedShelfExpanded: 1,
241 threadListV2SettledShelfExpanded: true,
242 threadListV2SnoozedShelfExpanded: true,
243 threadListSettledShelfExpanded: "false",
244 threadListSnoozedShelfExpanded: 1,
243245 }),
244246 10,
245247 );
apps/mobile/src/persistence/mobile-preferences.ts+9-10
......@@ -41,10 +41,9 @@ export interface Preferences {
4141 readonly legacyThreadListEnabled?: boolean;
4242 /** Device-local counterpart of desktop's `planModeEnabled` legacy flag. */
4343 readonly planModeEnabled?: boolean;
44 /** Undefined preserves the default expanded Settled shelf. */
45 readonly threadListV2SettledShelfExpanded?: boolean;
46 /** Undefined preserves the default collapsed Snoozed shelf. */
47 readonly threadListV2SnoozedShelfExpanded?: boolean;
44 /** Fresh keys reset both shelves to collapsed when users update. */
45 readonly threadListSettledShelfExpanded?: boolean;
46 readonly threadListSnoozedShelfExpanded?: boolean;
4847}
4948
5049export class MobilePreferencesLoadError extends Schema.TaggedErrorClass<MobilePreferencesLoadError>()(
......@@ -102,8 +101,8 @@ function sanitizePreferences(parsed: Preferences): Preferences {
102101 projectGroupingMode?: SidebarProjectGroupingMode;
103102 legacyThreadListEnabled?: boolean;
104103 planModeEnabled?: boolean;
105 threadListV2SettledShelfExpanded?: boolean;
106 threadListV2SnoozedShelfExpanded?: boolean;
104 threadListSettledShelfExpanded?: boolean;
105 threadListSnoozedShelfExpanded?: boolean;
107106 } = {};
108107
109108 if (typeof parsed.liveActivitiesEnabled === "boolean") {
......@@ -171,11 +170,11 @@ function sanitizePreferences(parsed: Preferences): Preferences {
171170 if (typeof parsed.planModeEnabled === "boolean") {
172171 preferences.planModeEnabled = parsed.planModeEnabled;
173172 }
174 if (typeof parsed.threadListV2SettledShelfExpanded === "boolean") {
175 preferences.threadListV2SettledShelfExpanded = parsed.threadListV2SettledShelfExpanded;
173 if (typeof parsed.threadListSettledShelfExpanded === "boolean") {
174 preferences.threadListSettledShelfExpanded = parsed.threadListSettledShelfExpanded;
176175 }
177 if (typeof parsed.threadListV2SnoozedShelfExpanded === "boolean") {
178 preferences.threadListV2SnoozedShelfExpanded = parsed.threadListV2SnoozedShelfExpanded;
176 if (typeof parsed.threadListSnoozedShelfExpanded === "boolean") {
177 preferences.threadListSnoozedShelfExpanded = parsed.threadListSnoozedShelfExpanded;
179178 }
180179 return preferences;
181180}
apps/web/src/components/Sidebar.tsx+4-4
......@@ -211,9 +211,9 @@ import {
211211// stays behind an explicit Show more.
212212const SETTLED_TAIL_INITIAL_COUNT = 10;
213213const SETTLED_TAIL_PAGE_COUNT = 25;
214// Keep the v2 key so existing preferences survive the v2-to-default rename.
215const SETTLED_SHELF_EXPANDED_KEY = "t3code:sidebar-v2:settled-expanded";
216const SNOOZED_SHELF_EXPANDED_KEY = "t3code:sidebar-v2:snoozed-expanded";
214// Fresh keys deliberately reset both shelves to collapsed for existing users.
215const SETTLED_SHELF_EXPANDED_KEY = "t3code:sidebar:settled-expanded";
216const SNOOZED_SHELF_EXPANDED_KEY = "t3code:sidebar:snoozed-expanded";
217217
218218function compactSidebarTimeLabel(label: string): string {
219219 if (label === "just now") return "now";
......@@ -2274,7 +2274,7 @@ export default function Sidebar() {
22742274 );
22752275 const [settledShelfExpanded, setSettledShelfExpanded] = useLocalStorage(
22762276 SETTLED_SHELF_EXPANDED_KEY,
2277 true,
2277 false,
22782278 Schema.Boolean,
22792279 );
22802280 const toggleSettledShelf = useCallback(