From c385eae0c79e9cc55a4f7f8511707cedd19b2c29 Mon Sep 17 00:00:00 2001 From: SSylvain1989 Date: Tue, 3 Mar 2026 21:27:46 +0100 Subject: [PATCH 1/2] Save note content when editor disappears Extract persistNote() from saveNote() to allow saving without closing the editor. Call persistNote() from onDisappear (when notch retracts) and cancelEdit() so user changes are never silently discarded. Empty new notes (no title, no content, no image) are skipped. --- .../components/Notch/NotchNotesView.swift | 85 +++++++++++-------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/DynamicIsland/components/Notch/NotchNotesView.swift b/DynamicIsland/components/Notch/NotchNotesView.swift index d78a1b8..177b3d9 100644 --- a/DynamicIsland/components/Notch/NotchNotesView.swift +++ b/DynamicIsland/components/Notch/NotchNotesView.swift @@ -102,6 +102,9 @@ struct NotchNotesView: View { updateLayoutState() } .onDisappear { + if isEditingNewNote || selectedNoteId != nil { + persistNote() + } coordinator.notesLayoutState = .list } .onChange(of: isEditingNewNote) { _, _ in @@ -197,48 +200,55 @@ struct NotchNotesView: View { isEditingNewNote = false } - private func saveNote() { + private func persistNote() { + guard let id = editorNoteId else { return } + + let isExistingNote = savedNotes.contains(where: { $0.id == id }) + if !isExistingNote && editorTitle.isEmpty && editorContent.isEmpty && editorImageData == nil { + return + } + var notes = savedNotes let now = Date() - - if let id = editorNoteId { - // Priority: Save image to disk if it exists - var fileName: String? = nil - if let data = editorImageData { - let name = "note_image_\(id.uuidString).png" - let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(name) - try? data.write(to: fileURL) - fileName = name + + var fileName: String? = nil + if let data = editorImageData { + let name = "note_image_\(id.uuidString).png" + let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(name) + try? data.write(to: fileURL) + fileName = name + } + + if let index = notes.firstIndex(where: { $0.id == id }) { + // Update + if let oldFileName = notes[index].imageFileName, oldFileName != fileName { + let oldFileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(oldFileName) + try? FileManager.default.removeItem(at: oldFileURL) } - if let index = notes.firstIndex(where: { $0.id == id }) { - // Update - // If there was an old image and it's being replaced or removed, delete old file - if let oldFileName = notes[index].imageFileName, oldFileName != fileName { - let oldFileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(oldFileName) - try? FileManager.default.removeItem(at: oldFileURL) - } - - notes[index].title = editorTitle - notes[index].content = editorContent - notes[index].imageFileName = fileName - notes[index].colorIndex = editorColorIndex - } else { - // Create - let newNote = NoteItem( - id: id, - title: editorTitle.isEmpty ? "Untitled Note" : editorTitle, - content: editorContent, - creationDate: now, - colorIndex: editorColorIndex, - isPinned: false, - imageFileName: fileName - ) - notes.insert(newNote, at: 0) - } - savedNotes = notes // Persistence fix: save back to @Default + notes[index].title = editorTitle + notes[index].content = editorContent + notes[index].imageFileName = fileName + notes[index].colorIndex = editorColorIndex + } else { + // Create + let newNote = NoteItem( + id: id, + title: editorTitle.isEmpty ? "Untitled Note" : editorTitle, + content: editorContent, + creationDate: now, + colorIndex: editorColorIndex, + isPinned: false, + imageFileName: fileName + ) + notes.insert(newNote, at: 0) } - + + savedNotes = notes + } + + private func saveNote() { + persistNote() closeEditor() } @@ -311,6 +321,7 @@ struct NotchNotesView: View { } private func cancelEdit() { + persistNote() closeEditor() } From 626518ef8bbbd269b12adb7f1929571a221c34cf Mon Sep 17 00:00:00 2001 From: SSylvain1989 Date: Tue, 3 Mar 2026 21:31:13 +0100 Subject: [PATCH 2/2] Add debounced auto-save while typing Schedule a persist after 1.5s of inactivity on title, content or color changes. The pending task is cancelled on each new keystroke and when the editor closes. --- .../components/Notch/NotchNotesView.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/DynamicIsland/components/Notch/NotchNotesView.swift b/DynamicIsland/components/Notch/NotchNotesView.swift index 177b3d9..4fe1119 100644 --- a/DynamicIsland/components/Notch/NotchNotesView.swift +++ b/DynamicIsland/components/Notch/NotchNotesView.swift @@ -36,6 +36,7 @@ struct NotchNotesView: View { @State private var editorImageData: Data? = nil @State private var editorColorIndex: Int = 0 @State private var editorNoteId: UUID? + @State private var autoSaveTask: Task? @Default(.enableNotes) var enableNotes @@ -122,6 +123,15 @@ struct NotchNotesView: View { .onChange(of: enableNotes) { _, _ in updateLayoutState() } + .onChange(of: editorContent) { _, _ in + scheduleAutoSave() + } + .onChange(of: editorTitle) { _, _ in + scheduleAutoSave() + } + .onChange(of: editorColorIndex) { _, _ in + scheduleAutoSave() + } } // MARK: - Actions @@ -247,6 +257,19 @@ struct NotchNotesView: View { savedNotes = notes } + private func scheduleAutoSave() { + guard isEditingNewNote || selectedNoteId != nil else { return } + + autoSaveTask?.cancel() + autoSaveTask = Task { + try? await Task.sleep(nanoseconds: 1_500_000_000) + guard !Task.isCancelled else { return } + await MainActor.run { + persistNote() + } + } + } + private func saveNote() { persistNote() closeEditor() @@ -338,6 +361,8 @@ struct NotchNotesView: View { } private func closeEditor() { + autoSaveTask?.cancel() + autoSaveTask = nil isEditingNewNote = false selectedNoteId = nil // Tiny delay to clear state after animation