@simon-says-build/demo-player 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/DemoHost.js +9 -2
- package/src/core.js +28 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simon-says-build/demo-player",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "SimonSays mobile demo player: walks a preview's viewer through a job's change, inside the app. Renders nothing unless the bundle carries a demo.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
package/src/DemoHost.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
16
|
import { Linking, Pressable, ScrollView, StyleSheet, Text, TextInput, View, useWindowDimensions } from 'react-native';
|
|
17
17
|
import { pageName } from './page-name.js';
|
|
18
|
-
import { OVERLAY_ID, cardBox, currentDemo, demoConfig, findTarget, handlerFor, onDemo, phoneSteps, playDemo, releaseOf, reviewChangesOf, scalePoint, rescueNote, screenCandidates, stepTitle, targetOfKey } from './core.js';
|
|
18
|
+
import { OVERLAY_ID, cardBox, currentDemo, demoConfig, findTarget, handlerFor, introBody, introOf, onDemo, phoneSteps, playDemo, releaseOf, reviewChangesOf, scalePoint, rescueNote, screenCandidates, stepTitle, targetOfKey } from './core.js';
|
|
19
19
|
import { demoApi, tourOf } from './api.js';
|
|
20
20
|
import { signInFirst, openInApp } from './signin.js';
|
|
21
21
|
import { runEvent, fillStep } from './events.js';
|
|
@@ -76,6 +76,7 @@ function Player({ config, router, insets, onClose, children }) {
|
|
|
76
76
|
const [changes, setChanges] = useState([]); // what the last card reviews (reviewChangesOf)
|
|
77
77
|
const [release, setRelease] = useState(null); // a release's demo ends on releasing it (releaseOf)
|
|
78
78
|
const [reviewing, setReviewing] = useState(false);
|
|
79
|
+
const [intro, setIntro] = useState(null); // the first screen: who asked, what, signed in as (introOf)
|
|
79
80
|
const [signIn, setSignIn] = useState(null); // a `signin` step: null while it runs, else { ok, error }
|
|
80
81
|
const [event, setEvent] = useState(null); // an `event` step: null while it runs, else { ok, error }
|
|
81
82
|
const [vars, setVars] = useState({}); // per person: what their events set up ({walkId} in later steps)
|
|
@@ -86,7 +87,7 @@ function Player({ config, router, insets, onClose, children }) {
|
|
|
86
87
|
useEffect(() => {
|
|
87
88
|
let live = true;
|
|
88
89
|
api.load()
|
|
89
|
-
.then((d) => { if (live) { setSteps(phoneSteps(tourOf(d, { here: !!config.here })).concat()); setChanges(reviewChangesOf(d)); setRelease(releaseOf(d)); } })
|
|
90
|
+
.then((d) => { if (live) { setSteps(phoneSteps(tourOf(d, { here: !!config.here })).concat()); setChanges(reviewChangesOf(d)); setRelease(releaseOf(d)); setIntro(introOf(d)); } })
|
|
90
91
|
.catch((e) => { if (live) setError(e.message); });
|
|
91
92
|
return () => { live = false; };
|
|
92
93
|
}, [api]);
|
|
@@ -221,6 +222,12 @@ function Player({ config, router, insets, onClose, children }) {
|
|
|
221
222
|
const overlay = (() => {
|
|
222
223
|
if (error) return h(Card, { at: null, screen, insets, card, setCard, title: 'Demo', body: `The demo didn't load: ${error}` });
|
|
223
224
|
if (!steps) return null;
|
|
225
|
+
if (intro && !tucked && !done) {
|
|
226
|
+
return h(Card, {
|
|
227
|
+
at: null, screen, insets, card, setCard, title: 'Guided Review', body: introBody(intro),
|
|
228
|
+
actions: [{ label: 'Hide', onPress: () => setTucked(true) }, { label: 'Start the review', onPress: () => setIntro(null), primary: true }],
|
|
229
|
+
});
|
|
230
|
+
}
|
|
224
231
|
if (reviewing && !tucked) return h(Review, { api, changes, release, screen, insets, onBack: steps.length ? () => setReviewing(false) : null, onDone: finish, onHide: () => setTucked(true) });
|
|
225
232
|
if (flash) return h(View, { pointerEvents: 'none', style: [s.flash, { top: insets.top + screen.height * 0.4 }], testID: 'ssb-demo-flash' },
|
|
226
233
|
h(Text, { style: s.flashText, accessibilityLiveRegion: 'polite' }, flash));
|
package/src/core.js
CHANGED
|
@@ -10,7 +10,7 @@ import { bubbleBox } from './step-place.js';
|
|
|
10
10
|
// What this player is, said when the app registers for "Ready to see"
|
|
11
11
|
// (packages/ssb-overlay sends it; functions/in-app-notify.mjs clientOf). Kept
|
|
12
12
|
// equal to package.json by test/core.test.mjs.
|
|
13
|
-
export const PLAYER_VERSION = '0.
|
|
13
|
+
export const PLAYER_VERSION = '0.10.0';
|
|
14
14
|
|
|
15
15
|
export const HOST = 5;
|
|
16
16
|
export const TEXT = 6;
|
|
@@ -219,3 +219,30 @@ export function releaseOf(demo) {
|
|
|
219
219
|
if (!sm || typeof sm.jobId !== 'string' || !sm.jobId) return null;
|
|
220
220
|
return { jobId: sm.jobId, label: String(sm.label || 'Approve & release'), note: String(sm.note || '') };
|
|
221
221
|
}
|
|
222
|
+
|
|
223
|
+
// THE FIRST SCREEN OF A REVIEW (0.10.0, Michael 2026-11-03): a very brief
|
|
224
|
+
// outline before anything plays — who asked for it, what they asked for, and
|
|
225
|
+
// who the review signs in as — then a button to start. Pure.
|
|
226
|
+
// → { requestedBy: [string], asks: [{ title, asked }], as: [string] } | null
|
|
227
|
+
const firstSentence = (t) => { const x = String(t || '').replace(/\s+/g, ' ').trim(); const m = /^(.{20,160}?[.!?])(\s|$)/.exec(x); return (m ? m[1] : x.slice(0, 140) + (x.length > 140 ? '…' : '')); };
|
|
228
|
+
export function introOf(demo) {
|
|
229
|
+
const asks = [], by = new Set(), as = [];
|
|
230
|
+
for (const a of demo?.actors || []) {
|
|
231
|
+
if (a?.label && !as.includes(a.label)) as.push(String(a.label));
|
|
232
|
+
for (const sec of a.sections || []) for (const f of sec.features || []) {
|
|
233
|
+
if (f?.requestedBy) by.add(String(f.requestedBy).trim());
|
|
234
|
+
if (!asks.some((x) => x.title === f.title)) asks.push({ title: String(f.title || 'This change'), ...(f.tryIt?.steps ? { asked: firstSentence(f.tryIt.steps) } : {}) });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return asks.length || as.length ? { requestedBy: [...by], asks, as } : null;
|
|
238
|
+
}
|
|
239
|
+
export function introBody(intro) {
|
|
240
|
+
if (!intro) return '';
|
|
241
|
+
const lines = [];
|
|
242
|
+
if (intro.requestedBy.length) lines.push(`Requested by ${intro.requestedBy.join(', ')}`);
|
|
243
|
+
const shown = intro.asks.slice(0, 3);
|
|
244
|
+
for (const a of shown) lines.push(`• ${a.title}`);
|
|
245
|
+
if (intro.asks.length > shown.length) lines.push(`…and ${intro.asks.length - shown.length} more`);
|
|
246
|
+
if (intro.as.length) lines.push(`Signed in as ${intro.as.join(', then ')}`);
|
|
247
|
+
return lines.join('\n');
|
|
248
|
+
}
|