Files
cinny/src/app/state/toast.test.ts
T
jared fd9e4a9802 feat(download): show a toast + button check when a file is saved
The desktop (Tauri) app has no native download UI, so FileSaver.saveAs saved
files silently — no visual or audio confirmation. Users re-clicked because
nothing said it worked (one report: 5 copies of the same file). Add a small
useSaveFile() hook that saves AND raises a 'Downloaded <filename>' toast, and
route every download call site through it (file attachments, image viewer, PDF
viewer, plus the recovery-key / key-backup exports). The file-message download
button also shows a green check on success.

Toast system extended with an optional iconSrc so system toasts render an icon
instead of an avatar/initials, and an empty roomName is no longer rendered.

Tests: createDownloadToast covered; 701/701 pass; typecheck + build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:30:57 -04:00

100 lines
3.0 KiB
TypeScript

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { toastQueueAtom, dismissToastAtom, ToastNotif, createDownloadToast } from './toast';
// The queue lives in an unexported baseAtom; we drive the two write-only setters
// (toastQueueAtom append + null no-op guard, dismissToastAtom remove-by-id)
// through a jotai store and read back via toastQueueAtom's getter.
const makeToast = (id: string): ToastNotif => ({
id,
displayName: `name-${id}`,
body: `body-${id}`,
roomName: `room-${id}`,
roomId: `!${id}:server`,
});
test('starts empty', () => {
const store = createStore();
assert.deepEqual(store.get(toastQueueAtom), []);
});
test('toastQueueAtom appends in order', () => {
const store = createStore();
const a = makeToast('a');
const b = makeToast('b');
store.set(toastQueueAtom, a);
store.set(toastQueueAtom, b);
assert.deepEqual(
store.get(toastQueueAtom).map((t) => t.id),
['a', 'b'],
);
assert.equal(store.get(toastQueueAtom)[0], a);
});
test('toastQueueAtom ignores null (no-op guard)', () => {
const store = createStore();
store.set(toastQueueAtom, makeToast('a'));
store.set(toastQueueAtom, null);
assert.deepEqual(
store.get(toastQueueAtom).map((t) => t.id),
['a'],
);
});
test('toastQueueAtom allows duplicate ids (no dedupe)', () => {
const store = createStore();
store.set(toastQueueAtom, makeToast('a'));
store.set(toastQueueAtom, makeToast('a'));
assert.equal(store.get(toastQueueAtom).length, 2);
});
test('dismissToastAtom removes the matching id only', () => {
const store = createStore();
store.set(toastQueueAtom, makeToast('a'));
store.set(toastQueueAtom, makeToast('b'));
store.set(toastQueueAtom, makeToast('c'));
store.set(dismissToastAtom, 'b');
assert.deepEqual(
store.get(toastQueueAtom).map((t) => t.id),
['a', 'c'],
);
});
test('dismissToastAtom removes every entry sharing the id', () => {
const store = createStore();
store.set(toastQueueAtom, makeToast('a'));
store.set(toastQueueAtom, makeToast('a'));
store.set(toastQueueAtom, makeToast('b'));
store.set(dismissToastAtom, 'a');
assert.deepEqual(
store.get(toastQueueAtom).map((t) => t.id),
['b'],
);
});
test('dismissToastAtom for an unknown id is a no-op', () => {
const store = createStore();
store.set(toastQueueAtom, makeToast('a'));
store.set(dismissToastAtom, 'missing');
assert.deepEqual(
store.get(toastQueueAtom).map((t) => t.id),
['a'],
);
});
test('createDownloadToast: filename in body, no room navigation, unique ids', () => {
const a = createDownloadToast('photo.jpg');
assert.equal(a.displayName, 'Downloaded');
assert.equal(a.body, 'photo.jpg');
// roomId empty + an onClick present → clicking dismisses without navigating to a room.
assert.equal(a.roomId, '');
assert.equal(a.roomName, '');
assert.equal(typeof a.onClick, 'function');
const b = createDownloadToast('photo.jpg');
assert.notEqual(a.id, b.id);
});