diff --git a/data/ui/notes.ui b/data/ui/notes.ui index 267477d..c5256ac 100644 --- a/data/ui/notes.ui +++ b/data/ui/notes.ui @@ -126,6 +126,14 @@ Keyboard Shortcuts win.show-help-overlay + + Import Notes… + app.import + + + Export All Notes… + app.export + About Sticky Notes app.about diff --git a/po/pt_BR.po b/po/pt_BR.po index b65cc0a..8261d26 100644 --- a/po/pt_BR.po +++ b/po/pt_BR.po @@ -504,3 +504,24 @@ msgstr "Nota vazia" #~ msgid "First beta release!" #~ msgstr "Primeiro lançamento beta!" + +#: src/application.ts:552 +msgid "Export Notes" +msgstr "Exportar notas" + +#: src/application.ts:581 +msgid "Failed to Export Notes" +msgstr "Falha ao exportar notas" + +#: src/application.ts:584 src/application.ts:613 +msgid "OK" +msgstr "OK" + +#: src/application.ts:592 +msgid "Import Notes" +msgstr "Importar notas" + +#: src/application.ts:610 +msgid "Failed to Import Notes" +msgstr "Falha ao importar notas" + diff --git a/src/application.ts b/src/application.ts index 8d29ddc..61dadaf 100644 --- a/src/application.ts +++ b/src/application.ts @@ -31,7 +31,7 @@ import Gtk from "gi://Gtk?version=4.0"; import Gdk from "gi://Gdk?version=4.0"; import { StickyNotes } from "./notes.js"; -import { Note, settings } from "./util.js"; +import { INote, Note, settings } from "./util.js"; import { delete_note, load_notes, @@ -293,6 +293,14 @@ export class Application extends Adw.Application { save.connect("activate", () => this.save()); this.add_action(save); + const export_notes = new Gio.SimpleAction({ name: "export" }); + export_notes.connect("activate", () => this.export_notes()); + this.add_action(export_notes); + + const import_notes = new Gio.SimpleAction({ name: "import" }); + import_notes.connect("activate", () => this.import_notes()); + this.add_action(import_notes); + this.add_action(settings.create_action("color-scheme")); this.set_accels_for_action("app.quit", ["q"]); @@ -539,6 +547,87 @@ export class Application extends Adw.Application { window.present(); } + async export_notes() { + const dialog = new Gtk.FileDialog({ + title: _("Export Notes"), + initial_name: "notes.json", + }); + + try { + const file = await dialog.save(this.active_window, null); + if (!file) return; + + const notes = this.notes_array(); + const data = { + v: 1, + notes: notes.map((n) => n.toJSON()), + }; + const encoder = new TextEncoder(); + file.replace_contents( + encoder.encode(JSON.stringify(data, null, 2)), + null, + false, + Gio.FileCreateFlags.NONE, + null, + ); + } catch (err) { + if (err instanceof GLib.Error && err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.CANCELLED)) { + return; + } + console.error("Failed to export notes:", err); + const error_dialog = Adw.MessageDialog.new( + this.active_window, + _("Failed to Export Notes"), + err instanceof Error ? err.message : String(err) + ); + error_dialog.add_response("ok", _("OK")); + error_dialog.present(); + } + } + + async import_notes() { + const dialog = new Gtk.FileDialog({ + title: _("Import Notes"), + }); + + try { + const file = await dialog.open(this.active_window, null); + if (!file) return; + + const [success, contents] = file.load_contents(null); + if (!success) return; + + const decoder = new TextDecoder(); + const data = JSON.parse(decoder.decode(contents)); + + if (data.v !== 1 || !Array.isArray(data.notes)) return; + + data.notes.forEach((noteData: INote) => { + // Avoid duplicates by UUID + if (!this.find_note(noteData.uuid)) { + const note = new Note(noteData); + note.connect("notify::modified", () => save_note(note)); + note.connect("notify::open", () => save_note(note)); + this.notes_list.append(note); + save_note(note); + } + }); + this.sort_notes(); + } catch (err) { + if (err instanceof GLib.Error && err.matches(Gio.io_error_quark(), Gio.IOErrorEnum.CANCELLED)) { + return; + } + console.error("Failed to import notes:", err); + const error_dialog = Adw.MessageDialog.new( + this.active_window, + _("Failed to Import Notes"), + err instanceof Error ? err.message : String(err) + ); + error_dialog.add_response("ok", _("OK")); + error_dialog.present(); + } + } + vfunc_startup() { super.vfunc_startup(); diff --git a/src/util.ts b/src/util.ts index 66a36c2..a3f322a 100644 --- a/src/util.ts +++ b/src/util.ts @@ -41,6 +41,9 @@ Gio._promisify( "replace_contents_finish", ); +Gio._promisify(Gtk.FileDialog.prototype, "save", "save_finish"); +Gio._promisify(Gtk.FileDialog.prototype, "open", "open_finish"); + export interface ITag { name: string; start: number;