fix(community): Label-ID im Veröffentlichen-Dialog, Status nie in „lädt“, Nachprüfung robust
Die Überschrift des Dialogs hatte dieselbe ID wie das Titelfeld (publish-title), das Label des Pflichtfelds zeigte damit auf die Überschrift; jetzt publish-heading. Schließt der Browser den Dialog mitten im Senden, wird er wieder geöffnet statt als abgebrochen aufgelöst. Die Status-Abfrage der Seite wird bei einer lokalen Änderung während der Anfrage wiederholt (nie in „lädt“ hängen), die Nachprüfung nach offenem Ausgang ist eine eigene Abfrage. Der Löschen-Dialog nennt bei unbekanntem Status einen allgemeinen Hinweis und sagt, dass sich eine Veröffentlichung nur auf der Community-Seite zurückziehen lässt. publishCard heißt lokal publishWithCheck. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f6b9ee61f4
commit
87b71ec71f
4 files changed
+216
-33
No files matched your search
@@ -85,6 +85,20 @@ describe('Veröffentlichen-Dialog: Felder', () => {
|
||||
expect(consent().closest('label')!.textContent).toBe(CONSENT_TEXT);
|
||||
});
|
||||
|
||||
it('jedes Feld hat sein Label (label.control === Eingabe), alle IDs sind eindeutig, der Titel des Dialogs ist die Überschrift', () => {
|
||||
void open();
|
||||
const labels = [...form().querySelectorAll('label')];
|
||||
const inputs = [...form().querySelectorAll('input')];
|
||||
expect(labels).toHaveLength(5);
|
||||
expect(labels.map((l) => l.control)).toEqual(inputs);
|
||||
for (const control of inputs) expect(control.labels).toHaveLength(1);
|
||||
const ids = [...dialogEl()!.querySelectorAll('[id]')].map((n) => n.id);
|
||||
expect(new Set(ids).size).toBe(ids.length);
|
||||
const heading = dialogEl()!.querySelector('h2')!;
|
||||
expect(dialogEl()!.getAttribute('aria-labelledby')).toBe(heading.id);
|
||||
expect(inputs.map((i) => i.id)).not.toContain(heading.id);
|
||||
});
|
||||
|
||||
it('nennt den Kartennamen als Text, nie als HTML', () => {
|
||||
void open({ cardName: '<img src=x onerror=alert(1)>' });
|
||||
expect(dialogEl()!.querySelector('img')).toBeNull();
|
||||
@@ -221,6 +235,39 @@ describe('Veröffentlichen-Dialog: Senden', () => {
|
||||
await outcome;
|
||||
});
|
||||
|
||||
it('schließt der Browser den Dialog mitten im Senden (close-Ereignis), geht er wieder auf und löst erst mit dem echten Ergebnis auf', async () => {
|
||||
let finish!: (r: { id: string }) => void;
|
||||
submit.mockReturnValue(new Promise((r) => (finish = r)));
|
||||
let resolved: PublishOutcome | null = null;
|
||||
const outcome = open().then((o) => (resolved = o));
|
||||
fill();
|
||||
send();
|
||||
dialogEl()!.close(); // wie ein Browser, der Esc trotz verhindertem `cancel` auswertet
|
||||
await settle();
|
||||
expect(dialogEl()?.open).toBe(true);
|
||||
dialogEl()!.dispatchEvent(new Event('close'));
|
||||
await settle();
|
||||
expect(resolved).toBeNull();
|
||||
expect(dialogEl()?.open).toBe(true);
|
||||
finish({ id: 'g1' });
|
||||
await outcome;
|
||||
expect(resolved).toBe('published');
|
||||
expect(dialogEl()).toBeNull();
|
||||
});
|
||||
|
||||
it('wurde der Dialog mitten im Senden geschlossen und das Senden scheitert, ist der Fehler sichtbar (Dialog wieder offen)', async () => {
|
||||
let fail!: (e: Error) => void;
|
||||
submit.mockReturnValue(new Promise((_, r) => (fail = r)));
|
||||
void open();
|
||||
fill();
|
||||
send();
|
||||
dialogEl()!.close();
|
||||
fail(new ApiError(503, 'Der Dienst ist gerade nicht erreichbar.'));
|
||||
await settle();
|
||||
expect(dialogEl()?.open).toBe(true);
|
||||
expect(errorText()).toBe('Veröffentlichen fehlgeschlagen – Der Dienst ist gerade nicht erreichbar.');
|
||||
});
|
||||
|
||||
it('Fehler des Dienstes: Servertext im Dialog, der Dialog bleibt offen, ein neuer Versuch ist möglich', async () => {
|
||||
submit.mockRejectedValueOnce(new ApiError(503, 'Der Dienst ist gerade nicht erreichbar.'));
|
||||
const outcome = open();
|
||||
|
||||
@@ -151,8 +151,9 @@ export function publishDialog(opts: PublishDialogOptions): Promise<PublishOutcom
|
||||
const form = el('form', { class: 'report-form', novalidate: true }, intro, fields, errorEl, statusEl, actions);
|
||||
const dialog = el(
|
||||
'dialog',
|
||||
{ class: 'report-dialog publish-dialog', 'aria-labelledby': 'publish-title' },
|
||||
el('div', { class: 'report-card' }, el('h2', { id: 'publish-title', class: 'report-title' }, 'Karte veröffentlichen'), form),
|
||||
{ class: 'report-dialog publish-dialog', 'aria-labelledby': 'publish-heading' },
|
||||
// Eigene ID für die Überschrift: `publish-title` gehört dem Titelfeld (`publish-<name>`), sonst zeigt sein Label auf die Überschrift.
|
||||
el('div', { class: 'report-card' }, el('h2', { id: 'publish-heading', class: 'report-title' }, 'Karte veröffentlichen'), form),
|
||||
);
|
||||
|
||||
const finish = () => {
|
||||
@@ -165,6 +166,17 @@ export function publishDialog(opts: PublishDialogOptions): Promise<PublishOutcom
|
||||
resolve(outcome);
|
||||
};
|
||||
|
||||
/** Öffnet den Dialog wieder, falls ihn der Browser geschlossen hat (Esc trotz verhindertem `cancel`). */
|
||||
function reopen(): boolean {
|
||||
if (dialog.open) return true;
|
||||
try {
|
||||
dialog.showModal();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function setBusy(value: boolean): void {
|
||||
busy = value;
|
||||
form.setAttribute('aria-busy', String(value));
|
||||
@@ -214,11 +226,13 @@ export function publishDialog(opts: PublishDialogOptions): Promise<PublishOutcom
|
||||
if (closed) return;
|
||||
if (isAlreadyPublished(err)) {
|
||||
setBusy(false);
|
||||
reopen();
|
||||
showAlready();
|
||||
return;
|
||||
}
|
||||
setBusy(false);
|
||||
errorEl.textContent = publishFailureText(err);
|
||||
reopen(); // war der Dialog zwischendurch zu, soll der Fehler zu sehen sein
|
||||
return;
|
||||
}
|
||||
if (closed) return;
|
||||
@@ -236,7 +250,14 @@ export function publishDialog(opts: PublishDialogOptions): Promise<PublishOutcom
|
||||
dialog.addEventListener('cancel', (event) => {
|
||||
if (busy) event.preventDefault();
|
||||
});
|
||||
dialog.addEventListener('close', finish);
|
||||
// Schließt der Browser den Dialog trotzdem mitten im Senden, wird er wieder geöffnet; aufgelöst wird erst mit dem echten Ergebnis.
|
||||
dialog.addEventListener('close', () => {
|
||||
if (busy && !closed) {
|
||||
reopen();
|
||||
return;
|
||||
}
|
||||
finish();
|
||||
});
|
||||
// Hintergrund = Klick auf das <dialog> selbst (die Karte füllt es ganz aus); Drücken und Loslassen müssen beide dort sein,
|
||||
// sonst schließt ein Textmarkieren, das außerhalb endet, den Dialog.
|
||||
let pressedOnBackdrop = false;
|
||||
|
||||
@@ -768,16 +768,77 @@ describe('Meine Karten: Veröffentlichung (Status an der Kachel)', () => {
|
||||
expect(mocks.myPublications).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Löschen einer veröffentlichten Karte weist darauf hin, dass die Veröffentlichung bleibt', async () => {
|
||||
await mountWith(pubMap(published('g1', 'a')));
|
||||
button(tileFor('a'), 'Löschen').click();
|
||||
const GENERIC_HINT =
|
||||
'Falls diese Karte veröffentlicht ist, bleibt die Veröffentlichung bestehen – zurückziehen kannst du sie auf ihrer Community-Seite.';
|
||||
const openDelete = async (id: string) => {
|
||||
button(tileFor(id), 'Löschen').click();
|
||||
await vi.waitFor(() => expect(dialog()?.open).toBe(true));
|
||||
};
|
||||
|
||||
it('Löschen einer veröffentlichten Karte (Status bekannt) nennt, dass die Veröffentlichung bleibt und wo man sie zurückzieht', async () => {
|
||||
await mountWith(pubMap(published('g1', 'a')));
|
||||
await openDelete('a');
|
||||
expect(dialog()!.textContent).toContain('bleibt in der Community-Galerie');
|
||||
expect(dialog()!.textContent).toContain('Community-Seite');
|
||||
expect(dialog()!.textContent).not.toContain('Falls diese Karte');
|
||||
button(dialog()!, 'Abbrechen').click();
|
||||
await vi.waitFor(() => expect(dialog()).toBeNull());
|
||||
button(tileFor('b'), 'Löschen').click();
|
||||
// Bekannt unveröffentlicht: kein Hinweis.
|
||||
await vi.waitFor(() => expect(button(tileFor('b'), 'Löschen').disabled).toBe(false));
|
||||
await openDelete('b');
|
||||
expect(dialog()!.textContent).not.toContain('Community');
|
||||
});
|
||||
|
||||
it('Löschen, solange der Status noch lädt: allgemeiner Hinweis', async () => {
|
||||
mocks.list.mockResolvedValue([card('a', 'Alpha')]);
|
||||
mocks.myPublications.mockReturnValue(new Promise(() => {}));
|
||||
mount();
|
||||
await vi.waitFor(() => expect(tiles()).toHaveLength(1));
|
||||
await openDelete('a');
|
||||
expect(dialog()!.textContent).toContain(GENERIC_HINT);
|
||||
});
|
||||
|
||||
it('Löschen, wenn der Status nicht geladen werden konnte: allgemeiner Hinweis', async () => {
|
||||
mocks.list.mockResolvedValue([card('a', 'Alpha')]);
|
||||
mocks.myPublications.mockRejectedValue(new TypeError('offline'));
|
||||
mount();
|
||||
await vi.waitFor(() => expect(text()).toContain('konnte nicht geladen werden'));
|
||||
await openDelete('a');
|
||||
expect(dialog()!.textContent).toContain(GENERIC_HINT);
|
||||
});
|
||||
|
||||
it('Status-Abfrage, die ein gleichzeitiges Zurückziehen überholt, lässt den Status nie in „lädt“ hängen', async () => {
|
||||
mocks.list.mockResolvedValue([card('a', 'Alpha'), card('b', 'Beta')]);
|
||||
mocks.myPublications.mockRejectedValueOnce(new TypeError('offline'));
|
||||
mount();
|
||||
await vi.waitFor(() => expect(text()).toContain('konnte nicht geladen werden'));
|
||||
await vi.waitFor(() => expect(labels(tileFor('a'))).toContain('Veröffentlichen'));
|
||||
// Im Fehlerzustand lässt sich veröffentlichen; danach gibt es „Zurückziehen“.
|
||||
mocks.publishSavedCard.mockResolvedValue({ id: 'g9' });
|
||||
await openPublish('a');
|
||||
fillDialog();
|
||||
sendForm();
|
||||
await vi.waitFor(() => expect(badge('a')).toBe('Veröffentlicht'));
|
||||
// Zurückziehen läuft (Anfrage unterwegs) ...
|
||||
let finishWithdraw!: () => void;
|
||||
mocks.withdrawCard.mockReturnValue(new Promise<void>((r) => (finishWithdraw = r)));
|
||||
await vi.waitFor(() => expect(button(tileFor('a'), 'Zurückziehen').disabled).toBe(false));
|
||||
button(tileFor('a'), 'Zurückziehen').click();
|
||||
await vi.waitFor(() => expect(dialog()?.open).toBe(true));
|
||||
expect(dialog()!.textContent).not.toContain('Community-Galerie');
|
||||
button(dialog()!, 'Zurückziehen').click();
|
||||
await vi.waitFor(() => expect(mocks.withdrawCard).toHaveBeenCalledWith('g9'));
|
||||
// ... währenddessen „Nochmal versuchen“ (die Abfrage hängt noch) ...
|
||||
let finishFetch!: (m: Map<string, GalleryCard>) => void;
|
||||
mocks.myPublications.mockReturnValueOnce(new Promise((r) => (finishFetch = r)));
|
||||
button(root, 'Nochmal versuchen').click();
|
||||
expect(labels(tileFor('b'))).not.toContain('Veröffentlichen'); // Status gilt wieder als unbekannt
|
||||
// ... und das Zurückziehen wird fertig, bevor die Abfrage antwortet.
|
||||
finishWithdraw();
|
||||
await vi.waitFor(() => expect(badge('a')).toBe(''));
|
||||
finishFetch(new Map());
|
||||
await vi.waitFor(() => expect(labels(tileFor('b'))).toContain('Veröffentlichen'));
|
||||
expect(labels(tileFor('a'))).toContain('Veröffentlichen');
|
||||
expect(text()).not.toContain('konnte nicht geladen werden');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -941,6 +1002,31 @@ describe('Meine Karten: Veröffentlichen', () => {
|
||||
expect(badge('a')).toBe('');
|
||||
});
|
||||
|
||||
it('die Nachprüfung geht nicht verloren, wenn währenddessen eine andere Kachel ihren Status ändert', async () => {
|
||||
mocks.publishSavedCard.mockRejectedValue(new ApiError(503, 'Der Dienst ist gerade nicht erreichbar.'));
|
||||
mocks.withdrawCard.mockResolvedValue(undefined);
|
||||
mocks.list.mockResolvedValue([card('a', 'Alpha'), card('b', 'Beta')]);
|
||||
mocks.myPublications.mockResolvedValue(new Map([['b', published('g2', 'b')]]));
|
||||
mount();
|
||||
await vi.waitFor(() => expect(tiles()).toHaveLength(2));
|
||||
await vi.waitFor(() => expect(labels(tileFor('b'))).toContain('Zurückziehen'));
|
||||
await openPublish('a');
|
||||
fillDialog();
|
||||
let finishCheck!: (m: Map<string, GalleryCard>) => void;
|
||||
mocks.myPublications.mockReturnValueOnce(new Promise((r) => (finishCheck = r)));
|
||||
sendForm();
|
||||
await vi.waitFor(() => expect(mocks.myPublications).toHaveBeenCalledTimes(2));
|
||||
// Während die Nachprüfung läuft, zieht der Nutzer in einer anderen Kachel eine Karte zurück.
|
||||
button(tileFor('b'), 'Zurückziehen').click();
|
||||
await vi.waitFor(() => expect(dialog()?.open).toBe(true));
|
||||
button(dialog()!, 'Zurückziehen').click();
|
||||
await vi.waitFor(() => expect(labels(tileFor('b'))).toContain('Veröffentlichen'));
|
||||
finishCheck(new Map([['a', published('g1', 'a')]]));
|
||||
await vi.waitFor(() => expect(badge('a')).toBe('Veröffentlicht'));
|
||||
expect(publishDialogEl()).toBeNull();
|
||||
expect(tileFor('a').querySelector('.card-msg')!.textContent).toBe('');
|
||||
});
|
||||
|
||||
it('die Nachprüfung findet nur die Veröffentlichung dieser Karte (eine andere zählt nicht)', async () => {
|
||||
mocks.publishSavedCard.mockRejectedValue(new ApiError(503, 'Der Dienst ist gerade nicht erreichbar.'));
|
||||
await start();
|
||||
@@ -950,7 +1036,7 @@ describe('Meine Karten: Veröffentlichen', () => {
|
||||
sendForm();
|
||||
await vi.waitFor(() => expect(dlgError()).toContain('Der Dienst ist gerade nicht erreichbar.'));
|
||||
expect(badge('a')).toBe('');
|
||||
expect(badge('b')).toBe('Veröffentlicht');
|
||||
expect(dlgError()).toContain('Der Dienst ist gerade nicht erreichbar.');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+53
-24
@@ -27,7 +27,11 @@ const LOGGED_OUT = 'Melde dich an, um deine Karten zu sehen.';
|
||||
const EMPTY = 'Noch keine Karten gespeichert.';
|
||||
const PREVIEW_PARALLEL = 4;
|
||||
const STATUS_FAILED = 'Der Veröffentlichungsstatus konnte nicht geladen werden.';
|
||||
const PUBLISHED_NOTE = 'Die veröffentlichte Karte bleibt in der Community-Galerie, bis du sie zurückziehst.';
|
||||
// Löschen einer privaten Karte lässt ihre Veröffentlichung bestehen; zurückgezogen wird dann auf der Community-Seite.
|
||||
const PUBLISHED_NOTE =
|
||||
'Die veröffentlichte Karte bleibt in der Community-Galerie, bis du sie zurückziehst – das geht dann nur noch auf ihrer Community-Seite.';
|
||||
const MAYBE_PUBLISHED_NOTE =
|
||||
'Falls diese Karte veröffentlicht ist, bleibt die Veröffentlichung bestehen – zurückziehen kannst du sie auf ihrer Community-Seite.';
|
||||
|
||||
/** Was die Kachel über die Veröffentlichung wissen muss. */
|
||||
interface Publication {
|
||||
@@ -50,7 +54,10 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
let userId = '';
|
||||
let publications = new Map<string, Publication>();
|
||||
let publicationsState: 'loading' | 'ready' | 'error' = 'loading';
|
||||
let publicationsToken = 0;
|
||||
/** Zählt Abfragen: nur die neueste darf den Stand setzen (Abmelden zählt auch, die alte Abfrage gehört dann dem alten Nutzer). */
|
||||
let fetchToken = 0;
|
||||
/** Zählt lokale Änderungen (`setPublication`): eine Abfrage, die währenddessen lief, kann älter sein und wird wiederholt. */
|
||||
let changes = 0;
|
||||
const tileUpdaters = new Map<string, () => void>();
|
||||
let statusNotice: HTMLElement | null = null;
|
||||
|
||||
@@ -139,7 +146,7 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
state = 'loggedOut';
|
||||
cards = [];
|
||||
publications = new Map();
|
||||
publicationsToken += 1; // eine noch laufende Abfrage gehört dem alten Nutzer
|
||||
fetchToken += 1; // eine noch laufende Abfrage gehört dem alten Nutzer
|
||||
tileUpdaters.clear();
|
||||
statusNotice = null;
|
||||
queue.length = 0;
|
||||
@@ -219,29 +226,39 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
updateStatusNotice();
|
||||
}
|
||||
|
||||
/** Holt die eigenen Veröffentlichungen neu. Wahr, wenn der Stand jetzt stimmt; bei Fehler bleibt der bisherige Stand. */
|
||||
/**
|
||||
* Holt die eigenen Veröffentlichungen neu. Wahr, wenn der Stand jetzt stimmt; bei Fehler bleibt der bisherige Stand
|
||||
* (war er noch unbekannt, gilt er als „Fehler“). Ändert sich währenddessen lokal etwas (Veröffentlichen/Zurückziehen),
|
||||
* kann die Antwort älter sein als diese Änderung und wird nicht übernommen, sondern neu angefragt. Verdrängt eine
|
||||
* neuere Abfrage diese, setzt sie den Stand; der Status bleibt so nie in „lädt“ hängen.
|
||||
*/
|
||||
async function fetchPublications(): Promise<boolean> {
|
||||
const token = ++publicationsToken;
|
||||
try {
|
||||
const found = await myPublications(userId);
|
||||
if (disposed || token !== publicationsToken) return false;
|
||||
const token = ++fetchToken;
|
||||
for (;;) {
|
||||
const seen = changes;
|
||||
let found: Awaited<ReturnType<typeof myPublications>>;
|
||||
try {
|
||||
found = await myPublications(userId);
|
||||
} catch {
|
||||
if (disposed || token !== fetchToken) return false;
|
||||
if (publicationsState === 'loading') publicationsState = 'error';
|
||||
updateAllTiles();
|
||||
return false;
|
||||
}
|
||||
if (disposed || token !== fetchToken) return false;
|
||||
if (changes !== seen) continue;
|
||||
const next = new Map<string, Publication>();
|
||||
for (const [cardId, entry] of found) next.set(cardId, { id: entry.id, title: entry.title, hidden: entry.hidden });
|
||||
publications = next;
|
||||
publicationsState = 'ready';
|
||||
} catch {
|
||||
if (disposed || token !== publicationsToken) return false;
|
||||
if (publicationsState === 'loading') publicationsState = 'error';
|
||||
updateAllTiles();
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
updateAllTiles();
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Trägt eine Veröffentlichung (oder ihr Ende) ein und aktualisiert nur diese Kachel. */
|
||||
function setPublication(cardId: string, entry: Publication | null): void {
|
||||
publicationsToken += 1; // eine noch laufende Abfrage kann älter sein als diese Änderung
|
||||
changes += 1;
|
||||
if (entry) publications.set(cardId, entry);
|
||||
else publications.delete(cardId);
|
||||
tileUpdaters.get(cardId)?.();
|
||||
@@ -250,9 +267,10 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
/**
|
||||
* Veröffentlicht die Karte. Ist sie schon veröffentlicht (409), wird der Status neu geholt (der Dialog zeigt den Text).
|
||||
* Bleibt der Ausgang offen (503, 429, 408, 5xx: die Veröffentlichung kann trotzdem angelegt worden sein), wird vor
|
||||
* der Fehlermeldung nachgesehen und bei Erfolg der Erfolgszustand gezeigt.
|
||||
* der Fehlermeldung nachgesehen und bei Erfolg der Erfolgszustand gezeigt. Die Nachsicht ist eine eigene Abfrage,
|
||||
* unabhängig vom Status-Laden der Seite, damit weder ein Neuladen noch eine andere Kachel sie verwirft.
|
||||
*/
|
||||
async function publishCard(card: SavedCard, form: PublishForm): Promise<{ id: string }> {
|
||||
async function publishWithCheck(card: SavedCard, form: PublishForm): Promise<{ id: string }> {
|
||||
try {
|
||||
const { id } = await withLogin(() => publishSavedCard(card, form));
|
||||
if (!disposed) setPublication(card.id, { id, title: form.title.trim(), hidden: false });
|
||||
@@ -263,9 +281,16 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
throw err;
|
||||
}
|
||||
if (isDefinitiveFailure(err)) throw err;
|
||||
const current = (await fetchPublications()) ? publications.get(card.id) : undefined;
|
||||
if (!current) throw err;
|
||||
return { id: current.id };
|
||||
let found: Publication | undefined;
|
||||
try {
|
||||
const entry = (await myPublications(userId)).get(card.id);
|
||||
if (entry) found = { id: entry.id, title: entry.title, hidden: entry.hidden };
|
||||
} catch {
|
||||
// Auch die Nachsicht scheitert: es bleibt bei der ursprünglichen Meldung.
|
||||
}
|
||||
if (!found) throw err;
|
||||
if (!disposed) setPublication(card.id, found);
|
||||
return { id: found.id };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,9 +385,15 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
publishSlot.replaceChildren(...controls);
|
||||
}
|
||||
|
||||
/** Zusatz im Löschen-Dialog: die Veröffentlichung bleibt. Ist der Status unbekannt, ein allgemeiner Hinweis. */
|
||||
function deleteNote(c: SavedCard): string {
|
||||
if (publicationsState !== 'ready') return ` ${MAYBE_PUBLISHED_NOTE}`;
|
||||
return publications.has(c.id) ? ` ${PUBLISHED_NOTE}` : '';
|
||||
}
|
||||
|
||||
function startPublish(): void {
|
||||
void run(async () => {
|
||||
await publishDialog({ cardName: card.name, signal: disposal.signal, submit: (form) => publishCard(card, form) });
|
||||
await publishDialog({ cardName: card.name, signal: disposal.signal, submit: (form) => publishWithCheck(card, form) });
|
||||
}).then(() => focusControl('[data-action="view"], [data-action="withdraw"], [data-action="publish"]'));
|
||||
}
|
||||
|
||||
@@ -415,9 +446,7 @@ export function mountCardsPage(root: HTMLElement, store: EditorStore): () => voi
|
||||
const confirmed = await confirmDialog(
|
||||
{
|
||||
title: `Karte „${card.name}“ löschen?`,
|
||||
text: publications.has(card.id)
|
||||
? `Die Karte und ihr Bild werden endgültig gelöscht. ${PUBLISHED_NOTE}`
|
||||
: 'Die Karte und ihr Bild werden endgültig gelöscht.',
|
||||
text: `Die Karte und ihr Bild werden endgültig gelöscht.${deleteNote(card)}`,
|
||||
confirmLabel: 'Löschen',
|
||||
},
|
||||
disposal.signal,
|
||||
|
||||
Reference in new issue
Block a user