feat: review Following during Prepare Today (Closes #1301)
This commit is contained in:
1 parent
875ec45928
commit
12c0e18dfe
19 files changed
+189
-42
No files matched your search
+17
-1
@@ -108,6 +108,19 @@
|
||||
const followingQueue = attachFollowing(item => {
|
||||
searchPreviewReturnKind = 'following';
|
||||
return searchPreview.open(item);
|
||||
}, {
|
||||
onCount:(count, items) => {
|
||||
mobileQueueCounts.following = count;
|
||||
mobileQueueCounts.followingUnavailable = false;
|
||||
mobilePreparationItems.following = items.filter(item => item.has_unseen_change === true);
|
||||
mobileStartDay.render();
|
||||
},
|
||||
onStatus:status => {
|
||||
if (status === 'loading') return;
|
||||
mobileQueueCounts.followingUnavailable = status === 'error';
|
||||
mobileStartDay.render();
|
||||
},
|
||||
onReviewComplete:() => mobileStartDay.completePhase('following'),
|
||||
});
|
||||
const mobileDeliveryRecovery = createMobileDeliveryRecovery({
|
||||
getItems: () => draftInbox.partition(lastDrafts).deliveries,
|
||||
@@ -145,6 +158,7 @@
|
||||
openFindWork: () => qs('#find-work').click(),
|
||||
});
|
||||
function openMobileStartDay() {
|
||||
followingQueue.load().catch(() => {});
|
||||
const state = mobileStartDay.state();
|
||||
mobileStartDay.render();
|
||||
qs('#mobile-queue-heading').textContent = state.active ? 'Resume Prepare Today' : 'Prepare Today';
|
||||
@@ -181,7 +195,9 @@
|
||||
openQueue: name => {
|
||||
const sheet = qs('#mobile-queue-sheet');
|
||||
if (sheet.open) sheet.close();
|
||||
return name === 'find' ? qs('#find-work').click() : mobileQueueLauncher.open(name);
|
||||
return name === 'find' ? qs('#find-work').click() :
|
||||
name === 'following' ? (mobileQueueCounts.followingUnavailable ? followingQueue.open() :
|
||||
followingQueue.review()) : mobileQueueLauncher.open(name);
|
||||
},
|
||||
onHandoff: current => {
|
||||
qs('#mobile-queue-heading').textContent = 'Prepare Today · ' + current.label;
|
||||
|
||||
+15
-2
@@ -80,18 +80,26 @@
|
||||
}
|
||||
|
||||
function finishReview() {
|
||||
const completed = Boolean(review?.active &&
|
||||
!snapshot.items.some(item => item.has_unseen_change === true));
|
||||
if (review) review.active = false;
|
||||
if (completed && !review.completed) {
|
||||
review.completed = true;
|
||||
options.onReviewComplete?.();
|
||||
}
|
||||
publish('ready');
|
||||
return completed;
|
||||
}
|
||||
|
||||
return {
|
||||
load, open, startReview, previewLoaded:acknowledge, finishReview,
|
||||
session:() => review?.active ? {items:[...review.items], more:false} : null,
|
||||
items:() => snapshot.items.map(item => ({...item})),
|
||||
count:() => snapshot.items.length,
|
||||
};
|
||||
}
|
||||
|
||||
function attachFollowing(onOpen) {
|
||||
function attachFollowing(onOpen, hooks = {}) {
|
||||
const document = globalThis.document;
|
||||
const query = selector => document.querySelector(selector);
|
||||
const escapeHtml = value => String(value ?? '').replace(/[&<>"']/g, character =>
|
||||
@@ -105,6 +113,7 @@
|
||||
};
|
||||
let feature;
|
||||
function render(state) {
|
||||
hooks.onStatus?.(state.status);
|
||||
const list = query('#following-list');
|
||||
const status = query('#following-status');
|
||||
const reviewButton = query('#review-following');
|
||||
@@ -138,8 +147,10 @@
|
||||
value.textContent = count;
|
||||
value.closest('button').setAttribute('aria-label', 'Following, ' + count +
|
||||
(count === 1 ? ' unseen change' : ' unseen changes'));
|
||||
hooks.onCount?.(count, feature.items());
|
||||
},
|
||||
onOpen,
|
||||
onReviewComplete:hooks.onReviewComplete,
|
||||
onAcknowledge:item => {
|
||||
const [owner, repo] = item.repository.split('/');
|
||||
return fetchJson('api/v1/following/' + encodeURIComponent(owner) + '/' +
|
||||
@@ -159,6 +170,7 @@
|
||||
query('#retry-following').addEventListener('click', () => feature.load().catch(() => {}));
|
||||
return {
|
||||
load:feature.load,
|
||||
review:feature.startReview,
|
||||
open() {
|
||||
const sheet = query('#following-sheet');
|
||||
if (!sheet.open) sheet.showModal();
|
||||
@@ -168,7 +180,8 @@
|
||||
session:feature.session,
|
||||
previewLoaded:feature.previewLoaded,
|
||||
returnToFollowing() {
|
||||
feature.finishReview();
|
||||
const completed = feature.finishReview();
|
||||
if (completed) return 'completed-following';
|
||||
const sheet = query('#following-sheet');
|
||||
if (!sheet.open) sheet.showModal();
|
||||
globalThis.requestAnimationFrame?.(() => {
|
||||
|
||||
+1
-1
@@ -2003,7 +2003,7 @@
|
||||
<div>
|
||||
<h3 id="mobile-start-day-heading">Prepare Today</h3>
|
||||
<p id="mobile-start-day-summary" class="small" aria-live="polite">Reviewing urgent queues…</p>
|
||||
<p id="mobile-start-day-phases" class="small muted">Checking Agenda, Attention, Updates, and Filed</p>
|
||||
<p id="mobile-start-day-phases" class="small muted">Checking Agenda, Attention, Updates, Filed, and Following</p>
|
||||
</div>
|
||||
<button class="mobile-start-day-action" id="mobile-start-day-action" type="button">Prepare Today</button>
|
||||
<button class="mobile-start-day-finish" id="finish-mobile-start-day" type="button" hidden>Finish for now</button>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
['attention', 'Attention'],
|
||||
['update', 'Updates'],
|
||||
['filed', 'Filed'],
|
||||
['following', 'Following'],
|
||||
];
|
||||
const anonymousItems = new WeakMap();
|
||||
let anonymousItemSequence = 0;
|
||||
@@ -102,19 +103,27 @@
|
||||
|
||||
function briefing() {
|
||||
const counts = options.getCounts ? options.getCounts() : {};
|
||||
const phases = reviewPhases(counts);
|
||||
let phases = reviewPhases(counts);
|
||||
const followingUnavailable = counts.followingUnavailable === true;
|
||||
if (followingUnavailable) {
|
||||
phases = phases.filter(phase => phase.name !== 'following');
|
||||
phases.push({name:'following', label:'Following unavailable', count:count(counts.following)});
|
||||
}
|
||||
const total = phases.reduce((sum, phase) => sum + phase.count, 0);
|
||||
const today = count(counts.today);
|
||||
const delivery = phases.find(phase => phase.name === 'delivery')?.count || 0;
|
||||
const other = total - delivery;
|
||||
const next = phases.length ? phases[0].name : (today ? 'today' : 'find');
|
||||
const nextLabel = next === 'delivery' ? 'Review Delivery' :
|
||||
(phases.length ? 'Review ' + phases[0].label : (today ? 'Continue Today' : 'Find Work'));
|
||||
(next === 'following' && followingUnavailable ? 'Retry Following' :
|
||||
(phases.length ? 'Review ' + phases[0].label : (today ? 'Continue Today' : 'Find Work')));
|
||||
const followingRetry = next === 'following' && followingUnavailable;
|
||||
return {
|
||||
total,
|
||||
next,
|
||||
label: nextLabel,
|
||||
summary: delivery ? delivery + (delivery === 1 ? ' delivery needs' : ' deliveries need') +
|
||||
summary: followingRetry ? 'Following needs retry before Today · ' + today + ' planned' :
|
||||
delivery ? delivery + (delivery === 1 ? ' delivery needs' : ' deliveries need') +
|
||||
' action before Today' + (other ? ' · ' + other + ' other ' + (other === 1 ? 'item' : 'items') : '') +
|
||||
' · ' + today + ' planned' :
|
||||
total ? total + ' items before Today · ' + today + ' planned' :
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const BASE = new URL('./', self.location.href).pathname;
|
||||
importScripts(BASE + 'static/private-data-registry.js');
|
||||
importScripts(BASE + 'static/background-issue-sync.js');
|
||||
const CACHE = 'stackchain-dashboard-shell-v131';
|
||||
const CACHE = 'stackchain-dashboard-shell-v132';
|
||||
const OFFLINE_LEASE_URL = new URL(BASE + '__offline-session-lease', self.location.origin).href;
|
||||
const OUTAGE_STATUSES = new Set([500, 502, 503, 504]);
|
||||
const NAVIGATION_TIMEOUT_MS = self.__STACKCHAIN_NAVIGATION_TIMEOUT_MS || 4000;
|
||||
|
||||
Reference in new issue
Block a user