diff --git a/android/app/src/main/java/com/hyzq/boss/BossApiClient.java b/android/app/src/main/java/com/hyzq/boss/BossApiClient.java index a776cba..5891665 100644 --- a/android/app/src/main/java/com/hyzq/boss/BossApiClient.java +++ b/android/app/src/main/java/com/hyzq/boss/BossApiClient.java @@ -90,7 +90,13 @@ public class BossApiClient { } public ApiResponse getConversationHome() throws IOException, JSONException { - return requestWithRestore("GET", "/api/v1/conversations/home", null); + return requestWithRestoreRaw( + "GET", + "/api/v1/conversations/home", + null, + DEFAULT_CONNECT_TIMEOUT_MS, + CONVERSATIONS_READ_TIMEOUT_MS + ); } public ApiResponse getConversationFolder(String folderKey) throws IOException, JSONException { diff --git a/android/app/src/test/java/com/hyzq/boss/BossApiClientDispatchPlansTest.java b/android/app/src/test/java/com/hyzq/boss/BossApiClientDispatchPlansTest.java index e687608..624fae6 100644 --- a/android/app/src/test/java/com/hyzq/boss/BossApiClientDispatchPlansTest.java +++ b/android/app/src/test/java/com/hyzq/boss/BossApiClientDispatchPlansTest.java @@ -53,6 +53,20 @@ public class BossApiClientDispatchPlansTest { assertEquals(30000, connection.readTimeoutValue); } + @Test + public void getConversationHomeUsesExtendedReadTimeoutForSlowHomeFeed() throws Exception { + RecordingConnection connection = new RecordingConnection(new URL("https://boss.hyzq.net/api/v1/conversations/home")); + RecordingBossApiClient apiClient = new RecordingBossApiClient(connection); + + BossApiClient.ApiResponse response = apiClient.getConversationHome(); + + assertEquals(200, response.statusCode); + assertEquals("/api/v1/conversations/home", apiClient.lastPath); + assertEquals("GET", connection.requestMethodValue); + assertEquals(12000, connection.connectTimeoutValue); + assertEquals(30000, connection.readTimeoutValue); + } + @Test public void confirmDispatchPlanWritesApprovedTargetProjectIds() throws Exception { RecordingConnection connection = new RecordingConnection(new URL("https://boss.hyzq.net/api/v1/projects/p1/dispatch-plans/plan-1/confirm")); @@ -65,7 +79,10 @@ public class BossApiClientDispatchPlansTest { assertEquals("POST", connection.requestMethodValue); assertEquals(12000, connection.connectTimeoutValue); assertEquals(65000, connection.readTimeoutValue); - assertEquals("{\"approvedTargetProjectIds\":[\"target-1\",\"target-2\"]}", connection.requestBody()); + assertEquals( + "{\"approvedTargetProjectIds\":[\"target-1\",\"target-2\"],\"rememberLightReminder\":false}", + connection.requestBody() + ); } @Test diff --git a/src/lib/boss-projections.ts b/src/lib/boss-projections.ts index e22e7f9..61dbcec 100644 --- a/src/lib/boss-projections.ts +++ b/src/lib/boss-projections.ts @@ -476,7 +476,6 @@ function buildFolderSearchAliases(items: ConversationItem[]) { if (!alias || seen.has(alias)) continue; aliases.push(alias); seen.add(alias); - if (aliases.length >= 4) break; } return aliases.length > 0 ? aliases : undefined; } diff --git a/tests/conversation-home-items.test.ts b/tests/conversation-home-items.test.ts index f484776..c5edf5f 100644 --- a/tests/conversation-home-items.test.ts +++ b/tests/conversation-home-items.test.ts @@ -436,7 +436,7 @@ test("folder archive homepage rows keep the project title, compact subtitle, and assert.equal(presentation.href, "/conversations/folders/mac-studio%3Aboss"); }); -test("folder archive search aliases stay bounded to the latest thread titles", async () => { +test("folder archive search aliases keep full reachability across five threads", async () => { await setup(); const state = await readState(); @@ -452,7 +452,31 @@ test("folder archive search aliases stay bounded to the latest thread titles", a const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive"); assert.ok(folder, "expected grouped folder archive item"); - assert.deepEqual(folder?.searchAliases, ["发布回滚", "Android UI 收尾", "日志收口", "网络修复"]); + assert.deepEqual(folder?.searchAliases, [ + "发布回滚", + "Android UI 收尾", + "日志收口", + "网络修复", + "审阅确认", + ]); +}); + +test("folder archive search aliases still dedupe when multiple threads share the same title", async () => { + await setup(); + const state = await readState(); + + state.projects = state.projects.filter((project) => project.id === "master-agent"); + state.projects.push( + buildImportedThreadProject("mac-studio", "boss-thread-1", "Boss", "boss", "发布回滚", "thread-1", "2026-03-30T14:00:00+08:00"), + buildImportedThreadProject("mac-studio", "boss-thread-2", "Boss", "boss", "发布回滚", "thread-2", "2026-03-30T13:00:00+08:00"), + buildImportedThreadProject("mac-studio", "boss-thread-3", "Boss", "boss", "日志收口", "thread-3", "2026-03-30T12:00:00+08:00"), + buildImportedThreadProject("mac-studio", "boss-thread-4", "Boss", "boss", "网络修复", "thread-4", "2026-03-30T11:00:00+08:00"), + ); + + const folder = getConversationHomeItems(state).find((item) => item.conversationType === "folder_archive"); + + assert.ok(folder, "expected grouped folder archive item"); + assert.deepEqual(folder?.searchAliases, ["发布回滚", "日志收口", "网络修复"]); }); test("conversation items expose context status while keeping idle activity silent", async () => {