group editor code display changed
Sonarqube Scanner / Prepare environment (push) Successful in 6s
Sonarqube Scanner / Build and test (push) Successful in 22s
Sonarqube Scanner / SonarQube analysis (push) Successful in 56s
Sonarqube Scanner / Deploy to Server (push) Successful in 6s

This commit is contained in:
Florian Zumpe
2026-10-18 23:03:12 +02:00
parent 04e05ecfd4
commit a66a795407
10 changed files with 109 additions and 111 deletions
+7 -5
View File
@@ -1,14 +1,16 @@
# Gemeinsam bearbeiten
In „Strecken bearbeiten“ öffnet der kleine Gruppenbutton zwischen Stift und
Löschen einen Dialog. Dort sind die beiden Wichtel groß zu sehen: links der Vogel,
rechts der Stein. Erst „Gemeinsamen Entwurf öffnen“ legt einen gemeinsamen
Entwurf auf der Website an. Das Original bleibt unverändert. Bei lokalen Entwürfen
In „Strecken bearbeiten“ öffnet der kleine Button mit zwei gezeichneten Wichteln
zwischen Stift und
Löschen den gemeinsamen Entwurf und zeigt anschließend den echten Code mit
fünf Wichteln in einem großen Popup an. Eine vorgeschaltete Zwei-Wichtel-Vorschau
gibt es nicht mehr. Das Original bleibt unverändert. Bei lokalen Entwürfen
fragt die App vorher, ob Weg, Stationen, Bilder und Ton übertragen werden dürfen.
Ein bereits eingesandter eigener Entwurf wird dabei zuerst aktualisiert.
Danach zeigt die App fünf Wichtel als Gruppencode. Andere öffnen in der
Streckenverwaltung „Gruppencode eingeben“ und schieben die Bilder in den fünf
Streckenverwaltung „Gruppencode eingeben“. Ein eigenes breites Popup bietet
Platz für alle fünf Drehräder. Dort schieben sie die Bilder in den fünf
Drehrädern nach oben oder unten. Es zählen die Wichtel in der Mitte, von links
nach rechts. Wiederholungen sind erlaubt. Pfeiltasten und die Pfeilschaltflächen
funktionieren ebenfalls. „Mitmachen“ öffnet den gemeinsamen Entwurf.
+11
View File
@@ -92,3 +92,14 @@ Sitzungen, Rollen owner/member, Codewechsel und Widerruf. Bedienung und Einspiel
nicht vorhanden; bestehende Gruppenrechte gelten unverändert. Heartbeats sind
POSTs mit eigenem Bearbeitungsschlüssel und dürfen weder fremde noch bereits
abgelaufene Sperren übernehmen.
## Code-Popups
- Gruppenbutton zeigt unmittelbar den echten fünfstelligen Einladungscode. Keine
vorgeschaltete Zwei-Wichtel-Vorschau wieder einführen; sie wurde mit dem Code
verwechselt. Eine unvollständige Codeantwort nicht anzeigen.
- Codeanzeige und Codeeingabe verwenden getrennte breite native Dialoge. Alle
fünf Drehräder nebeneinander anordnen; Wiederholungen weiterhin zulassen.
- Scrollschritt in groups.js und Optionshöhe/Innenabstand in editor.css müssen
übereinstimmen (96 Pixel). Schließen der Eingabe verwirft verspätete Antworten;
die automatische Einladungsaktualisierung darf die Eingabe nicht überdecken.
+8 -8
View File
@@ -595,24 +595,24 @@
.current-location-button img { width: 24px; height: 24px; vertical-align: middle; }
/* Shared drafts and vertically scrolling Wichtel-code wheels. */
.group-share-pictures { display: flex; align-items: center; justify-content: center; gap: 1rem; margin: 1rem 0; }
.group-share-pictures img { width: min(34vw, 9rem); height: 10rem; object-fit: contain; }
.group-share-button img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
.group-dialog { width: min(35rem, calc(100% - 2rem)); max-height: 90dvh; box-sizing: border-box; overflow-y: auto; padding: 1.25rem; border: 1px solid #c8d3ca; border-radius: 1.25rem; background: #f8faf5; color: #1f3022; }
.group-code-dialog { width: min(56rem, calc(100vw - 1rem)); max-height: calc(100dvh - 1rem); padding: clamp(.5rem, 2vw, 1.5rem); }
.group-dialog::backdrop { background: rgba(20, 40, 25, .5); }
.group-dialog-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.group-dialog-header h2 { margin: 0; font-size: 1.2rem; }
.group-dialog button { min-width: 44px; min-height: 44px; border: 1px solid #c8d3ca; border-radius: .85rem; background: white; color: #24462b; touch-action: manipulation; }
.group-dialog button:focus-visible, .group-wheel-viewport:focus-visible { outline: 3px solid #1e6fba; outline-offset: 2px; }
.group-wheels { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .3rem; padding: 0; border: 0; margin: 1rem 0; }
.group-wheels { display: grid; grid-template-columns: repeat(5, minmax(44px, 1fr)); gap: .3rem; padding: 0; border: 0; margin: 1rem 0; }
.group-code-fieldset { border: 0; padding: 0; min-width: 0; }
.group-code-fieldset legend { margin-bottom: .5rem; font-weight: 600; }
.group-wheel { display: grid; gap: .3rem; min-width: 0; }
.group-wheel-viewport { height: 156px; padding: 52px 0; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory; scrollbar-width: none; border-radius: .85rem; background: linear-gradient(to bottom, #edf2eb 0 33.33%, #dcebdc 33.33% 66.66%, #edf2eb 66.66%); }
.group-wheel-viewport { height: 288px; padding: 96px 0; box-sizing: border-box; overflow-y: auto; overscroll-behavior: contain; scroll-snap-type: y mandatory; scrollbar-width: none; border-radius: .85rem; background: linear-gradient(to bottom, #edf2eb 0 33.33%, #dcebdc 33.33% 66.66%, #edf2eb 66.66%); }
.group-wheel-viewport::-webkit-scrollbar { display: none; }
.group-wheel-option { height: 52px; display: grid; place-items: center; scroll-snap-align: center; cursor: pointer; }
.group-wheel-option img { width: 100%; max-width: 48px; height: 48px; object-fit: contain; pointer-events: none; }
.group-code { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: .3rem; padding: .6rem; border: 1px solid #c8d3ca; border-radius: 1rem; background: #e7f1e9; }
.group-code img { width: 100%; height: 64px; object-fit: contain; }
.group-wheel-option { height: 96px; display: grid; place-items: center; scroll-snap-align: center; cursor: pointer; }
.group-wheel-option img { width: 100%; max-width: 88px; height: 88px; object-fit: contain; pointer-events: none; }
.group-code { display: grid; grid-template-columns: repeat(5, minmax(44px, 1fr)); gap: .3rem; padding: .6rem; border: 1px solid #c8d3ca; border-radius: 1rem; background: #e7f1e9; }
.group-code img { width: 100%; height: clamp(64px, 14vw, 128px); object-fit: contain; }
.group-actions { max-width: min(18rem, 52vw); flex-wrap: wrap; justify-content: flex-end; }
.group-actions .editor-secondary-button { font-size: .85rem; }
.group-status { display: block; min-height: 1.5rem; color: #9b2c2c; }
+8
View File
@@ -0,0 +1,8 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" fill="none" stroke="#315d3a" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M5 23 12 6Q14 2 16 6L23 23Z" fill="#e7f1e9"/>
<path d="M7 24Q5 34 14 43 23 34 21 24" fill="#fff"/>
<circle cx="14" cy="25" r="3" fill="#f2ca6b"/>
<path d="M25 25 32 10Q34 6 36 10L43 25Z" fill="#f8f5eb"/>
<path d="M27 26Q25 35 34 43 43 35 41 26" fill="#fff"/>
<circle cx="34" cy="27" r="3" fill="#f2ca6b"/>
</svg>

After

Width:  |  Height:  |  Size: 507 B

+13 -17
View File
@@ -579,33 +579,29 @@
<script src="js/user-messages.js?v=0.12.14"></script>
<script src="js/local-routes.js?v=0.12.14"></script>
<script src="js/route-recorder.js?v=0.12.14"></script>
<dialog id="group-share-dialog" class="group-dialog" aria-labelledby="group-share-title">
<dialog id="group-entry-dialog" class="group-dialog group-code-dialog" aria-labelledby="group-entry-title">
<header class="group-dialog-header">
<h2 id="group-share-title" data-i18n="groups.share">Gemeinsam bearbeiten</h2>
<button id="group-share-close" type="button" data-i18n-aria-label="info.close">×</button>
</header>
<p id="group-share-route"></p>
<div id="group-share-pictures" class="group-share-pictures"></div>
<p data-i18n="groups.share-help">Mit diesen beiden Wichteln öffnest du einen gemeinsamen Entwurf. Andere können mit einem Wichtelcode mitmachen. Das Original bleibt erhalten.</p>
<div class="editor-form-actions">
<button id="group-share-start" type="button" data-i18n="groups.share-start">Gemeinsamen Entwurf öffnen</button>
</div>
</dialog>
<dialog id="group-dialog" class="group-dialog" aria-labelledby="group-title">
<header class="group-dialog-header">
<h2 id="group-title" data-i18n="groups.share">Gemeinsam bearbeiten</h2>
<button id="group-close" type="button" aria-label="Schließen" data-i18n-aria-label="info.close">×</button>
<h2 id="group-entry-title" data-i18n="groups.join-open">Gruppencode eingeben</h2>
<button id="group-entry-close" type="button" data-i18n-aria-label="info.close">×</button>
</header>
<section id="group-entry">
<p data-i18n="groups.wheel-help">Schiebe die Wichtel nach oben oder unten. Die fünf Wichtel in der Mitte ergeben den Code. Lies von links nach rechts. Ein Wichtel darf mehrmals vorkommen.</p>
<fieldset class="group-code-fieldset"><legend data-i18n="groups.code-label">Gruppencode</legend><div id="group-wheels" class="group-wheels"></div></fieldset>
<button id="group-join" type="button" data-i18n="groups.join">Mitmachen</button>
<div class="editor-form-actions"><button id="group-join" type="button" data-i18n="groups.join">Mitmachen</button></div>
</section>
<output id="group-entry-status" class="group-status"></output>
</dialog>
<dialog id="group-dialog" class="group-dialog group-code-dialog" aria-labelledby="group-title">
<header class="group-dialog-header">
<h2 id="group-title" data-i18n="groups.code-label">Gruppencode</h2>
<button id="group-close" type="button" aria-label="Schließen" data-i18n-aria-label="info.close">×</button>
</header>
<p id="group-route"></p>
<section id="group-invitation" hidden>
<p data-i18n="groups.invite-help">Zeige diesen Code den anderen. Damit können sie deinen gemeinsamen Entwurf öffnen und bearbeiten. Das Original bleibt erhalten.</p>
<div id="group-code" class="group-code" aria-label="Gruppencode" data-i18n-aria-label="groups.code-label"></div>
<p id="group-expiry"></p>
<button id="group-edit" type="button" data-i18n="groups.edit">Entwurf bearbeiten</button>
<div class="editor-form-actions"><button id="group-edit" type="button" data-i18n="groups.edit">Entwurf bearbeiten</button></div>
</section>
<output id="group-status" class="group-status"></output>
</dialog>
-1
View File
@@ -1652,7 +1652,6 @@ globalThis.Wegwichtel = globalThis.Wegwichtel || {};
if (shareInProgress) return;
shareInProgress = true;
try {
if (ns.Groups.confirmShare && !await ns.Groups.confirmShare(routeName(route))) return;
let sourceId = routeId(route);
if (routeSource(route) === 'local') {
if (!globalThis.confirm(ns.I18n.t('groups.upload-confirm'))) return;
+28 -43
View File
@@ -6,10 +6,11 @@ globalThis.Wegwichtel ??= {};
joinOpen: 'group-join-open', editorJoin: 'editor-group-join', list: 'editor-group-list',
dialog: 'group-dialog', close: 'group-close', status: 'group-status', wheels: 'group-wheels',
entry: 'group-entry', invitation: 'group-invitation', code: 'group-code', join: 'group-join',
edit: 'group-edit', expiry: 'group-expiry'
edit: 'group-edit', expiry: 'group-expiry',
entryDialog: 'group-entry-dialog', entryClose: 'group-entry-close', entryStatus: 'group-entry-status'
};
const codeLength = 5;
const wheelStep = 52;
const wheelStep = 96;
const revisions = new Map();
let selection = [];
let currentGroup = null;
@@ -73,12 +74,12 @@ globalThis.Wegwichtel ??= {};
async function guarded(action) {
if (pending) return;
pending = true;
element(ids.status).textContent = '';
const status = element(element(ids.entryDialog).open ? ids.entryStatus : ids.status);
status.textContent = '';
element(ids.join).disabled = true;
try { await action(); } catch (error) {
element(ids.status).textContent = errorMessage(error);
if (!element(ids.dialog).open) {
element(ids.entry).hidden = true;
element(element(ids.entryDialog).open ? ids.entryStatus : ids.status).textContent = errorMessage(error);
if (!element(ids.dialog).open && !element(ids.entryDialog).open) {
element(ids.invitation).hidden = true;
element(ids.dialog).showModal();
}
@@ -146,21 +147,22 @@ globalThis.Wegwichtel ??= {};
}
function openEntry() {
if (element(ids.dialog).open) element(ids.dialog).close();
dialogGeneration += 1;
selection = new Array(codeLength).fill(0);
element(ids.status).textContent = '';
element(ids.entry).hidden = false;
element(ids.invitation).hidden = true;
element(ids.entryStatus).textContent = '';
const wheels = element(ids.wheels);
wheels.replaceChildren(...selection.map((_, index) => makeWheel(index)));
if (!element(ids.dialog).open) element(ids.dialog).showModal();
if (!element(ids.entryDialog).open) element(ids.entryDialog).showModal();
}
function showInvitation(payload) {
if (!Array.isArray(payload.code) || payload.code.length !== codeLength) throw new Error(t('error.connection'));
currentGroup = payload.group;
element(ids.status).textContent = '';
element(ids.entry).hidden = true;
if (element(ids.entryDialog).open) element(ids.entryDialog).close();
element(ids.invitation).hidden = false;
element('group-route').textContent = payload.group.route.name || '';
const motifs = payload.code.map(value => ns.GroupSymbols.get(value));
element(ids.code).replaceChildren(...motifs.map(picture));
const time = new Date(payload.expiresAt * 1000).toLocaleTimeString(ns.I18n.language, { hour: '2-digit', minute: '2-digit' });
@@ -182,7 +184,8 @@ globalThis.Wegwichtel ??= {};
finally { invitationPolling = false; }
}
async function edit(group) {
element(ids.dialog).close();
if (element(ids.dialog).open) element(ids.dialog).close();
if (element(ids.entryDialog).open) element(ids.entryDialog).close();
await ns.EditorUi.openGroup(group.route);
}
@@ -258,34 +261,20 @@ globalThis.Wegwichtel ??= {};
function shareButton(action) {
const result = document.createElement('button');
result.type = 'button';
result.className = 'editor-icon-button';
result.className = 'editor-icon-button group-share-button';
result.dataset.editorAction = action;
result.title = t('share');
result.setAttribute('aria-label', t('share'));
result.setAttribute('aria-haspopup', 'dialog');
result.textContent = '👥';
const icon = document.createElement('img');
icon.src = '/images/icons/group-edit.svg';
icon.alt = '';
icon.width = 32;
icon.height = 32;
result.appendChild(icon);
return result;
}
let shareDecision = null;
function confirmShare(name) {
if (shareDecision) return Promise.resolve(false);
const dialog = element('group-share-dialog');
element('group-share-route').textContent = name;
element('group-share-pictures').replaceChildren(...['🐦', '🪨'].map(value => picture(ns.GroupSymbols.get(value))));
return new Promise(resolve => {
shareDecision = resolve;
dialog.showModal();
});
}
function finishShare(accepted) {
const resolve = shareDecision;
shareDecision = null;
element('group-share-dialog').close();
resolve?.(accepted);
}
function headersFor(path) {
const id = /^\/routes\/(\d+)(?:\/|$)/.exec(path)?.[1];
return id && revisions.has(id) ? { 'If-Match': String(revisions.get(id)) } : {};
@@ -306,31 +295,27 @@ globalThis.Wegwichtel ??= {};
code: selection.map(index => motifs[index].unicode), creator_id: ns.LocalRoutes.creatorId()
});
} catch (error) {
if (!element(ids.dialog).open || generation !== dialogGeneration) return;
if (!element(ids.entryDialog).open || generation !== dialogGeneration) return;
throw error;
}
if (!element(ids.dialog).open || generation !== dialogGeneration) return;
if (!element(ids.entryDialog).open || generation !== dialogGeneration) return;
await edit(payload.group);
await refresh();
}
function init() {
globalThis.setInterval?.(() => { refreshInvitation(); }, 30_000);
activate(element('group-share-start'), () => finishShare(true));
activate(element('group-share-close'), () => finishShare(false));
element('group-share-dialog').addEventListener('close', () => {
const resolve = shareDecision; shareDecision = null; resolve?.(false);
});
document.addEventListener('wegwichtel:point-drafts-changed', () => { renderPending(); ns.StorageUsage?.refresh(); });
element(ids.dialog).addEventListener('cancel', () => { dialogGeneration += 1; });
element(ids.dialog).addEventListener('close', () => { dialogGeneration += 1; });
element(ids.entryDialog).addEventListener('cancel', () => { dialogGeneration += 1; });
element(ids.entryDialog).addEventListener('close', () => { dialogGeneration += 1; });
activate(element(ids.entryClose), () => element(ids.entryDialog).close());
for (const id of [ids.joinOpen, ids.editorJoin]) activate(element(id), openEntry);
activate(element(ids.close), () => element(ids.dialog).close());
activate(element(ids.join), () => guarded(joinSelectedGroup));
activate(element(ids.edit), () => guarded(() => edit(currentGroup)));
}
ns.Groups = Object.freeze({ share, shareButton, confirmShare, refresh, refreshInvitation, headersFor, observe });
ns.Groups = Object.freeze({ share, shareButton, refresh, refreshInvitation, headersFor, observe });
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true });
else init();
}(globalThis.Wegwichtel));
-2
View File
@@ -135,8 +135,6 @@ globalThis.Wegwichtel ??= {};
},
"groups": {
"code-wait": "In der Gruppe wird gerade eine Station bearbeitet. Ein neuer Code erscheint, sobald die Stationen wieder frei sind. Ein abgelaufener Code erlaubt keinen neuen Beitritt.",
"share-help": "Mit diesen beiden Wichteln öffnest du einen gemeinsamen Entwurf. Andere können mit einem Wichtelcode mitmachen. Das Original bleibt erhalten.",
"share-start": "Gemeinsamen Entwurf öffnen",
"title": "Gemeinsame Entwürfe",
"upload-confirm": "Dein Entwurf mit Bildern und Ton wird dafür an die Website gesendet und als gemeinsamer Entwurf kopiert. Fortfahren?",
"share": "Gemeinsam bearbeiten",
-2
View File
@@ -135,8 +135,6 @@ globalThis.Wegwichtel ??= {};
},
"groups": {
"code-wait": "Someone in the group is editing a stop. A new code will appear once the stops are free. An expired code cannot be used to join.",
"share-help": "These two gnomes open a shared draft. Others can join using a gnome code. The original stays unchanged.",
"share-start": "Open a shared draft",
"title": "Shared drafts",
"upload-confirm": "Your draft, including pictures and recordings, will be sent to the website and copied into a shared draft. Continue?",
"share": "Edit together",
+34 -33
View File
@@ -27,7 +27,7 @@ function key(node, value) {
}
async function harness(loading = false) {
const ids = ['group-join-open', 'editor-group-join', 'editor-group-list', 'group-dialog', 'group-close',
'group-share-dialog', 'group-share-route', 'group-share-pictures', 'group-share-start', 'group-share-close',
'group-entry-dialog', 'group-entry-close', 'group-entry-status', 'group-route',
'group-status', 'group-wheels', 'group-entry', 'group-invitation', 'group-code', 'group-join', 'group-edit', 'group-expiry'];
const nodes = new Map(ids.map(id => [id, new Element()]));
const document = new EventTarget();
@@ -66,9 +66,9 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
const h = await harness(true);
h.context.document.dispatchEvent(new Event('DOMContentLoaded'));
pointer(h.nodes.get('group-join-open'), { isPrimary: false });
assert.equal(h.nodes.get('group-dialog').open, false);
assert.equal(h.nodes.get('group-entry-dialog').open, false);
pointer(h.nodes.get('group-join-open'));
assert.equal(h.nodes.get('group-dialog').open, true);
assert.equal(h.nodes.get('group-entry-dialog').open, true);
const columns = h.nodes.get('group-wheels').children;
assert.equal(columns.length, 5);
const [up, viewport, down] = columns[0].children;
@@ -82,7 +82,7 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
key(viewport, 'Home'); key(viewport, 'ArrowDown');
pointer(viewport.children[5]);
assert.equal(viewport.attributes.get('aria-valuetext'), 'Vogel');
viewport.scrollTop = 3 * 52; viewport.dispatchEvent(new Event('scroll'));
viewport.scrollTop = 3 * 96; viewport.dispatchEvent(new Event('scroll'));
key(h.nodes.get('group-join'), ' ');
await tick();
const joined = h.calls.find(call => call.url.endsWith('/join'));
@@ -91,10 +91,10 @@ test('group code wheels support arrows, keyboard wrapping, scrolling and joining
assert.equal(body.code[0], '🌼');
assert.equal(body.creator_id, 'a'.repeat(64));
assert.deepEqual(h.edits, [22]);
assert.equal(h.nodes.get('group-dialog').open, false);
assert.equal(h.nodes.get('group-entry-dialog').open, false);
pointer(h.nodes.get('editor-group-join'));
pointer(h.nodes.get('group-close'));
assert.equal(h.nodes.get('group-dialog').open, false);
pointer(h.nodes.get('group-entry-close'));
assert.equal(h.nodes.get('group-entry-dialog').open, false);
});
test('sharing displays five motifs and the owner can renew or close access with confirmation', async () => {
@@ -102,11 +102,14 @@ test('sharing displays five motifs and the owner can renew or close access with
await h.groups.share(7);
assert.equal(JSON.parse(h.calls[0].body).routeId, 7);
assert.equal(h.nodes.get('group-code').children.length, 5);
assert.equal(h.nodes.get('group-entry').hidden, true);
assert.equal(h.nodes.get('group-entry-dialog').open, false);
assert.equal(h.nodes.get('group-dialog').open, true);
assert.match(h.nodes.get('group-expiry').textContent, /Code gilt/);
const shareButton = h.groups.shareButton('share-route');
assert.equal(shareButton.dataset.editorAction, 'share-route');
assert.equal(shareButton.textContent, '👥');
assert.equal(shareButton.children.length, 1);
assert.equal(shareButton.children[0].src, '/images/icons/group-edit.svg');
assert.equal(shareButton.children[0].alt, '');
assert.equal(shareButton.attributes.get('aria-haspopup'), 'dialog');
pointer(h.nodes.get('group-edit')); await tick();
assert.deepEqual(h.edits, [22]);
@@ -140,13 +143,13 @@ test('code failures are localized and group list refresh never advances an editi
for (const [status, text] of [[404, /Code stimmt/], [401, /nicht geöffnet/], [403, /nicht geöffnet/], [409, /inzwischen geändert/], [429, /eine Minute/], [500, /Internetverbindung/]]) {
h.controls.error = status;
pointer(h.nodes.get('group-join')); await tick();
assert.match(h.nodes.get('group-status').textContent, text);
assert.match(h.nodes.get('group-entry-status').textContent, text);
assert.equal(h.nodes.get('group-join').disabled, false);
}
h.controls.error = 0; h.controls.offline = true;
h.context.Wegwichtel.I18n.setLanguage('en', null);
pointer(h.nodes.get('group-join')); await tick();
assert.match(h.nodes.get('group-status').textContent, /internet connection/);
assert.match(h.nodes.get('group-entry-status').textContent, /internet connection/);
await h.groups.refresh();
});
@@ -161,10 +164,10 @@ test('a late join response does not reopen a cancelled dialog or editor', async
pointer(h.nodes.get('group-join-open'));
pointer(h.nodes.get('group-join'));
await tick();
pointer(h.nodes.get('group-close'));
pointer(h.nodes.get('group-entry-close'));
release();
await tick();
assert.equal(h.nodes.get('group-dialog').open, false);
assert.equal(h.nodes.get('group-entry-dialog').open, false);
assert.deepEqual(h.edits, []);
});
@@ -189,29 +192,27 @@ test('unsent station changes are listed before network access and discarded only
assert.equal(list.children.length, 0);
});
test('compact sharing button opens a large gnome preview and requires confirmation before starting', async () => {
test('sharing opens the actual five-symbol invitation directly without a two-symbol preview', async () => {
const h = await harness();
const decision = h.groups.confirmShare('Blumenweg');
const dialog = h.nodes.get('group-share-dialog');
assert.equal(dialog.open, true);
assert.equal(h.nodes.get('group-share-route').textContent, 'Blumenweg');
const pictures = h.nodes.get('group-share-pictures').children;
assert.equal(pictures.length, 2);
assert.match(pictures[0].src, /🐦/);
assert.match(pictures[1].src, /🪨/);
assert.equal(h.calls.length, 0);
assert.equal(await h.groups.confirmShare('Zweiter Weg'), false);
pointer(h.nodes.get('group-share-close'));
assert.equal(await decision, false);
const accepted = h.groups.confirmShare('Blumenweg');
key(h.nodes.get('group-share-start'), 'Enter');
assert.equal(await accepted, true);
assert.equal(dialog.open, false);
const dismissed = h.groups.confirmShare('Blumenweg');
dialog.close();
assert.equal(await dismissed, false);
await h.groups.share(7);
assert.equal(h.nodes.get('group-dialog').open, true);
assert.equal(h.nodes.get('group-entry-dialog').open, false);
assert.equal(h.nodes.get('group-code').children.length, 5);
assert.equal(h.nodes.get('group-route').textContent, 'Waldweg');
pointer(h.nodes.get('group-close'));
pointer(h.nodes.get('editor-group-join'));
assert.equal(h.nodes.get('group-entry-dialog').open, true);
assert.equal(h.nodes.get('group-dialog').open, false);
assert.equal(h.nodes.get('group-wheels').children.length, 5);
});
test('an incomplete invitation is never displayed as a two-symbol group code', async () => {
const h = await harness();
h.context.fetch = async () => ({ ok: true, json: async () => ({ group: { route: { id: 7 } }, code: ['🐦', '🪨'] }) });
await assert.rejects(h.groups.share(7));
assert.equal(h.nodes.get('group-dialog').open, false);
assert.equal(h.nodes.get('group-code').children.length, 0);
});
test('the open invitation refreshes its code automatically and shows a deferred renewal during station editing', async () => {
const h = await harness();