mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
feat(first-run): render the mission launcher from a store
initFirstRunExperience no longer writes the launcher card's classes, attributes and text, wires its listeners or removes it: it presents the card's visibility, busy state, status line, copy and scroll fade to the gev-first-run store and connects the controls the welcome view's choices, DISMISS and "Don't show this again" call. A refused save hands the tick back to the view to revert. The launcher still reads the rendered card for its hit test, keyboard trap, focus and overflow measurement. The owner-chosen copy moves to src/firstRunCopy.js, shared by both. The source-text tests give way to component tests, and the first-run mutation QA follows the new code (67/67 caught).
This commit is contained in:
1 parent
c03b4a7643
commit
0adcd61184
11 files changed
+447
-179
No files matched your search
@@ -0,0 +1,31 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { markRaw, shallowRef } from 'vue';
|
||||
|
||||
/**
|
||||
* The first-run mission launcher as the welcome view renders it.
|
||||
* initFirstRunExperience presents it and connects the controls its buttons
|
||||
* and checkbox call.
|
||||
*/
|
||||
export const useFirstRunStore = defineStore('gev-first-run', () => {
|
||||
/**
|
||||
* `{removed, shown, visible, closing, state, busy, status, description,
|
||||
* environmentalTitle, environmentalSub, scrollable}`, or null until the
|
||||
* launcher is initialized (the card stays hidden).
|
||||
*/
|
||||
const launcher = shallowRef(null);
|
||||
|
||||
/** @param {?object} next A fresh presentation; every publish re-renders. */
|
||||
function publish(next) {
|
||||
launcher.value = next && markRaw(next);
|
||||
}
|
||||
|
||||
/** `{choose(choice), dismiss(), suppress(wanted) → boolean}`, or null. */
|
||||
const controls = shallowRef(null);
|
||||
|
||||
/** @param {?object} next The launcher's controls, or null to disconnect. */
|
||||
function connect(next) {
|
||||
controls.value = next && markRaw(next);
|
||||
}
|
||||
|
||||
return { launcher, publish, controls, connect };
|
||||
});
|
||||
@@ -1,9 +1,35 @@
|
||||
<script setup>
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { msg } from '../../i18n/index.js';
|
||||
import {
|
||||
FIRST_RUN_TIP,
|
||||
environmentalLabel,
|
||||
} from '../../firstRunCopy.js';
|
||||
import { useFirstRunStore } from '../stores/firstRun.js';
|
||||
|
||||
// The launcher initFirstRunExperience presents, and the controls it connects.
|
||||
const { launcher, controls } = storeToRefs(useFirstRunStore());
|
||||
// The card stays hidden until it is initialized; these show in the markup.
|
||||
const DESCRIPTION = msg(
|
||||
'It feels like a forbidden cockpit—then you realize the sources are public and the data is real.',
|
||||
);
|
||||
const ENVIRONMENTAL_SUB = msg(
|
||||
'Live earthquakes and active fires, from USGS and NASA',
|
||||
);
|
||||
|
||||
// A refused save puts the box back where the stored preference is.
|
||||
function onSuppress(event) {
|
||||
const box = event.currentTarget;
|
||||
if (controls.value?.suppress(box.checked) === false) box.checked = !box.checked;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Explicit mission choice on a fresh session. Live feeds are not auto-enabled
|
||||
until the visitor asks; shared links skip this surface entirely. It returns
|
||||
every session until "Don't show this again" is ticked — see
|
||||
src/firstRunExperience.js for the full show policy. -->
|
||||
<aside id="first-run-launcher" role="dialog" aria-labelledby="first-run-title" aria-describedby="first-run-description" hidden>
|
||||
<aside v-if="!launcher?.removed" id="first-run-launcher" role="dialog" aria-labelledby="first-run-title" aria-describedby="first-run-description" :class="{ visible: launcher?.visible }" :hidden="!launcher?.shown" :aria-hidden="launcher?.closing ? 'true' : undefined" :aria-busy="launcher?.busy ?? undefined" :data-state="launcher?.state ?? undefined">
|
||||
<div class="first-run-scanline" aria-hidden="true"></div>
|
||||
<header class="first-run-header">
|
||||
<span class="first-run-kicker" data-i18n>MISSION CONTROL · FIRST LAUNCH</span>
|
||||
@@ -13,28 +39,28 @@
|
||||
— including the unspaced em dash. The arc it has to carry: this looks
|
||||
like something you should not have access to → the sources are all
|
||||
public → the data is real. Tile subcopy stays purely functional. -->
|
||||
<p id="first-run-description" data-i18n-runtime>It feels like a forbidden cockpit—then you realize the sources are public and the data is real.</p>
|
||||
<div class="first-run-choices ui-scroll">
|
||||
<button type="button" data-first-run-choice="contacts">
|
||||
<p id="first-run-description" data-i18n-runtime>{{ launcher?.description ?? DESCRIPTION }}</p>
|
||||
<div class="first-run-choices ui-scroll" :data-scrollable="launcher?.scrollable ?? undefined">
|
||||
<button type="button" data-first-run-choice="contacts" :aria-disabled="launcher?.busy ?? undefined" @click="controls?.choose('contacts')">
|
||||
<span class="material-symbols-outlined" aria-hidden="true">radar</span>
|
||||
<span><strong data-i18n>LIVE CONTACTS</strong><small data-i18n>Aircraft, vessels and nearby intelligence</small></span>
|
||||
<span class="material-symbols-outlined first-run-arrow" aria-hidden="true">arrow_forward</span>
|
||||
</button>
|
||||
<button type="button" data-first-run-choice="space-missions">
|
||||
<button type="button" data-first-run-choice="space-missions" :aria-disabled="launcher?.busy ?? undefined" @click="controls?.choose('space-missions')">
|
||||
<span class="material-symbols-outlined" aria-hidden="true">rocket_launch</span>
|
||||
<span><strong data-i18n>SPACE MISSIONS</strong><small data-i18n>Launches, spacecraft and orbital context</small></span>
|
||||
<span class="material-symbols-outlined first-run-arrow" aria-hidden="true">arrow_forward</span>
|
||||
</button>
|
||||
<button type="button" data-first-run-choice="environmental">
|
||||
<button type="button" data-first-run-choice="environmental" :aria-disabled="launcher?.busy ?? undefined" @click="controls?.choose('environmental')">
|
||||
<span class="material-symbols-outlined" aria-hidden="true">local_fire_department</span>
|
||||
<!-- Title text is painted from ENVIRONMENTAL_LABEL_CHOICE at init. -->
|
||||
<!-- The title is ENVIRONMENTAL_LABEL_CHOICE's label once initialized. -->
|
||||
<!-- Subcopy names BOTH feeds this tile turns on, because the launcher
|
||||
optimizes for the fully configured experience (owner ruling,
|
||||
2026-08-23) — see the mission table in src/firstRunExperience.js. -->
|
||||
<span data-i18n-runtime><strong data-first-run-environmental-title>ENVIRONMENTAL</strong><small>Live earthquakes and active fires, from USGS and NASA</small></span>
|
||||
<span data-i18n-runtime><strong data-first-run-environmental-title>{{ launcher?.environmentalTitle ?? environmentalLabel().title }}</strong><small>{{ launcher?.environmentalSub ?? ENVIRONMENTAL_SUB }}</small></span>
|
||||
<span class="material-symbols-outlined first-run-arrow" aria-hidden="true">arrow_forward</span>
|
||||
</button>
|
||||
<button type="button" class="first-run-explore" data-first-run-choice="explore">
|
||||
<button type="button" class="first-run-explore" data-first-run-choice="explore" :aria-disabled="launcher?.busy ?? undefined" @click="controls?.choose('explore')">
|
||||
<span class="material-symbols-outlined" aria-hidden="true">public</span>
|
||||
<span><strong data-i18n>EXPLORE MANUALLY</strong><small data-i18n>Begin with a clean globe</small></span>
|
||||
<span class="material-symbols-outlined first-run-arrow" aria-hidden="true">arrow_forward</span>
|
||||
@@ -42,19 +68,19 @@
|
||||
</div>
|
||||
<div class="first-run-footer">
|
||||
<label class="first-run-suppress">
|
||||
<input type="checkbox" data-first-run-suppress />
|
||||
<input type="checkbox" data-first-run-suppress @change="onSuppress" />
|
||||
<span data-i18n>Don't show this again</span>
|
||||
</label>
|
||||
<!-- A button, not just a hint: phones and tablets have no ESC key. It
|
||||
closes exactly as ESC does, for this session only. Touch screens
|
||||
see DISMISS; a keyboard keeps the ESC hint (first-run.css). -->
|
||||
<button type="button" class="first-run-dismiss" data-first-run-dismiss>
|
||||
<button type="button" class="first-run-dismiss" data-first-run-dismiss @click="controls?.dismiss()">
|
||||
<span class="first-run-dismiss-key" data-i18n>ESC to dismiss</span>
|
||||
<span class="first-run-dismiss-touch" data-i18n>DISMISS</span>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Kept out of the footer row: it swaps to mission progress and errors, and
|
||||
a live region that also holds layout makes the row jump as it speaks. -->
|
||||
<p class="first-run-note" data-first-run-status role="status" aria-live="polite" data-i18n>Tip: the GEV MIC button in the dock lets you talk to the map.</p>
|
||||
<p class="first-run-note" data-first-run-status role="status" aria-live="polite">{{ launcher?.status ?? FIRST_RUN_TIP }}</p>
|
||||
</aside>
|
||||
</template>
|
||||
Reference in new issue
Block a user