refactor: simplify notes components to use service directly

- Remove prop drilling, components get notes from NoteService context
- NoteCard gets note by ID from service, uses {#key} for Markdown reactivity
- NotesGrid handles filtering internally via service methods
- NoteEditor gets service from context instead of prop
- Add PageState type for shallow routing selectedNoteId
- Add +layout.server.ts to pass user data to client

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 13:54:51 -05:00
parent 9363bbcc76
commit 56b8e9956a
7 changed files with 142 additions and 193 deletions
+10 -39
View File
@@ -1,4 +1,5 @@
<script lang="ts">
import type { User } from 'better-auth';
import { pushState } from '$app/navigation';
import { page } from '$app/state';
import { setNoteService } from '$lib/context/notes.svelte';
@@ -11,37 +12,16 @@
import NoteSearchBar from './NoteSearchBar.svelte';
import NoteEditor from './NoteEditor.svelte';
// Get note service from layout context
const noteService = setNoteService('default-user');
let { data }: { data: { user: User } } = $props();
// Create note service with authenticated user
// svelte-ignore state_referenced_locally
const noteService = setNoteService(data.user.id);
let searchQuery = $state('');
let currentFilter = $state<'all' | 'pinned' | 'archived' | 'trash'>('all');
let view = $state<'grid' | 'list'>('grid');
// Get filtered notes based on current tab
const filteredNotes = $derived.by(() => {
let notes = noteService.activeNotes;
switch (currentFilter) {
case 'pinned':
notes = noteService.pinnedNotes;
break;
case 'archived':
notes = noteService.archivedNotes;
break;
case 'trash':
notes = noteService.trashedNotes;
break;
}
// Apply search filter
if (searchQuery.trim()) {
return noteService.searchNotes(searchQuery, notes);
}
return notes;
});
async function createNewNote() {
const note = await noteService.createNote({
title: '',
@@ -149,19 +129,10 @@
<!-- Notes Grid -->
<NotesGrid
notes={filteredNotes}
filter={currentFilter}
search={searchQuery}
{view}
loading={noteService.loading}
emptyMessage={searchQuery
? 'No notes match your search'
: currentFilter === 'pinned'
? 'No pinned notes'
: currentFilter === 'archived'
? 'No archived notes'
: currentFilter === 'trash'
? 'Trash is empty'
: 'No notes yet'}
onNoteClick={(note) => openNote(note.id)}
onNoteClick={openNote}
/>
</div>
@@ -169,7 +140,7 @@
<Dialog.Root open={!!selectedNoteId} onOpenChange={(open) => !open && closeNote()}>
<Dialog.Content class="w-[90vw]! max-w-6xl! h-[90vh] flex flex-col overflow-hidden">
{#if selectedNoteId}
<NoteEditor noteId={selectedNoteId} {noteService} />
<NoteEditor noteId={selectedNoteId} />
{/if}
</Dialog.Content>
</Dialog.Root>