fix: address PR review comments

- Wrap readFile in try/catch, return 404 for missing attachment files
- Remove unused CollaboratorPopover import from NoteCard
- Make darkMode a proper $state rune for reactivity (add svelte-runes
  polyfill for Vitest so $state works in unit tests)
- Add error toasts for failed share link toggle (non-2xx responses)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Frederic Cabassut
2026-03-21 08:47:20 +01:00
committed by Fred Cabassut
co-authored by Claude Opus 4.6
parent 552c2b6265
commit 4eb97b86f9
6 changed files with 38 additions and 19 deletions
+11
View File
@@ -0,0 +1,11 @@
/**
* Polyfill Svelte 5 runes for Vitest.
* The Svelte compiler transforms these in .svelte and .svelte.ts files,
* but Vitest doesn't run the Svelte compiler, so we provide plain-JS stubs.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
(globalThis as any).$state = <T>(initial: T): T => initial;
(globalThis as any).$derived = <T>(value: T): T => value;
(globalThis as any).$effect = (_fn: () => void): void => {};
(globalThis as any).$props = (): any => ({});
-1
View File
@@ -4,7 +4,6 @@
import { renderMarkdown } from '$lib/utils/markdown.js'; import { renderMarkdown } from '$lib/utils/markdown.js';
import { togglePin, trashNote, archiveNote, unarchiveNote, restoreNote, deleteNote, leaveNote, currentFilter } from '$lib/stores/notes.js'; import { togglePin, trashNote, archiveNote, unarchiveNote, restoreNote, deleteNote, leaveNote, currentFilter } from '$lib/stores/notes.js';
import ImageLightbox from './ImageLightbox.svelte'; import ImageLightbox from './ImageLightbox.svelte';
import CollaboratorPopover from './CollaboratorPopover.svelte';
import SharingIndicator from './SharingIndicator.svelte'; import SharingIndicator from './SharingIndicator.svelte';
import type { Note } from '$lib/types/index.js'; import type { Note } from '$lib/types/index.js';
import Undo2 from 'lucide-svelte/icons/undo-2'; import Undo2 from 'lucide-svelte/icons/undo-2';
+13 -9
View File
@@ -43,19 +43,23 @@
try { try {
if (publicShareToken) { if (publicShareToken) {
const res = await fetch(`/api/notes/${noteId}/share`, { method: 'DELETE' }); const res = await fetch(`/api/notes/${noteId}/share`, { method: 'DELETE' });
if (res.ok) { if (!res.ok) {
publicShareToken = null; showToast('Failed to remove share link', 'error');
onShareUpdate(null); return;
showToast('Public link removed', 'success');
} }
publicShareToken = null;
onShareUpdate(null);
showToast('Public link removed', 'success');
} else { } else {
const res = await fetch(`/api/notes/${noteId}/share`, { method: 'POST' }); const res = await fetch(`/api/notes/${noteId}/share`, { method: 'POST' });
if (res.ok) { if (!res.ok) {
const data = await res.json(); showToast('Failed to create share link', 'error');
publicShareToken = data.token; return;
onShareUpdate(data.token);
showToast('Public link created', 'success');
} }
const data = await res.json();
publicShareToken = data.token;
onShareUpdate(data.token);
showToast('Public link created', 'success');
} }
} catch { } catch {
showToast('Failed to update share link', 'error'); showToast('Failed to update share link', 'error');
+1 -1
View File
@@ -3,7 +3,7 @@ import { browser } from '$app/environment';
const LIGHT_THEME_COLOR = '#f0e6d3'; const LIGHT_THEME_COLOR = '#f0e6d3';
const DARK_THEME_COLOR = '#1a1715'; const DARK_THEME_COLOR = '#1a1715';
let darkMode = false; let darkMode = $state(false);
export function getIsDarkMode(): boolean { export function getIsDarkMode(): boolean {
return darkMode; return darkMode;
@@ -26,12 +26,16 @@ export const GET: RequestHandler = async ({ params, url, getClientAddress }) =>
const filePath = wantThumb && attachment.thumbnailPath ? attachment.thumbnailPath : attachment.path; const filePath = wantThumb && attachment.thumbnailPath ? attachment.thumbnailPath : attachment.path;
const mimeType = wantThumb && attachment.thumbnailPath ? 'image/webp' : attachment.mimeType; const mimeType = wantThumb && attachment.thumbnailPath ? 'image/webp' : attachment.mimeType;
const buffer = await readFile(filePath); try {
return new Response(buffer, { const buffer = await readFile(filePath);
headers: { return new Response(buffer, {
'Content-Type': mimeType, headers: {
'Content-Disposition': `inline; filename="${attachment.filename}"`, 'Content-Type': mimeType,
'Cache-Control': 'public, max-age=3600' 'Content-Disposition': `inline; filename="${attachment.filename}"`,
} 'Cache-Control': 'public, max-age=3600'
}); }
});
} catch {
throw error(404, 'Attachment not found');
}
}; };
+1
View File
@@ -11,6 +11,7 @@ export default defineConfig({
test: { test: {
include: ['src/**/*.test.ts'], include: ['src/**/*.test.ts'],
environment: 'node', environment: 'node',
setupFiles: ['src/lib/__mocks__/svelte-runes.ts'],
coverage: { coverage: {
provider: 'v8', provider: 'v8',
reporter: ['text', 'html', 'lcov', 'json-summary'], reporter: ['text', 'html', 'lcov', 'json-summary'],