feat: prefetch the next mobile update (Closes #757)
This commit is contained in:
1 parent
1a3df5d7a7
commit
db08c7e75f
7 files changed
+141
-9
No files matched your search
@@ -912,6 +912,7 @@
|
||||
});
|
||||
const notificationReader = createNotificationReader({
|
||||
load: fetchNotificationDetail,
|
||||
getScope: () => confirmedOwnerLogin,
|
||||
loadConversation: fetchNotificationConversation,
|
||||
markRead: markNotificationRead,
|
||||
acknowledge: acknowledgeNotification,
|
||||
@@ -978,6 +979,9 @@
|
||||
},
|
||||
onStatus: message => {
|
||||
qs('#update-sheet-status').textContent = message;
|
||||
if (message === 'Update ready.' && updateTriage.active() && !offlineWorkMode) {
|
||||
notificationReader.prefetch(updateTriage.next());
|
||||
}
|
||||
qs('#retry-update-load').hidden = !message.startsWith('Could not load update.');
|
||||
if (message === 'Inbox cleared.') {
|
||||
qs('#my-work-action-status').textContent = message;
|
||||
@@ -1001,6 +1005,7 @@
|
||||
progress.textContent = 'Update ' + state.index + ' of ' + state.total;
|
||||
},
|
||||
onFinish: () => {
|
||||
notificationReader.prefetch();
|
||||
qs('#update-triage-progress').hidden = true;
|
||||
showMobileQueueCompletion('Updates');
|
||||
},
|
||||
|
||||
+31
-6
@@ -380,12 +380,27 @@ function createNotificationReader({
|
||||
loadConversation = null,
|
||||
onConversation = () => {},
|
||||
createPager = typeof createConversationPager === 'function' ? createConversationPager : null,
|
||||
getScope = () => '',
|
||||
}) {
|
||||
let selected = null;
|
||||
let loadVersion = 0;
|
||||
let marking = false;
|
||||
let conversationPager = null;
|
||||
let offlineHydrated = false;
|
||||
let prefetched = null;
|
||||
let prefetchKey = '';
|
||||
|
||||
function prefetch(item) {
|
||||
if (!item) { prefetched = null; prefetchKey = ''; return false; }
|
||||
const notificationId = item?.notification_id;
|
||||
if (!Number.isInteger(notificationId) || offlineHydrated) return false;
|
||||
const key = getScope() + notificationId;
|
||||
if (prefetchKey === key) return prefetched;
|
||||
if (prefetched) return false;
|
||||
prefetchKey = key;
|
||||
prefetched = load(notificationId);
|
||||
return prefetched;
|
||||
}
|
||||
|
||||
async function advanceAfterRead(items, current, queueing = false) {
|
||||
const updated = acknowledgeNotification(items, current.notification_id);
|
||||
@@ -407,10 +422,19 @@ function createNotificationReader({
|
||||
selected = item;
|
||||
offlineHydrated = Boolean(savedDetail && !Array.isArray(savedDetail));
|
||||
const version = ++loadVersion;
|
||||
const preload = !offlineHydrated && prefetchKey === getScope() + item.notification_id ? prefetched : null;
|
||||
prefetched = null;
|
||||
prefetchKey = '';
|
||||
onOpen(item);
|
||||
onStatus('Loading update…');
|
||||
if (!preload) onStatus('Loading update…');
|
||||
try {
|
||||
const detail = offlineHydrated ? savedDetail : await load(item.notification_id);
|
||||
let detail;
|
||||
if (offlineHydrated) detail = savedDetail;
|
||||
else if (preload) detail = await preload.catch(() => {
|
||||
onStatus('Loading update…');
|
||||
return load(item.notification_id);
|
||||
});
|
||||
else detail = await load(item.notification_id);
|
||||
if (selected !== item || version !== loadVersion) return false;
|
||||
onDetail(detail);
|
||||
if (createPager && loadConversation && detail.conversation) {
|
||||
@@ -433,6 +457,7 @@ function createNotificationReader({
|
||||
|
||||
return {
|
||||
open,
|
||||
prefetch,
|
||||
commentPager() {
|
||||
return conversationPager;
|
||||
},
|
||||
@@ -516,7 +541,7 @@ function createNotificationReplier({
|
||||
if ((storage.getItem(keyFor(item)) || '') !== body) storage.removeItem(operationKeyFor(item));
|
||||
storage.setItem(keyFor(item), body);
|
||||
}
|
||||
catch (_error) { /* Keep the editable textarea as the fallback. */ }
|
||||
catch (_error) {}
|
||||
},
|
||||
async submit(item, body, attachment = null) {
|
||||
if (pending) return false;
|
||||
@@ -542,15 +567,15 @@ function createNotificationReplier({
|
||||
return { queued:true };
|
||||
}
|
||||
try { storage.removeItem(keyFor(item)); storage.removeItem(operationKeyFor(item)); }
|
||||
catch (_error) { /* Confirmed delivery is authoritative. */ }
|
||||
catch (_error) {}
|
||||
onStatus('Reply posted. You can mark this update read when ready.');
|
||||
return delivery.confirmed[0];
|
||||
}
|
||||
const result = await post(item.notification_id, body, operationId);
|
||||
try { storage.removeItem(keyFor(item)); }
|
||||
catch (_error) { /* The posted reply is still authoritative. */ }
|
||||
catch (_error) {}
|
||||
try { storage.removeItem(operationKeyFor(item)); }
|
||||
catch (_error) { /* A confirmed result no longer needs replay identity. */ }
|
||||
catch (_error) {}
|
||||
onStatus('Reply posted. You can mark this update read when ready.');
|
||||
return result;
|
||||
} catch (error) {
|
||||
|
||||
@@ -35,6 +35,12 @@
|
||||
.map(id => byIdentity.get(id));
|
||||
}
|
||||
|
||||
function nextAvailable() {
|
||||
if (!running || !state) return null;
|
||||
const candidates = available().filter(item => identity(item) !== state.current);
|
||||
return candidates.find(item => state.identities.indexOf(identity(item)) > state.identities.indexOf(state.current)) || candidates[0] || null;
|
||||
}
|
||||
|
||||
function finish() {
|
||||
running = false;
|
||||
state = null;
|
||||
@@ -94,6 +100,7 @@
|
||||
completeAndNext: advance,
|
||||
acceptCompleted: () => advance(false),
|
||||
keepUnreadAndNext: advance,
|
||||
next: nextAvailable,
|
||||
items: () => state ? available().slice() : [],
|
||||
end: finish,
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user