Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,17 @@ try {
await dialog.getByText('未找到匹配的委派。', {exact: true}).waitFor();
await dialog.getByRole('button', {name: '清空', exact: true}).click(); await settled();
await rows(12);
await dialog.getByRole('button', {name: '更多记录', exact: true}).click(); await settled();
await page.setViewportSize({width: 390, height: 844});
const more = dialog.getByRole('button', {name: '更多记录', exact: true});
assert.ok(await more.evaluate(node => {
const box = node.getBoundingClientRect();
return box.top >= 0 && box.bottom <= innerHeight;
}), 'The next page must be reachable without scrolling through the current page');
await more.focus(); await page.keyboard.press('Enter'); await settled();
await rows(13);
assert.equal(await page.evaluate(() => document.activeElement?.textContent), '已显示 13 / 13',
'Finishing keyboard pagination keeps focus on its result');
await page.setViewportSize({width: 1440, height: 1000});
await fixture('fault'); await search('reviewer'); await settled();
await dialog.getByRole('alert').waitFor();
await rows(0);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {useEffect, useRef, useState} from "react";
import {ChevronRight, ListChecks, RefreshCw, X} from "lucide-react";
import {ChevronRight, CircleAlert, ListChecks, RefreshCw, X} from "lucide-react";
import {fetchChatSession, fetchChatSessions, requestJson, type ChatImageAttachment, type ChatVisibleMessage, type ChatReplyContext} from "../../data/chat";
import {z} from "zod";
import {fetchConversationRequests, type ConversationRequest, type ConversationRequestInventory} from "../../data/conversation-requests";
Expand Down Expand Up @@ -211,6 +211,9 @@ export function ConversationRequests({sessionId, ownerFallback, refreshKey}: {se
const dialog = useRef<HTMLDialogElement>(null);
const trigger = useRef<HTMLButtonElement>(null);
const requestAbort = useRef<AbortController | null>(null);
const moreButton = useRef<HTMLButtonElement>(null);
const pageCountStatus = useRef<HTMLSpanElement>(null);
const restorePagingFocus = useRef(false);
useEffect(() => {
setOpen(false); setHistory(false); setPages([]); setError(false); setSearch(""); setSearchDraft("");
}, [sessionId]);
Expand Down Expand Up @@ -264,6 +267,14 @@ export function ConversationRequests({sessionId, ownerFallback, refreshKey}: {se
} finally {if (current === generation.current) setBusy(false);}
}
const page = pages[0], last = pages.at(-1);
useEffect(() => {
if (!busy && restorePagingFocus.current) {
restorePagingFocus.current = false;
if (document.activeElement === document.body || document.activeElement === moreButton.current) {
(moreButton.current ?? pageCountStatus.current)?.focus({preventScroll: true});
}
}
}, [busy]);
const unknown = page && (!page.coverage.scan_complete || page.coverage.unreadable > 0);
const rows = [...new Map(pages.flatMap(value => value.rows).map(row => [row.request_id, row])).values()];
const recipients = [...new Set(rows.map(row => `${row.goal_id}\n${row.agent_id}`))];
Expand Down Expand Up @@ -302,14 +313,12 @@ export function ConversationRequests({sessionId, ownerFallback, refreshKey}: {se
</form>
<div className="conversation-requests-body" aria-busy={busy}>
{error ? <p role="alert">{zh ? "委派记录暂时无法核验。" : "Delegation records cannot be verified."} <button type="button" onClick={() => setRevision(value => value + 1)}>{zh ? "重试" : "Retry"}</button></p> : null}
{unknown ? <p role="status">{zh ? "部分记录未能读取,数量与列表尚不完整。" : "Some records could not be read. Counts and list are incomplete."}</p> : null}
{busy && !rows.length ? <p role="status">{zh ? "正在读取委派…" : "Loading delegations…"}</p> : null}
{!busy && noSession ? <p>{zh ? "尚无可读取的管家会话。" : "No readable steward conversation yet."}</p> : null}
{!busy && !noSession && !error && page && !unknown && !rows.length ? <p>{search
? (zh ? "未找到匹配的委派。" : "No matching delegations.")
: history ? (zh ? "暂无委派记录。" : "No delegation records.")
: (zh ? "当前没有待跟进的委派。" : "No delegations awaiting follow-up.")}</p> : null}
{!busy && !error && page && search && rows.length ? <p role="status">{zh ? `匹配 ${page.matched} 条${unknown ? "(部分记录)" : ""}` : `${page.matched} matches${unknown ? " (partial records)" : ""}`}</p> : null}
{recipients.map(recipient => {const [goal, agent] = recipient.split("\n"); return <section key={recipient} className="conversation-requests-group">
<h3>{agent}<span>{goal}</span></h3>
{rows.filter(row => row.goal_id === goal && row.agent_id === agent).map(row => <details key={row.request_id} className="conversation-request-row">
Expand All @@ -333,8 +342,17 @@ export function ConversationRequests({sessionId, ownerFallback, refreshKey}: {se
</div>
</details>)}
</section>;})}
{last?.next_offset !== null && last?.next_offset !== undefined ? <button type="button" disabled={busy} onClick={() => void load(last.next_offset!)}>{zh ? "更多记录" : "More records"}</button> : null}
</div>
{page && !error ? <footer className="conversation-requests-pagination">
<div><span ref={pageCountStatus} tabIndex={-1} role="status" aria-live="polite" aria-atomic="true">{zh
? `已显示 ${rows.length} / ${page.matched}${unknown ? "+" : ""}`
: `${rows.length} / ${page.matched}${unknown ? "+" : ""} shown`}</span>
{unknown ? <ContextHelp icon={CircleAlert} label={zh ? "部分委派无法核验" : "Some delegations cannot be verified"}>
<p>{zh ? "部分记录无法核验,实际数量可能更多。" : "Some records cannot be verified. The actual total may be higher."}</p>
</ContextHelp> : null}
</div>
{last?.next_offset !== null && last?.next_offset !== undefined ? <button ref={moreButton} type="button" aria-disabled={busy} onClick={event => {if (!busy) {restorePagingFocus.current = document.activeElement === event.currentTarget; void load(last.next_offset!);}}}>{zh ? "更多记录" : "More records"}</button> : null}
</footer> : null}
</dialog>
</div>;
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading