Auto-save entries and lists on navigate-away and app close
- Add saveCurrentDocument() that persists entries (via saveEntryFromStore) and lists (via updateListByStoreId) when switching documents or sections - Register flush callback in session store so +layout.svelte can trigger a save before vault rebuild on window close - Remove explicit Save button from MarkdownEditor (no longer needed) - Simplify MarkdownEditor props (removed activeSection, onOpenDocument, onDeleteDocument since save is now handled by the parent) - Todos already save instantly; fragments keep their form-based workflow Co-Authored-By: Oz <oz-agent@warp.dev>
This commit is contained in:
parent
c7933aeeec
commit
58f9f46cb9
@ -36,13 +36,10 @@
|
||||
/>
|
||||
{:else}
|
||||
<MarkdownEditor
|
||||
{activeSection}
|
||||
{openDocumentId}
|
||||
{openDocumentName}
|
||||
{openDocumentContent}
|
||||
{onDocumentContentChange}
|
||||
{onOpenDocument}
|
||||
{onDeleteDocument}
|
||||
/>
|
||||
{/if}
|
||||
</main>
|
||||
|
||||
@ -1,23 +1,15 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
saveEntryFromStore,
|
||||
type EntryItem,
|
||||
} from "$lib/stores/entries";
|
||||
import { renderMarkdown, extractEditorTitle } from "$lib/utils/markdown";
|
||||
|
||||
export let activeSection = "entries";
|
||||
export let openDocumentId = "";
|
||||
export let openDocumentName = "";
|
||||
export let openDocumentContent = "";
|
||||
export let onDocumentContentChange: (content: string) => void = () => {};
|
||||
export let onOpenDocument: (doc: { id: string; label: string; initialContent: string }) => void = () => {};
|
||||
export let onDeleteDocument: (id: string) => void = () => {};
|
||||
|
||||
let markdownText = openDocumentContent;
|
||||
let lastOpenDocumentId = openDocumentId;
|
||||
let previewOnly = true;
|
||||
let editorInput: HTMLTextAreaElement | null = null;
|
||||
let entrySaveBusy = false;
|
||||
|
||||
function updateDraft(value: string) {
|
||||
markdownText = value;
|
||||
@ -69,28 +61,6 @@
|
||||
applyWrap("[", "](https://example.com)");
|
||||
}
|
||||
|
||||
async function saveEntryDocument() {
|
||||
if (activeSection !== "entries") return;
|
||||
|
||||
try {
|
||||
entrySaveBusy = true;
|
||||
const previousId = openDocumentId;
|
||||
const saved: EntryItem | null = await saveEntryFromStore(previousId, markdownText, "Overwrite");
|
||||
if (!saved) return;
|
||||
|
||||
if (saved.id !== previousId) {
|
||||
onDeleteDocument(previousId);
|
||||
}
|
||||
|
||||
onOpenDocument(saved);
|
||||
onDocumentContentChange(saved.initialContent);
|
||||
} catch (error) {
|
||||
console.error("[editor] entries:save:error", error);
|
||||
} finally {
|
||||
entrySaveBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
$: if (openDocumentId !== lastOpenDocumentId) {
|
||||
markdownText = openDocumentContent;
|
||||
lastOpenDocumentId = openDocumentId;
|
||||
@ -102,11 +72,6 @@
|
||||
<header class="editor-header">
|
||||
<h1>{editorTitle}</h1>
|
||||
<div class="editor-actions">
|
||||
{#if activeSection === "entries"}
|
||||
<button type="button" on:click={saveEntryDocument} disabled={entrySaveBusy}>
|
||||
{entrySaveBusy ? "Saving..." : "Save"}
|
||||
</button>
|
||||
{/if}
|
||||
<button type="button" class:primary={!previewOnly} on:click={() => (previewOnly = false)}>Write</button>
|
||||
<button type="button" class:primary={previewOnly} on:click={() => (previewOnly = true)}>Preview</button>
|
||||
</div>
|
||||
|
||||
@ -22,3 +22,13 @@ export function clearVaultSession(): void {
|
||||
_password.set(null);
|
||||
_unlocked.set(false);
|
||||
}
|
||||
|
||||
let _flushCallback: (() => Promise<void>) | null = null;
|
||||
|
||||
export function setFlushCallback(fn: () => Promise<void>): void {
|
||||
_flushCallback = fn;
|
||||
}
|
||||
|
||||
export async function flushBeforeClose(): Promise<void> {
|
||||
if (_flushCallback) await _flushCallback();
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { onMount } from "svelte";
|
||||
import { getSessionPassword, clearVaultSession } from "$lib/stores/session";
|
||||
import { getSessionPassword, clearVaultSession, flushBeforeClose } from "$lib/stores/session";
|
||||
import { persistAndClearVault } from "$lib/backend/auth";
|
||||
|
||||
let closeInProgress = false;
|
||||
@ -14,6 +14,8 @@
|
||||
event.preventDefault();
|
||||
closeInProgress = true;
|
||||
|
||||
try { await flushBeforeClose(); } catch {}
|
||||
|
||||
const password = getSessionPassword();
|
||||
if (password) {
|
||||
try {
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
import { goto } from "$app/navigation";
|
||||
import { unlockVaultWorkspace } from "$lib/backend/auth";
|
||||
import AppModal from "$lib/components/AppModal.svelte";
|
||||
import { entriesStore, getDefaultEntry, hydrateEntries, loadEntryByStoreId } from "$lib/stores/entries";
|
||||
import { entriesStore, getDefaultEntry, hydrateEntries, loadEntryByStoreId, saveEntryFromStore } from "$lib/stores/entries";
|
||||
import { hydrateFragments } from "$lib/stores/fragments";
|
||||
import { hydrateLists } from "$lib/stores/lists";
|
||||
import { isVaultReady, setVaultSession } from "$lib/stores/session";
|
||||
import { hydrateLists, updateListByStoreId } from "$lib/stores/lists";
|
||||
import { isVaultReady, setFlushCallback, setVaultSession } from "$lib/stores/session";
|
||||
import { hydrateTodos } from "$lib/stores/todos";
|
||||
import Navbar from "$lib/components/Navbar.svelte";
|
||||
import SidePanel from "$lib/components/SidePanel.svelte";
|
||||
@ -195,6 +195,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function saveCurrentDocument() {
|
||||
if (!activeDocumentId) return;
|
||||
const content = openDocuments[activeDocumentId];
|
||||
if (!content?.trim()) return;
|
||||
|
||||
try {
|
||||
if (selectedSection === "entries") {
|
||||
const saved = await saveEntryFromStore(activeDocumentId, content, "Overwrite");
|
||||
if (saved && saved.id !== activeDocumentId) {
|
||||
const { [activeDocumentId]: _, ...rest } = openDocuments;
|
||||
openDocuments = { ...rest, [saved.id]: saved.initialContent };
|
||||
activeDocumentId = saved.id;
|
||||
activeDocumentLabel = saved.label;
|
||||
}
|
||||
} else if (selectedSection === "lists" && activeDocumentId.startsWith("lists/") && !activeDocumentId.startsWith("lists/draft-")) {
|
||||
await updateListByStoreId(activeDocumentId, undefined, content);
|
||||
}
|
||||
} catch {
|
||||
// best-effort save
|
||||
}
|
||||
}
|
||||
|
||||
function handleSelect(id: string) {
|
||||
if (id === "account" || id === "settings") {
|
||||
goto(`/${id}`);
|
||||
@ -219,6 +241,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
saveCurrentDocument();
|
||||
selectedSection = id;
|
||||
panelOpen = true;
|
||||
activeDocumentId = "";
|
||||
@ -226,6 +249,7 @@
|
||||
}
|
||||
|
||||
async function handleOpenDocument(doc: OpenDocument) {
|
||||
await saveCurrentDocument();
|
||||
let resolvedDoc = doc;
|
||||
if (selectedSection === "entries" && doc.id.startsWith("entries/file/") && !doc.initialContent) {
|
||||
try {
|
||||
@ -255,6 +279,7 @@
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
setFlushCallback(saveCurrentDocument);
|
||||
bootstrapFragmentsWithUnlock();
|
||||
});
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user