feat: open focused blockers from Today HUD (Closes #1070)
CI / lint (pull_request) Successful in 2m41s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 2m14s
CI / release-candidate (pull_request) Has been skipped

This commit is contained in:
timmy committed 2026-08-18 08:25:27 +00:00
1 parent 027ded1f8f
commit f710b2ce1d
5 files changed
+135 -13

No files matched your search

+11 -1
View File
@@ -290,6 +290,8 @@ textarea { resize: vertical; min-height: 120px; }
.today-progress-evidence .issue-evidence-note textarea { min-height:72px; }
.today-progress-blocker { display:grid; gap:8px; margin-top:14px; padding:12px; border:1px solid #875f2a; border-radius:12px; background:#21180d; }
.today-progress-blocker input, .today-progress-blocker button { box-sizing:border-box; width:100%; min-height:44px; }
.today-blocker-presets { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
.today-blocker-presets button { min-width:0; padding-inline:6px; }
.today-progress-blocker p { margin:0; }
.today-progress-blocker .today-progress-blocker-recovery { padding:8px; border-radius:8px; background:#3a270d; color:#fde68a; font-weight:700; }
.today-progress-actions { display:grid; grid-template-columns:1fr 1fr; gap:8px; margin-top:12px; }
@@ -297,6 +299,13 @@ textarea { resize: vertical; min-height: 120px; }
.today-progress-panel .voice-conversation { margin-top:10px; }
.today-progress-panel .voice-conversation-controls button,
.today-progress-panel .voice-conversation-review-actions button { min-height:44px; }
.today-progress-panel.blocker-mode .today-progress-activity,
.today-progress-panel.blocker-mode .voice-conversation,
.today-progress-panel.blocker-mode .today-progress-evidence,
.today-progress-panel.blocker-mode .today-progress-update-help,
.today-progress-panel.blocker-mode .today-progress-actions { display:none; }
.today-progress-panel.blocker-mode textarea { min-height:112px; }
@media(max-width:359px) { .today-blocker-presets { grid-template-columns:1fr; } }
@media(max-width:359px) { .today-progress-actions { grid-template-columns:1fr; } }
.today-recap-sheet { position:fixed; inset:0; z-index:88; display:flex; align-items:flex-end; justify-content:center; background:rgba(5,12,21,.82); backdrop-filter:blur(4px); }
.today-recap-sheet[hidden] { display:none; }
@@ -1116,7 +1125,8 @@ textarea { resize: vertical; min-height: 120px; }
.mobile-today-hud [data-mobile-today-complete] { grid-area:complete; }
.mobile-today-hud [data-mobile-today-toggle] { grid-area:toggle; }
.mobile-today-hud button { min-height:44px; max-width:100%; }
.mobile-today-hud [data-mobile-today-update] { grid-column:1 / -1; width:100%; }
.mobile-today-hud [data-mobile-today-update] { grid-column:1; width:100%; }
.mobile-today-hud [data-mobile-today-blocked] { grid-column:2; width:100%; }
.mobile-today-hud [data-today-break-open] { grid-column:1 / -1; }
.mobile-today-hud [data-work-session-adjust-plan] { grid-column:1 / -1; }
+8 -2
View File
@@ -1537,6 +1537,7 @@
<div class="small" data-work-session-progress aria-live="polite"></div>
<button data-mobile-today-toggle data-work-session-timer-toggle type="button">Pause timer</button>
<button data-mobile-today-update type="button" hidden>Add update</button>
<button class="mobile-today-blocked" data-mobile-today-blocked type="button" hidden>Blocked</button>
<button data-today-break-open type="button">Take break</button>
<button data-work-session-adjust-plan type="button" hidden>Adjust remaining plan</button>
</section>
@@ -1556,7 +1557,7 @@
<button id="load-older-today-progress-activity" type="button" hidden>Load older</button>
</div>
</section>
<label for="today-progress-body">Update</label>
<label id="today-progress-body-label" for="today-progress-body">Update</label>
<textarea id="today-progress-body" rows="5" maxlength="2000" placeholder="What changed, what you learned, or what comes next"></textarea>
<div class="mention-options" id="today-progress-body-mentions" role="listbox" aria-label="Teammates" hidden></div>
<div class="mention-status small" id="today-progress-body-mention-status" aria-live="polite"></div>
@@ -1600,12 +1601,17 @@
</div>
</div>
</section>
<p class="small muted">Save privately on this device, or post to the exact Gitea item. Today keeps running either way.</p>
<p class="small muted today-progress-update-help">Save privately on this device, or post to the exact Gitea item. Today keeps running either way.</p>
<section class="today-progress-blocker" aria-labelledby="today-progress-blocker-title">
<strong id="today-progress-blocker-title">Blocked?</strong>
<p id="today-progress-blocker-recovery" class="today-progress-blocker-recovery small" role="status" hidden></p>
<label for="today-blocker-return-at">Return this item to Later at</label>
<input id="today-blocker-return-at" type="datetime-local" />
<div class="today-blocker-presets" aria-label="Return time choices">
<button data-today-blocker-preset="later-today" type="button">Later today</button>
<button data-today-blocker-preset="tomorrow" type="button">Tomorrow</button>
<button data-today-blocker-custom type="button">Choose time</button>
</div>
<button id="post-today-blocker" type="button">Post blocker &amp; move on</button>
<p class="small muted">Posts this update first, then defers the item and continues Today.</p>
</section>
+37 -10
View File
@@ -309,11 +309,15 @@ function mountTodayProgressActivity(qs, fetchJson, actionSource = {}, render) {
return activity;
}
function createTodayProgressView({ progress, currentTarget, qs, photos, voice, activity, mentions, announce = () => {}, onAdmitted = () => {}, moveOn = null }) {
function createTodayProgressView({ progress, currentTarget, qs, photos, voice, activity, mentions, announce = () => {}, onAdmitted = () => {}, moveOn = null, now = () => new Date() }) {
const sheet = qs('#today-progress-sheet');
const panel = qs('.today-progress-panel');
const title = qs('#today-progress-title');
const body = qs('#today-progress-body');
const bodyLabel = qs('#today-progress-body-label');
const status = qs('#today-progress-status');
const launcher = qs('[data-mobile-today-update]');
const blockerLauncher = qs('[data-mobile-today-blocked]');
const blockerButton = qs('#post-today-blocker');
const blockerReturn = qs('#today-blocker-return-at');
const blockerRecovery = qs('#today-progress-blocker-recovery');
@@ -342,6 +346,7 @@ function createTodayProgressView({ progress, currentTarget, qs, photos, voice, a
const update = () => {
const target = currentTarget();
launcher.hidden = !target;
if (blockerLauncher) blockerLauncher.hidden = !target;
if (openedTarget && target?.identity !== openedTarget.identity) mentions?.dismiss?.();
};
const checkpoint = async () => {
@@ -362,28 +367,50 @@ function createTodayProgressView({ progress, currentTarget, qs, photos, voice, a
activity?.close?.();
mentions?.dismiss?.();
if (sheet.open) sheet.close();
panel?.classList?.remove('blocker-mode');
openedTarget = null;
};
launcher.addEventListener('click', async () => {
const open = async blockerMode => {
const target = currentTarget();
if (!target) return;
mentions?.dismiss?.();
openedTarget = target;
panel?.classList?.[blockerMode ? 'add' : 'remove']('blocker-mode');
if (title) title.textContent = blockerMode ? 'Report blocker' : 'Add progress update';
if (bodyLabel) bodyLabel.textContent = blockerMode ? 'Blocker description' : 'Update';
qs('#today-progress-target').textContent = target.label + (target.title ? ' · ' + target.title : '');
body.value = progress.load(target.identity);
showBlockerRecovery(progress.blockerRecovery?.(target.identity));
status.textContent = 'Restoring saved photo evidence…';
sheet.showModal();
try {
await voice?.open?.(target.identity);
await photos?.open?.(target);
await activity?.open?.(target);
status.textContent = '';
if (!blockerMode) {
status.textContent = 'Restoring saved photo evidence…';
try {
await voice?.open?.(target.identity);
await photos?.open?.(target);
await activity?.open?.(target);
status.textContent = '';
}
catch (error) { status.textContent = error.message + ' You can retry by reopening this update.'; }
}
catch (error) { status.textContent = error.message + ' You can retry by reopening this update.'; }
else status.textContent = '';
body.focus();
});
};
launcher.addEventListener('click', () => open(false));
blockerLauncher?.addEventListener('click', () => open(true));
const setReturn = preset => {
const date = new Date(now());
if (preset === 'later-today') date.setHours(date.getHours() + 4);
else {
date.setDate(date.getDate() + 1);
date.setHours(9, 0, 0, 0);
}
blockerReturn.value = localDateTime(date);
};
qs('[data-today-blocker-preset="later-today"]')?.addEventListener('click', () => setReturn('later-today'));
qs('[data-today-blocker-preset="tomorrow"]')?.addEventListener('click', () => setReturn('tomorrow'));
qs('[data-today-blocker-custom]')?.addEventListener('click', () => blockerReturn?.focus?.());
qs('#cancel-today-progress').addEventListener('click', async () => {
if (await checkpoint()) close();
});