mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-11 10:57:07 +00:00
fix(app): keep rewind invalidation for find results after Next and Previous
The rewind marker of the result set was cleared in cancel(), which also stops navigation work. Next and Previous call cancel() while keeping the results, so after one step a remote rewind no longer invalidated them and the selection could stay on a retired message. The marker now shares the result set's lifetime and is cleared only when a new query discards it.
This commit is contained in:
@@ -281,6 +281,79 @@ it("stops a search still receiving pages when a rewind lands between them", asyn
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
["Next", "next"],
|
||||
["Previous", "previous"],
|
||||
] as const)("reports a rewind after %s as a changed history", async (_label, step) => {
|
||||
const model = new ChatFindModel({
|
||||
async search() {
|
||||
return response([
|
||||
{ seq: 1, role: "assistant" },
|
||||
{ seq: 2, role: "assistant" },
|
||||
]);
|
||||
},
|
||||
async load() {},
|
||||
async reveal() {
|
||||
return { count: 1, occurrence: 0 };
|
||||
},
|
||||
clear() {},
|
||||
});
|
||||
try {
|
||||
model.updateHistory("epoch", [message(1), message(2)], null);
|
||||
model.open();
|
||||
model.setQuery("target");
|
||||
await expect.poll(() => model.getSnapshot().phase).toBe("ready");
|
||||
model[step]();
|
||||
await expect.poll(() => model.getSnapshot().selectedItemId).toBe("row-2");
|
||||
await expect.poll(() => model.getSnapshot().phase).toBe("ready");
|
||||
model.updateHistory("epoch", [message(1)], 3);
|
||||
expect(model.getSnapshot()).toMatchObject({
|
||||
phase: "error",
|
||||
failure: "historyChanged",
|
||||
selectedItemId: null,
|
||||
});
|
||||
} finally {
|
||||
model.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("stops a pending Next when a rewind lands before it reveals", async () => {
|
||||
let reveals = 0;
|
||||
let revealSecond!: () => void;
|
||||
const secondReveal = new Promise<void>((resolve) => {
|
||||
revealSecond = resolve;
|
||||
});
|
||||
const model = new ChatFindModel({
|
||||
async search() {
|
||||
return response([
|
||||
{ seq: 1, role: "assistant" },
|
||||
{ seq: 2, role: "assistant" },
|
||||
]);
|
||||
},
|
||||
async load() {},
|
||||
async reveal() {
|
||||
reveals += 1;
|
||||
if (reveals > 1) await secondReveal;
|
||||
return { count: 1, occurrence: 0 };
|
||||
},
|
||||
clear() {},
|
||||
});
|
||||
try {
|
||||
model.updateHistory("epoch", [message(1), message(2)], null);
|
||||
model.open();
|
||||
model.setQuery("target");
|
||||
await expect.poll(() => model.getSnapshot().phase).toBe("ready");
|
||||
model.next();
|
||||
await expect.poll(() => reveals).toBe(2);
|
||||
model.updateHistory("epoch", [message(1)], 3);
|
||||
revealSecond();
|
||||
await expect.poll(() => model.getSnapshot().failure).toBe("historyChanged");
|
||||
expect(model.getSnapshot()).toMatchObject({ phase: "error", selectedItemId: null });
|
||||
} finally {
|
||||
model.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("can load a search location before the initial chat history has arrived", async () => {
|
||||
const model = new ChatFindModel({
|
||||
async search() {
|
||||
|
||||
@@ -78,7 +78,7 @@ export class ChatFindModel {
|
||||
private resultEpoch: string | null = null;
|
||||
/** The latest rewind applied to the history. A rewind keeps the epoch and retires messages. */
|
||||
private rewindSeq: number | null = null;
|
||||
/** The latest rewind when the current search started; undefined while no search runs or holds results. */
|
||||
/** The latest rewind when the current result set's search started; undefined until a search starts. */
|
||||
private searchedRewindSeq: number | null | undefined = undefined;
|
||||
private locations: Location[] = [];
|
||||
private resolved = new Map<Location, Target>();
|
||||
@@ -99,7 +99,6 @@ export class ChatFindModel {
|
||||
this.listeners.forEach((listener) => listener());
|
||||
}
|
||||
private cancel() {
|
||||
this.searchedRewindSeq = undefined;
|
||||
this.abort.abort();
|
||||
this.abort = new AbortController();
|
||||
clearTimeout(this.timer);
|
||||
@@ -118,6 +117,7 @@ export class ChatFindModel {
|
||||
this.cancel();
|
||||
this.locations = [];
|
||||
this.resultEpoch = null;
|
||||
this.searchedRewindSeq = undefined;
|
||||
this.resolved.clear();
|
||||
this.current = -1;
|
||||
this.inMessage = { occurrence: 0, count: 0 };
|
||||
|
||||
Reference in New Issue
Block a user