UPG-100 batch HUD: on a phone, the block notice and the print's progress are visible
.upg-status is display:none below 768px (the legend sheet owns the bottom edge),
so the phone ceiling's "This needs a desktop or laptop computer." and the print's
progress ("fetching people groups…", "printing 905 people groups…") were written
only where a phone shows nothing. A reader tapped Prayer List and nothing visible
happened.
hudSay writes a batch action's message to the batch HUD's line (.upg-hud-flash,
now a polite live region, visible at every width) as well as to the status
badge. The block notice stays 6 s and failures 5 s. The print's progress is
mirrored into the same line whenever no flash is up, and is cleared when the
print ends.
check:batch-selection-export arm C checks that the notice, every batch action's
failure text and the progress reach the HUD line, that no batch action writes
via flashStatus alone, and that no rule hides .upg-hud-flash. Four mutants each
fail, including "notice written only to .upg-status".
This commit is contained in:
parent
7853689771
commit
c78a829810
2 changed files with 33 additions and 10 deletions
|
|
@ -256,9 +256,23 @@ const wired = {
|
|||
const body = (name) => { const at = vue.indexOf(`async function ${name}() {`); return at < 0 ? '' : vue.slice(at, vue.indexOf('\n}\n', at)) }
|
||||
const before = (name, call) => { const b = body(name); const g = b.indexOf('phoneTooBig('), p = b.indexOf(call); return g > 0 && p > g }
|
||||
return before('makeSelectionPacket', 'packetPrints.printPacket(') && before('makeSelectionCards', 'packetPrints.printCardDeck(')
|
||||
&& before('makeSelectionPoster', 'packetPrints.printPoster(') && /flashStatus\(t\('poster\.phoneLimitAll'\)/.test(vue)
|
||||
&& before('makeSelectionPoster', 'packetPrints.printPoster(') && /hudSay\(t\('poster\.phoneLimitAll'\), 6000\)/.test(vue)
|
||||
&& /const ON_PHONE = isPhonePrint\(\);/.test(vue)
|
||||
})(),
|
||||
// ★ ON A PHONE .upg-status IS display:none, so a batch message written only there is INVISIBLE (a reader tapped
|
||||
// Prayer List and nothing visible happened). Every batch action's message, and the print's progress, reach the
|
||||
// HUD's line (.upg-hud-flash, a polite live region that no rule hides) as well as the status badge.
|
||||
'batch messages and progress reach the visible HUD on a phone': (() => {
|
||||
const body = (name) => { const at = vue.indexOf(`async function ${name}() {`); return at < 0 ? '' : vue.slice(at, vue.indexOf('\n}\n', at)) }
|
||||
const actions = ['makeSelectionPacket', 'makeSelectionCards', 'makeSelectionPoster'].map(body)
|
||||
const tooBig = vue.slice(vue.indexOf('function phoneTooBig('), vue.indexOf('function phoneTooBig(') + 300)
|
||||
return /const hudSay = \(text, ms\) => \{ flashStatus\(text, ms\); flashRef\(copyFlash, text, ms\); \};/.test(vue)
|
||||
&& /hudSay\(t\('poster\.phoneLimitAll'\)/.test(tooBig) && !/flashStatus\(/.test(tooBig)
|
||||
&& actions.every((b) => b && !/flashStatus\(/.test(b))
|
||||
&& /class="upg-hud-flash" role="status" aria-live="polite">\{\{ copyFlash \|\| hudProgress \}\}/.test(vue)
|
||||
&& /hudProgress\.value = printStatus;/.test(vue)
|
||||
&& !/\.upg-hud-flash\{[^}]*display:\s*none/.test(vue) && !/@media[^{]*\{[^}]*\.upg-hud-flash\{[^}]*display:\s*none/.test(vue)
|
||||
})(),
|
||||
'the regroup warning on Copy': /const warn = regroupable\(sel\) \? '' : t\('upgMap\.copyNotRegroupable'\)/.test(vue),
|
||||
// The packet / card-deck title goes through selectionTitleNames (arm A6 proves it), with the keyed empty case.
|
||||
'the title names come from selectionTitleNames': /const names = selectionTitleNames\(zoneNames, selectedTerritories\.value, from,\s*\n\s*\(\) => keyedError\('wall\.hud\.noRecords'/.test(vue)
|
||||
|
|
|
|||
|
|
@ -1528,14 +1528,15 @@ const packetPrints = usePosterPrints({
|
|||
// watcher wrote is remembered and cleared exactly — the same rule flashRef follows above.
|
||||
let printStatus = ''
|
||||
watch(packetPrints.exportState, (st) => {
|
||||
if (!st) { if (printStatus && status.value === printStatus) status.value = ''; printStatus = ''; return; }
|
||||
if (!st) { if (printStatus && status.value === printStatus) status.value = ''; printStatus = ''; hudProgress.value = ''; return; }
|
||||
if (st.phase === 'error') {
|
||||
printStatus = '';
|
||||
flashStatus(t('messages.packetFailed', { reason: st.message }), 4000);
|
||||
printStatus = ''; hudProgress.value = '';
|
||||
hudSay(t('messages.packetFailed', { reason: st.message }), 5000);
|
||||
return;
|
||||
}
|
||||
printStatus = st.phase === 'done' ? '' : `${st.message}…`;
|
||||
status.value = printStatus;
|
||||
hudProgress.value = printStatus;
|
||||
});
|
||||
|
||||
// BUNDLE-SPECIFIC: who is in this list. ⚠ pg_slugs BYPASSES the shared evaluator —
|
||||
|
|
@ -1576,6 +1577,14 @@ function flashRef(target, text, ms) {
|
|||
setTimeout(() => { if (target.value === text) target.value = ''; }, ms);
|
||||
}
|
||||
const flashStatus = (text, ms) => flashRef(status, text, ms);
|
||||
// ★ A BATCH ACTION SPEAKS WHERE A PHONE CAN SEE IT. .upg-status is display:none below 768px (the legend sheet owns
|
||||
// the bottom edge there), so a message written only to it was invisible on a phone: a reader tapped Prayer List
|
||||
// and nothing visible happened. hudSay writes the same text to the batch HUD's line (.upg-hud-flash, a polite
|
||||
// live region, visible at every width) as well as to the status badge.
|
||||
const hudSay = (text, ms) => { flashStatus(text, ms); flashRef(copyFlash, text, ms); };
|
||||
// The print's progress ("fetching people groups…", "printing 905 people groups…"), mirrored into the HUD for the
|
||||
// same reason; the HUD shows it whenever no flash is up.
|
||||
const hudProgress = ref('');
|
||||
|
||||
// `variant` selects WHICH packet — 'full' (map, life calendar, %-evangelical grid, triangle,
|
||||
// profiles, sets, points, wheel) or 'lightweight' (map, triangle, profiles, points, wheel).
|
||||
|
|
@ -2004,7 +2013,7 @@ async function exportSelectionCsv() {
|
|||
const ON_PHONE = isPhonePrint();
|
||||
function phoneTooBig(count) {
|
||||
if (!overPhoneCeiling(count, ON_PHONE)) return false;
|
||||
flashStatus(t('poster.phoneLimitAll'), 6000);
|
||||
hudSay(t('poster.phoneLimitAll'), 6000);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2031,7 +2040,7 @@ async function makeSelectionPacket() {
|
|||
funnel: funnel && lists.length === 1 ? funnel : (funnel ? { ...funnel, listCount: 0 } : null),
|
||||
});
|
||||
} catch (e) {
|
||||
flashStatus(t('messages.selectionPacketFailed', { reason: errorText(e, t).slice(0, 90) }), 4000);
|
||||
hudSay(t('messages.selectionPacketFailed', { reason: errorText(e, t).slice(0, 90) }), 5000);
|
||||
} finally { batchBusy.value = false; busyAction.value = ''; }
|
||||
}
|
||||
|
||||
|
|
@ -2046,7 +2055,7 @@ async function makeSelectionCards() {
|
|||
if (phoneTooBig(sel.count)) return;
|
||||
await packetPrints.printCardDeck(sel);
|
||||
} catch (e) {
|
||||
flashStatus(t('upgMap.selectionCardsFailed', { reason: errorText(e, t).slice(0, 90) }), 4000);
|
||||
hudSay(t('upgMap.selectionCardsFailed', { reason: errorText(e, t).slice(0, 90) }), 5000);
|
||||
} finally {
|
||||
batchBusy.value = false;
|
||||
busyAction.value = '';
|
||||
|
|
@ -2074,13 +2083,13 @@ async function makeSelectionPoster() {
|
|||
// is exactly the wall's poster; with several, say so plainly instead of silently printing
|
||||
// the first zone's poster as though it covered the selection.
|
||||
if (lists.length > 1) {
|
||||
flashStatus(t('messages.posterOneTerritory', { count: lists.length }), 5000);
|
||||
hudSay(t('messages.posterOneTerritory', { count: lists.length }), 5000);
|
||||
return;
|
||||
}
|
||||
if (phoneTooBig(lists[0].count)) return;
|
||||
await packetPrints.printPoster(lists[0]);
|
||||
} catch (e) {
|
||||
flashStatus(t('messages.selectionPosterFailed', { reason: errorText(e, t).slice(0, 90) }), 4000);
|
||||
hudSay(t('messages.selectionPosterFailed', { reason: errorText(e, t).slice(0, 90) }), 5000);
|
||||
} finally { batchBusy.value = false; busyAction.value = ''; }
|
||||
}
|
||||
|
||||
|
|
@ -3410,7 +3419,7 @@ onBeforeUnmount(() => {
|
|||
@click="exportSelectionCsv">{{ busyAction === 'csv' ? t('buttons.working') : t('upgMap.exportCsv') }}</button>
|
||||
<button class="upg-hud-btn clear" @click="clearSelection">{{ t('buttons.clearSelection') }}</button>
|
||||
</div>
|
||||
<div v-if="selCount" class="upg-hud-flash">{{ copyFlash }}</div>
|
||||
<div v-if="selCount" class="upg-hud-flash" role="status" aria-live="polite">{{ copyFlash || hudProgress }}</div>
|
||||
</div>
|
||||
<div class="upg-status">{{ status }}</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue