human hand off bug
This commit is contained in:
parent
223f846782
commit
6e7a0b9539
22 changed files with 1040 additions and 336 deletions
|
|
@ -6,8 +6,8 @@ import {
|
|||
CircleHelp,
|
||||
Filter,
|
||||
LayoutGrid,
|
||||
List,
|
||||
Mail,
|
||||
MoreHorizontal,
|
||||
RefreshCw,
|
||||
Search,
|
||||
Settings,
|
||||
|
|
@ -46,7 +46,14 @@ import {
|
|||
} from "../utils/inboxSignals";
|
||||
import { buildReplyBodyHtml } from "../utils/emailReply";
|
||||
|
||||
const PAGE_SIZE = 25;
|
||||
const PAGE_SIZE_KEY = "agmail_page_size";
|
||||
const PAGE_SIZE_OPTIONS = [25, 50, 100];
|
||||
|
||||
function readPageSize() {
|
||||
const stored = Number(window.localStorage.getItem(PAGE_SIZE_KEY));
|
||||
return PAGE_SIZE_OPTIONS.includes(stored) ? stored : 50;
|
||||
}
|
||||
|
||||
const CATEGORY_TABS = [
|
||||
{ name: "Primary", icon: LayoutGrid },
|
||||
{ name: "Updates", icon: Bell },
|
||||
|
|
@ -57,10 +64,11 @@ const CATEGORY_TABS = [
|
|||
];
|
||||
|
||||
function resultPage(result) {
|
||||
if (Array.isArray(result)) return { items: result, nextCursor: null };
|
||||
if (Array.isArray(result)) return { items: result, nextCursor: null, total: result.length };
|
||||
return {
|
||||
items: result.items || result.emails || result.results || [],
|
||||
nextCursor: result.next_cursor || result.nextCursor || null,
|
||||
total: Number(result.total || 0),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +120,9 @@ export default function MailboxView({
|
|||
const token = sessionStorage.getItem("agmail_access_token");
|
||||
const [emails, setEmails] = useState([]);
|
||||
const [nextCursor, setNextCursor] = useState(null);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [pageSize, setPageSize] = useState(readPageSize);
|
||||
const [pageMenuOpen, setPageMenuOpen] = useState(false);
|
||||
const [cursor, setCursor] = useState(null);
|
||||
const [cursorHistory, setCursorHistory] = useState([]);
|
||||
const [selected, setSelected] = useState(null);
|
||||
|
|
@ -140,7 +151,7 @@ export default function MailboxView({
|
|||
const result = await listThreads(
|
||||
{
|
||||
mailbox,
|
||||
limit: PAGE_SIZE,
|
||||
limit: pageSize,
|
||||
cursor,
|
||||
q: filters.query.trim(),
|
||||
is_read: filters.read === "all" ? undefined : filters.read === "read",
|
||||
|
|
@ -153,11 +164,12 @@ export default function MailboxView({
|
|||
const page = resultPage(result);
|
||||
setEmails(page.items.map(threadToRow));
|
||||
setNextCursor(page.nextCursor);
|
||||
setTotal(page.total);
|
||||
setStatus({ loading: false, error: "" });
|
||||
} catch (error) {
|
||||
setStatus({ loading: false, error: error.message });
|
||||
}
|
||||
}, [cursor, filters, mailbox, token]);
|
||||
}, [cursor, filters, mailbox, pageSize, token]);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = window.setTimeout(load, filters.query ? 250 : 0);
|
||||
|
|
@ -250,6 +262,22 @@ export default function MailboxView({
|
|||
});
|
||||
}, [emails, onStats]);
|
||||
|
||||
useEffect(() => {
|
||||
setCursor(null);
|
||||
setCursorHistory([]);
|
||||
setChecked({});
|
||||
setPageMenuOpen(false);
|
||||
}, [mailbox]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pageMenuOpen) return undefined;
|
||||
function closeMenu() {
|
||||
setPageMenuOpen(false);
|
||||
}
|
||||
window.addEventListener("click", closeMenu);
|
||||
return () => window.removeEventListener("click", closeMenu);
|
||||
}, [pageMenuOpen]);
|
||||
|
||||
function changeFilter(name, value) {
|
||||
setFilters((current) => ({ ...current, [name]: value }));
|
||||
setCursor(null);
|
||||
|
|
@ -580,6 +608,7 @@ export default function MailboxView({
|
|||
setCursor(nextCursor);
|
||||
setSelected(null);
|
||||
setDetail(null);
|
||||
setChecked({});
|
||||
}
|
||||
|
||||
function previousPage() {
|
||||
|
|
@ -589,11 +618,30 @@ export default function MailboxView({
|
|||
setCursor(previous);
|
||||
setSelected(null);
|
||||
setDetail(null);
|
||||
setChecked({});
|
||||
}
|
||||
|
||||
function changePageSize(size) {
|
||||
if (size === pageSize) {
|
||||
setPageMenuOpen(false);
|
||||
return;
|
||||
}
|
||||
window.localStorage.setItem(PAGE_SIZE_KEY, String(size));
|
||||
setPageSize(size);
|
||||
setCursor(null);
|
||||
setCursorHistory([]);
|
||||
setChecked({});
|
||||
setPageMenuOpen(false);
|
||||
}
|
||||
|
||||
const initials = `${account?.first_name?.[0] || ""}${account?.last_name?.[0] || ""}`.toUpperCase();
|
||||
const rangeStart = listedEmails.length ? cursorHistory.length * PAGE_SIZE + 1 : 0;
|
||||
const rangeEnd = cursorHistory.length * PAGE_SIZE + listedEmails.length;
|
||||
const rangeStart = emails.length ? cursorHistory.length * pageSize + 1 : 0;
|
||||
const rangeEnd = emails.length ? rangeStart + emails.length - 1 : 0;
|
||||
const rangeLabel = emails.length === 0
|
||||
? `0–0 of ${total}`
|
||||
: `${rangeStart}–${rangeEnd} of ${total || rangeEnd}`;
|
||||
const allSelected = listedEmails.length > 0 && selectedCount === listedEmails.length;
|
||||
const someSelected = selectedCount > 0 && !allSelected;
|
||||
|
||||
return (
|
||||
<section className="mailbox-workspace">
|
||||
|
|
@ -652,10 +700,85 @@ export default function MailboxView({
|
|||
<div className="mailbox-main-column">
|
||||
<div className={`mailbox-shell${selected ? " detail-visible" : ""}`}>
|
||||
<div className="mailbox-list-pane">
|
||||
<div className="mailbox-list-bar" role="toolbar" aria-label="Mailbox controls">
|
||||
<div className="mailbox-list-bar-actions">
|
||||
<label className="mailbox-select-all">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={allSelected}
|
||||
ref={(node) => {
|
||||
if (node) node.indeterminate = someSelected;
|
||||
}}
|
||||
aria-label={allSelected ? "Deselect all messages" : "Select all messages"}
|
||||
onChange={toggleSelectAll}
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
className={status.loading ? "is-busy" : ""}
|
||||
title="Refresh"
|
||||
aria-label="Refresh"
|
||||
onClick={load}
|
||||
>
|
||||
<RefreshCw size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<div className="mailbox-page-size-wrap" onClick={(event) => event.stopPropagation()}>
|
||||
<button
|
||||
type="button"
|
||||
title="More options"
|
||||
aria-label="More options"
|
||||
aria-expanded={pageMenuOpen}
|
||||
onClick={() => setPageMenuOpen((open) => !open)}
|
||||
>
|
||||
<MoreHorizontal size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
{pageMenuOpen && (
|
||||
<div className="mailbox-page-size-menu" role="menu">
|
||||
<p>Emails per page</p>
|
||||
{PAGE_SIZE_OPTIONS.map((size) => (
|
||||
<button
|
||||
key={size}
|
||||
type="button"
|
||||
role="menuitemradio"
|
||||
aria-checked={pageSize === size}
|
||||
className={pageSize === size ? "active" : ""}
|
||||
onClick={() => changePageSize(size)}
|
||||
>
|
||||
{size}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mailbox-list-bar-page">
|
||||
<span className="mailbox-pagination-range">{rangeLabel}</span>
|
||||
<button
|
||||
type="button"
|
||||
title="Newer"
|
||||
aria-label="Previous page"
|
||||
disabled={!cursorHistory.length || status.loading}
|
||||
onClick={previousPage}
|
||||
>
|
||||
<ChevronLeft size={18} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
title="Older"
|
||||
aria-label="Next page"
|
||||
disabled={!nextCursor || status.loading}
|
||||
onClick={nextPage}
|
||||
>
|
||||
<ChevronRight size={18} strokeWidth={1.75} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedCount > 0 && (
|
||||
<MailboxSelectionBar
|
||||
count={selectedCount}
|
||||
total={listedEmails.length}
|
||||
hideSelectAll
|
||||
onToggleAll={toggleSelectAll}
|
||||
onArchive={() => runBulkAction("archive")}
|
||||
onSpam={() => runBulkAction("spam")}
|
||||
|
|
@ -755,32 +878,6 @@ export default function MailboxView({
|
|||
))}
|
||||
</div>
|
||||
|
||||
{!status.loading && (
|
||||
<footer className="mailbox-pagination">
|
||||
<span className="mailbox-pagination-range">
|
||||
{listedEmails.length === 0
|
||||
? "0 messages"
|
||||
: `${rangeStart}–${rangeEnd}${nextCursor ? "" : ` · ${rangeEnd} loaded`}`}
|
||||
</span>
|
||||
<div className="mailbox-pagination-controls">
|
||||
{listedEmails.length > 0 && (
|
||||
<>
|
||||
<button type="button" title="Previous page" aria-label="Previous page" disabled={!cursorHistory.length} onClick={previousPage}>
|
||||
<ChevronLeft size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
<button type="button" title="Next page" aria-label="Next page" disabled={!nextCursor} onClick={nextPage}>
|
||||
<ChevronRight size={16} strokeWidth={1.75} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="mailbox-pagination-density" aria-label="List density">
|
||||
<button type="button" className="active" title="Comfortable density" aria-label="Comfortable density"><List size={15} strokeWidth={1.75} /></button>
|
||||
<button type="button" title="Compact density" aria-label="Compact density"><LayoutGrid size={15} strokeWidth={1.75} /></button>
|
||||
</div>
|
||||
</footer>
|
||||
)}
|
||||
|
||||
<MailDetail
|
||||
email={detail}
|
||||
thread={threadDetail}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue