Strip pin metadata from web conversations
This commit is contained in:
@@ -7,7 +7,7 @@ import {
|
||||
StatusBar,
|
||||
} from "@/components/app-ui";
|
||||
import { requirePageSession } from "@/lib/boss-auth";
|
||||
import { getConversationHomeItems } from "@/lib/boss-projections";
|
||||
import { getConversationWebItems } from "@/lib/boss-projections";
|
||||
import { readState } from "@/lib/boss-data";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
@@ -15,7 +15,7 @@ export const dynamic = "force-dynamic";
|
||||
export default async function ConversationsPage() {
|
||||
await requirePageSession();
|
||||
const state = await readState();
|
||||
const conversations = getConversationHomeItems(state);
|
||||
const conversations = getConversationWebItems(state);
|
||||
|
||||
return (
|
||||
<AppShell>
|
||||
|
||||
@@ -660,6 +660,14 @@ export function getConversationHomeItems(state: BossState): ConversationItem[] {
|
||||
return sortConversationItems(passthrough);
|
||||
}
|
||||
|
||||
export function getConversationWebItems(state: BossState): ConversationItem[] {
|
||||
return getConversationHomeItems(state).map((item) => ({
|
||||
...item,
|
||||
topPinnedLabel: undefined,
|
||||
manualPinned: false,
|
||||
}));
|
||||
}
|
||||
|
||||
export function getConversationHomeItemForProject(state: BossState, projectId: string): ConversationItem | null {
|
||||
const normalizedProjectId = projectId.trim();
|
||||
if (!normalizedProjectId) {
|
||||
|
||||
@@ -8,6 +8,7 @@ let runtimeRoot = "";
|
||||
let readState: (typeof import("../src/lib/boss-data"))["readState"];
|
||||
let updateConversationAction: (typeof import("../src/lib/boss-data"))["updateConversationAction"];
|
||||
let getConversationHomeItems: (typeof import("../src/lib/boss-projections"))["getConversationHomeItems"];
|
||||
let getConversationWebItems: (typeof import("../src/lib/boss-projections"))["getConversationWebItems"];
|
||||
let getConversationHomeItemForProject: (typeof import("../src/lib/boss-projections"))["getConversationHomeItemForProject"];
|
||||
let getConversationThreadItemForProject: (typeof import("../src/lib/boss-projections"))["getConversationThreadItemForProject"];
|
||||
let getConversationFolderView: (typeof import("../src/lib/boss-projections"))["getConversationFolderView"];
|
||||
@@ -31,6 +32,7 @@ async function setup() {
|
||||
readState = data.readState;
|
||||
updateConversationAction = data.updateConversationAction;
|
||||
getConversationHomeItems = projections.getConversationHomeItems;
|
||||
getConversationWebItems = projections.getConversationWebItems;
|
||||
getConversationHomeItemForProject = projections.getConversationHomeItemForProject;
|
||||
getConversationThreadItemForProject = projections.getConversationThreadItemForProject;
|
||||
getConversationFolderView = projections.getConversationFolderView;
|
||||
@@ -770,6 +772,65 @@ test("homepage rows do not expose pinned labels in the Web surface", async () =>
|
||||
assert.equal(getConversationPinnedBadgeLabel(masterAgent!), "");
|
||||
});
|
||||
|
||||
test("web conversation projection strips pin metadata before rendering the Mac/Web surface", async () => {
|
||||
await setup();
|
||||
const state = await readState();
|
||||
|
||||
state.projects = state.projects.filter((project) => project.id === "master-agent");
|
||||
state.projects.push(
|
||||
{
|
||||
...buildImportedThreadProject(
|
||||
"mac-studio",
|
||||
"solo-pinned-thread",
|
||||
"Solo",
|
||||
"solo",
|
||||
"单线程置顶验证",
|
||||
"solo-thread",
|
||||
"2026-03-30T13:00:00+08:00",
|
||||
),
|
||||
pinned: true,
|
||||
},
|
||||
{
|
||||
...buildImportedThreadProject(
|
||||
"mac-studio",
|
||||
"boss-thread-1",
|
||||
"Boss",
|
||||
"boss",
|
||||
"归档确认",
|
||||
"thread-1",
|
||||
"2026-03-30T11:00:00+08:00",
|
||||
),
|
||||
pinned: true,
|
||||
},
|
||||
buildImportedThreadProject(
|
||||
"mac-studio",
|
||||
"boss-thread-2",
|
||||
"Boss",
|
||||
"boss",
|
||||
"发布回滚",
|
||||
"thread-2",
|
||||
"2026-03-30T12:00:00+08:00",
|
||||
),
|
||||
);
|
||||
|
||||
const webItems = getConversationWebItems(state);
|
||||
const pinnedThread = webItems.find((item) => item.projectId === "solo-pinned-thread");
|
||||
const folder = webItems.find((item) => item.conversationType === "folder_archive");
|
||||
const masterAgent = webItems.find((item) => item.projectId === "master-agent");
|
||||
|
||||
assert.ok(pinnedThread, "expected pinned thread in web projection");
|
||||
assert.equal(pinnedThread?.topPinnedLabel, undefined);
|
||||
assert.equal(pinnedThread?.manualPinned, false);
|
||||
|
||||
assert.ok(folder, "expected folder archive in web projection");
|
||||
assert.equal(folder?.topPinnedLabel, undefined);
|
||||
assert.equal(folder?.manualPinned, false);
|
||||
|
||||
assert.ok(masterAgent, "expected master agent in web projection");
|
||||
assert.equal(masterAgent?.topPinnedLabel, undefined);
|
||||
assert.equal(masterAgent?.manualPinned, false);
|
||||
});
|
||||
|
||||
test("folder archive action path encodes folder keys with nested path segments", async () => {
|
||||
await setup();
|
||||
|
||||
|
||||
29
tests/conversation-web-surface.test.ts
Normal file
29
tests/conversation-web-surface.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import path from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const testsDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function readWorkspaceFile(relativePath: string) {
|
||||
return readFile(path.join(testsDir, "..", relativePath), "utf8");
|
||||
}
|
||||
|
||||
test("web conversations page uses a web-specific projection without pin metadata", async () => {
|
||||
const [pageSource, projectionsSource] = await Promise.all([
|
||||
readWorkspaceFile("src/app/conversations/page.tsx"),
|
||||
readWorkspaceFile("src/lib/boss-projections.ts"),
|
||||
]);
|
||||
|
||||
assert.match(
|
||||
projectionsSource,
|
||||
/export function getConversationWebItems\(/,
|
||||
"expected a web-specific conversation projection helper",
|
||||
);
|
||||
assert.match(
|
||||
pageSource,
|
||||
/const conversations = getConversationWebItems\(state\);/,
|
||||
"expected the web conversations page to use the web-specific projection",
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user