diff --git a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte index 8be42771..f1d4a46f 100644 --- a/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte +++ b/src/routes/journals/ae_comp__journal_entry_obj_id_view.svelte @@ -59,7 +59,6 @@ function deep_copy(obj: any) { if (!obj) return null; try { - // Manual selective copy to avoid proxy issues and handle Dates const copy: key_val = {}; for (const key in obj) { const val = obj[key]; @@ -68,7 +67,6 @@ } else if (Array.isArray(val)) { copy[key] = [...val]; } else if (typeof val === 'object' && val !== null) { - // Simple object copy (shallow for nested to avoid recursion) copy[key] = { ...val }; } else { copy[key] = val; @@ -85,7 +83,6 @@ let has_unsaved_changes = $derived.by(() => { if (!tmp_entry_obj || !orig_entry_obj || is_processing) return false; - // Use normalization to avoid loop on whitespace/null differences const normalize = (val: any) => (val === null || val === undefined) ? '' : String(val).trim(); const content_changed = normalize(tmp_entry_obj.content) !== normalize(orig_entry_obj.content); @@ -106,27 +103,22 @@ // 1. Initial Load & Background Sync $effect(() => { - const entry = $lq__journal_entry_obj; - const journal = $lq__journal_obj; - if (entry && (entry.updated_on || entry.created_on)) { - - // ISOLATE Store checks to avoid re-triggering this effect when stores change - const is_decrypted = untrack(() => { - const session_kv = $journals_sess?.journal_kv[journal?.id]; - return session_kv?.journal_passcode_decrypted === true; - }); + const entry = $lq__journal_entry_obj; // Track only entry + + untrack(() => { + const journal = $lq__journal_obj; + if (!entry || !(entry.updated_on || entry.created_on)) return; - console.log(`ae_view: Effect[Sync] trigger. Decrypted: ${is_decrypted}. Status: ${save_status}`); + const session_kv = $journals_sess?.journal_kv[journal?.id]; + const is_decrypted = session_kv?.journal_passcode_decrypted === true; // Only sync if saved and not currently processing/editing if (save_status === 'saved' && !has_unsaved_changes && !is_processing) { - // If decrypted, don't let DB's null content overwrite local recovered text + // Prevent overwrite of recovered text if we are in a decrypted session if (is_decrypted && tmp_entry_obj.content && !entry.content) { - console.log('ae_view: Effect[Sync] preserving decrypted content.'); return; } - console.log('ae_view: Effect[Sync] update local state.'); const base = deep_copy(entry); if (base) { base.content = base.content ?? null; @@ -135,29 +127,31 @@ tmp_entry_obj = { ...base }; } } - } + }); }); // 2. Auto-Save Debounce $effect(() => { - // Dependency tracking + // Track core properties const _content = tmp_entry_obj.content; const _private = tmp_entry_obj.private; - if (has_unsaved_changes && !is_processing) { - console.log('ae_view: Effect[AutoSave] trigger.'); + // Isolate logic from secondary dependencies + const should_save = untrack(() => has_unsaved_changes && !is_processing && save_status !== 'saving'); + + if (should_save) { if (save_status !== 'saving') save_status = 'unsaved'; const auto_save_enabled = untrack(() => $journals_loc.entry.auto_save); if (auto_save_enabled) { clearTimeout(auto_save_timer); auto_save_timer = setTimeout(() => { - if (untrack(() => has_unsaved_changes && save_status !== 'saving')) { + if (untrack(() => has_unsaved_changes && !is_processing && save_status !== 'saving')) { update_journal_entry(); } }, 2000); } - } else if (save_status !== 'saving' && !has_unsaved_changes) { + } else if (save_status === 'unsaved' && !has_unsaved_changes) { save_status = 'saved'; } }); @@ -165,16 +159,17 @@ // 3. Auto-Decryption Workflow $effect(() => { const journal = $lq__journal_obj; - if (!journal?.id) return; + const entry = $lq__journal_entry_obj; + if (!journal?.id || !entry) return; + // Track session state selectively const session = $journals_sess?.journal_kv[journal.id]; const is_verified = session?.journal_passcode_verified; const decrypted_status = session?.journal_passcode_decrypted; - const has_encrypted_content = !!$lq__journal_entry_obj?.content_encrypted; + const has_encrypted_content = !!entry.content_encrypted; if (has_encrypted_content && is_verified && decrypted_status !== true && decrypted_status !== 'processing') { - console.log('ae_view: Auto-decrypt triggering.'); - run_decryption_workflow(); + untrack(() => run_decryption_workflow()); } }); @@ -209,9 +204,6 @@ } // SUCCESS - console.log(`ae_view: Decryption SUCCESS.`); - - // Update both to prevent "unsaved changes" loop const content = result.content || ''; tmp_entry_obj.content = content; tmp_entry_obj.content_md_html = handle_marked(content); @@ -237,10 +229,8 @@ } async function update_journal_entry() { - if (!$ae_loc.trusted_access || save_status === 'saving') return; + if (!$ae_loc.trusted_access || save_status === 'saving' || is_processing) return; - // Prevent concurrent saves - if (is_processing) return; is_processing = true; save_status = 'saving'; @@ -278,7 +268,6 @@ } try { - console.log('ae_view: Starting PATCH update...'); await journals_func.update_ae_obj__journal_entry({ api_cfg: $ae_api, journal_entry_id: $lq__journal_entry_obj?.journal_entry_id, @@ -286,10 +275,9 @@ log_lvl: 1 }); - // Re-sync after successful save + // CRITICAL: Sync ORIG after save to clear unsaved changes orig_entry_obj = deep_copy(tmp_entry_obj); save_status = 'saved'; - console.log('ae_view: PATCH complete and state synced.'); } catch (error) { console.error('Update failed:', error); save_status = 'unsaved'; @@ -411,4 +399,4 @@ Loading Journal Entry... {/if} - + \ No newline at end of file