188 lines
4.3 KiB
Svelte
188 lines
4.3 KiB
Svelte
<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>
|