Prefetch adjacent emails while reading
This commit is contained in:
@@ -1285,17 +1285,24 @@ def setup_email_routes():
|
||||
logger.debug(f"mark-seen after cached read failed uid={uid}: {e}")
|
||||
|
||||
@router.get("/read/{uid}")
|
||||
async def read_email_by_uid(uid: str, folder: str = Query("INBOX"), account_id: str | None = Query(None), owner: str = Depends(require_owner)):
|
||||
async def read_email_by_uid(
|
||||
uid: str,
|
||||
folder: str = Query("INBOX"),
|
||||
account_id: str | None = Query(None),
|
||||
mark_seen: bool = Query(True),
|
||||
owner: str = Depends(require_owner),
|
||||
):
|
||||
"""Read email body. Cached for 30m, sync IMAP work runs in a thread."""
|
||||
ck = _read_cache_key(account_id, folder, uid, owner=owner)
|
||||
cached = _read_cache_get(ck)
|
||||
if cached is not None:
|
||||
try:
|
||||
_asyncio.create_task(_asyncio.to_thread(_mark_email_seen_sync, uid, folder, account_id, owner))
|
||||
except RuntimeError:
|
||||
pass
|
||||
if mark_seen:
|
||||
try:
|
||||
_asyncio.create_task(_asyncio.to_thread(_mark_email_seen_sync, uid, folder, account_id, owner))
|
||||
except RuntimeError:
|
||||
pass
|
||||
return cached
|
||||
result = await _asyncio.to_thread(_read_email_sync, uid, folder, account_id, owner)
|
||||
result = await _asyncio.to_thread(_read_email_sync, uid, folder, account_id, owner, mark_seen)
|
||||
if result and not result.get("error"):
|
||||
_read_cache_put(ck, result)
|
||||
return result
|
||||
|
||||
@@ -1788,6 +1788,34 @@ function _syncCardNavArrows(card) {
|
||||
if (next) next.disabled = !_findSiblingEmailCard(card, 1);
|
||||
}
|
||||
|
||||
const _emailReadPrefetching = new Set();
|
||||
|
||||
function _prefetchAdjacentEmails(card, count = 3) {
|
||||
if (!card || state._libFolder === '__scheduled__') return;
|
||||
const grid = card.closest('.doclib-grid');
|
||||
if (!grid) return;
|
||||
const cards = [...grid.querySelectorAll('.doclib-card[data-uid]')];
|
||||
const idx = cards.indexOf(card);
|
||||
if (idx === -1) return;
|
||||
const targets = [];
|
||||
for (let i = 1; i <= count; i++) {
|
||||
if (cards[idx + i]) targets.push(cards[idx + i]);
|
||||
}
|
||||
if (targets.length < count) {
|
||||
for (let i = 1; targets.length < count && cards[idx - i]; i++) targets.push(cards[idx - i]);
|
||||
}
|
||||
for (const target of targets) {
|
||||
const uid = target.dataset.uid;
|
||||
if (!uid) continue;
|
||||
const key = `${state._libAccountId || ''}|${state._libFolder}|${uid}`;
|
||||
if (_emailReadPrefetching.has(key)) continue;
|
||||
_emailReadPrefetching.add(key);
|
||||
fetch(`${API_BASE}/api/email/read/${encodeURIComponent(uid)}?folder=${encodeURIComponent(state._libFolder)}${_acct()}&mark_seen=false`)
|
||||
.catch(() => {})
|
||||
.finally(() => _emailReadPrefetching.delete(key));
|
||||
}
|
||||
}
|
||||
|
||||
async function _toggleCardPreview(card, em) {
|
||||
const grid = card.closest('.doclib-grid');
|
||||
|
||||
@@ -1843,6 +1871,7 @@ async function _toggleCardPreview(card, em) {
|
||||
|
||||
// Mark as read locally
|
||||
_syncEmailReadState(em.uid, true);
|
||||
_prefetchAdjacentEmails(card);
|
||||
|
||||
// Build the attachments wrap using the shared helper so the signature-
|
||||
// image filter (small inline PNGs/JPGs, Outlook image001 placeholders,
|
||||
|
||||
Reference in New Issue
Block a user