Fix stale conversation sync labels on mobile
This commit is contained in:
@@ -667,6 +667,10 @@ public class BossApiClient {
|
||||
connection.setDoInput(true);
|
||||
connection.setRequestProperty("Accept", "application/json");
|
||||
connection.setRequestProperty("x-boss-native-app", "1");
|
||||
if ("GET".equals(method)) {
|
||||
connection.setRequestProperty("Cache-Control", "no-cache, no-store, max-age=0");
|
||||
connection.setRequestProperty("Pragma", "no-cache");
|
||||
}
|
||||
|
||||
String cookie = getSessionCookie();
|
||||
if (!cookie.isEmpty()) {
|
||||
|
||||
@@ -51,6 +51,8 @@ public class BossApiClientDispatchPlansTest {
|
||||
assertEquals("GET", connection.requestMethodValue);
|
||||
assertEquals(12000, connection.connectTimeoutValue);
|
||||
assertEquals(30000, connection.readTimeoutValue);
|
||||
assertEquals("no-cache, no-store, max-age=0", connection.getRequestProperty("Cache-Control"));
|
||||
assertEquals("no-cache", connection.getRequestProperty("Pragma"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,6 +69,18 @@ public class BossApiClientDispatchPlansTest {
|
||||
assertEquals(30000, connection.readTimeoutValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConversationHomeSendsNoCacheHeadersToAvoidStaleMobileFeed() 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("no-cache, no-store, max-age=0", connection.getRequestProperty("Cache-Control"));
|
||||
assertEquals("no-cache", connection.getRequestProperty("Pragma"));
|
||||
}
|
||||
|
||||
@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"));
|
||||
|
||||
Binary file not shown.
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"fileName": "boss-android-v2.5.11-release.apk",
|
||||
"urlPath": "/api/v1/user/ota/package",
|
||||
"sizeBytes": 3351549,
|
||||
"updatedAt": "2026-04-06T05:25:05Z",
|
||||
"sha256": "67a66ea104696d639d576738cdef4ef485efb76d12d63b1b138e59dab091ef00",
|
||||
"sizeBytes": 3351826,
|
||||
"updatedAt": "2026-04-07T05:34:00Z",
|
||||
"sha256": "2ea5fd0b33304f04b3f8f0ba66ec8f3ff40e2e8699c1d989de71ba08c9ab7e77",
|
||||
"versionName": "2.5.11",
|
||||
"versionCode": 24,
|
||||
"buildFlavor": "release"
|
||||
|
||||
Binary file not shown.
@@ -180,6 +180,20 @@ export function formatTimestampLabel(value?: string, fallback = "刚刚") {
|
||||
return shanghaiDayFormatter.format(date);
|
||||
}
|
||||
|
||||
const STALE_CONTEXT_SYNC_LABEL = "待同步";
|
||||
const STALE_CONTEXT_REPLY_THRESHOLD_MS = 7 * 24 * 60 * 60_000;
|
||||
|
||||
function formatConversationLatestReplyLabel(value: string, hasVisibleContext: boolean) {
|
||||
if (hasVisibleContext && value.includes("T")) {
|
||||
const date = new Date(value);
|
||||
const diff = Date.now() - date.getTime();
|
||||
if (!Number.isNaN(date.getTime()) && diff >= STALE_CONTEXT_REPLY_THRESHOLD_MS) {
|
||||
return STALE_CONTEXT_SYNC_LABEL;
|
||||
}
|
||||
}
|
||||
return formatTimestampLabel(value);
|
||||
}
|
||||
|
||||
function compareSnapshots(a: ThreadContextSnapshot, b: ThreadContextSnapshot) {
|
||||
if (a.mustFinishBeforeCompaction !== b.mustFinishBeforeCompaction) {
|
||||
return a.mustFinishBeforeCompaction ? -1 : 1;
|
||||
@@ -386,7 +400,10 @@ function buildConversationItem(state: BossState, project: Project): Conversation
|
||||
topPinnedLabel,
|
||||
manualPinned: Boolean(project.pinned && !project.systemPinned),
|
||||
latestReplyAt: latestConversationActivityAt,
|
||||
latestReplyLabel: formatTimestampLabel(latestConversationActivityAt),
|
||||
latestReplyLabel: formatConversationLatestReplyLabel(
|
||||
latestConversationActivityAt,
|
||||
Boolean(topThread),
|
||||
),
|
||||
unreadCount: project.unreadCount,
|
||||
riskLevel: project.riskLevel,
|
||||
activeDeviceCount: devices.length,
|
||||
|
||||
@@ -818,6 +818,43 @@ test("conversation items prefer latest observed codex activity over stale last m
|
||||
assert.equal(thread?.latestReplyLabel, formatTimestampLabel("2026-04-04T11:48:00+08:00"));
|
||||
});
|
||||
|
||||
test("conversation items mark stale context-backed timestamps as waiting for sync", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
||||
state.projects.push({
|
||||
...buildImportedThreadProject(
|
||||
"mac-studio",
|
||||
"stale-context-project",
|
||||
"Boss",
|
||||
"boss",
|
||||
"上下文老化线程",
|
||||
"thread-stale",
|
||||
"2026-03-25T10:58:00+08:00",
|
||||
),
|
||||
preview: "老上下文还在挂着",
|
||||
unreadCount: 1,
|
||||
});
|
||||
state.threadContextSnapshots.push(
|
||||
buildThreadContextSnapshot(
|
||||
"stale-context-project",
|
||||
"thread-stale",
|
||||
"上下文老化线程",
|
||||
"watch",
|
||||
false,
|
||||
58,
|
||||
"2026-03-25T10:58:00+08:00",
|
||||
"mac-studio",
|
||||
),
|
||||
);
|
||||
|
||||
const item = getConversationHomeItems(state).find((entry) => entry.projectId === "stale-context-project");
|
||||
|
||||
assert.ok(item, "expected stale context conversation");
|
||||
assert.equal(item?.contextBudgetIndicator.visible, true);
|
||||
assert.equal(item?.latestReplyLabel, "待同步");
|
||||
});
|
||||
|
||||
test("default seeded conversations no longer expose Boss 移动控制台", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
|
||||
Reference in New Issue
Block a user