qnote/web/src/lib/components/NoteView.svelte
2025-04-18 19:13:37 +03:00

188 lines
4.3 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts">
import { onMount } from "svelte"
import { goto } from "$app/navigation"
import ThemeToggle from "./ThemeToggle.svelte"
import Sidebar from "./Sidebar.svelte"
import NoteEditor from "./NoteEditor.svelte"
import SettingsModal from "./SettingsModal.svelte"
import {
apiClient,
currentUser,
// isPending,
currentFullNote,
availableNotes,
cError
} from "$lib/client"
import ToggleSidebar from "$lib/icons/ToggleSidebar.svelte"
// Props
export let isLoading = false
// State
let sidebarOpen = true
let showSettings = false
let isEditing = false
onMount(async () => {
isLoading = true
try {
// The following fetch attempts to refresh any expired tokens automatically
await apiClient.getCurrentUser()
// If still no current user after the fetch attempt, redirect to login
if (!$currentUser) {
console.log("no user data found, routing to auth page")
goto("/login")
return
}
await loadNotes()
} catch (error) {
console.error(`error during auth: ${error}`)
goto("/login")
} finally {
isLoading = false
}
})
const loadNotes = async () => {
const notes = await apiClient.listNotes()
if (notes) {
console.log(notes)
availableNotes.set(notes)
}
}
const toggleSidebar = () => {
sidebarOpen = !sidebarOpen
}
const toggleSettings = () => {
showSettings = !showSettings
}
const createNewNote = async () => {
const newNote = await apiClient.createNote()
if (newNote) {
// Refresh notes list
await loadNotes()
// Get the full note details of the newly created note
// (we need to find the ID from the updated `availableNotes`)
if ($availableNotes && $availableNotes.length > 0) {
const latestNote = $availableNotes[0] // Assuming the latest note is first
await selectNote(latestNote.id)
}
isEditing = true // Open brand new notes in edit mode by default
}
}
// TODO: add caching (!!!)
const selectNote = async (noteId: string) => {
console.log(`loading ${noteId}`)
const note = await apiClient.getFullNote(noteId)
if (note) {
currentFullNote.set(note)
isEditing = false
}
}
const toggleEditMode = () => {
isEditing = !isEditing
}
const saveNote = async (title: string, content: string) => {
if ($currentFullNote) {
await apiClient.createVersion($currentFullNote.id, title, content)
// Refresh the current note to get the latest version (+ assure that client and server are synced)
await selectNote($currentFullNote.id)
// Refresh the notes list to update any changes (ordering based on `updatedAt` field)
await loadNotes()
isEditing = false
}
}
const logout = async () => {
await apiClient.logout()
}
</script>
<div class="flex h-screen bg-[var(--light-background)]">
<!-- Error notification -->
{#if $cError}
<div class="fixed right-4 top-4 z-50 max-w-md">
<div class="error">
{$cError}
<button class="ml-2 text-red-700" on:click={() => cError.set(null)}> × </button>
</div>
</div>
{/if}
<!-- Sidebar -->
<Sidebar
{sidebarOpen}
notes={$availableNotes || []}
currentNote={$currentFullNote}
{toggleSettings}
{logout}
{createNewNote}
{selectNote}
/>
<!-- Main content -->
<div
class="flex flex-1 flex-col overflow-hidden transition-all duration-300"
class:ml-64={sidebarOpen}
class:ml-0={!sidebarOpen}
>
<!-- Top navbar -->
<header class="main-header">
<button
on:click={toggleSidebar}
class="btn-secondary rounded-full p-2"
aria-label="Toggle sidebar"
>
<ToggleSidebar />
</button>
<div class="flex items-center space-x-4 pl-2">
{#if $currentFullNote}
<button on:click={toggleEditMode} class="btn-primary rounded-full">
{isEditing ? "Preview" : "Edit"}
</button>
{/if}
</div>
<div class="flex items-center space-x-4 pl-2">
<ThemeToggle />
</div>
</header>
<!-- Note content area -->
<main class="main-content">
{#if $currentFullNote}
<NoteEditor note={$currentFullNote} {isEditing} {saveNote} />
{:else}
<div class="flex h-full flex-col items-center justify-center">
<p class="mb-4 text-lg">None selected</p>
<button on:click={createNewNote} class="btn-primary">Create note</button>
</div>
{/if}
</main>
</div>
<!-- Settings Modal -->
{#if showSettings}
<SettingsModal onClose={toggleSettings} />
{/if}
</div>