diff --git a/app/desktop/src/main/filesystem.ts b/app/desktop/src/main/filesystem.ts index f16882f..e1c7e84 100644 --- a/app/desktop/src/main/filesystem.ts +++ b/app/desktop/src/main/filesystem.ts @@ -35,10 +35,19 @@ export class DesktopDocument extends EventEmitter { return this.fileStatus.path; } - get saved() { + get needsSave() { + // Check for blank, unsaved documents + if ( + !this.fileStatus.path && + (!this.content || this.content.doc.eq(Text.empty)) + ) { + return false; + } + + // Then check if the document has been edited return this.fileStatus.version === this.content?.version - ? this.fileStatus.saved - : false; + ? !this.fileStatus.saved + : true; } constructor( diff --git a/app/desktop/src/main/index.ts b/app/desktop/src/main/index.ts index 76be368..51502a0 100644 --- a/app/desktop/src/main/index.ts +++ b/app/desktop/src/main/index.ts @@ -8,7 +8,7 @@ fixPath(); import { autoUpdater } from "electron-updater"; -autoUpdater.checkForUpdatesAndNotify(); +// autoUpdater.checkForUpdatesAndNotify(); import { StateManagement } from "@core/state"; @@ -49,10 +49,17 @@ const createWindow = ( }) ); + listeners.push( + filesystem.on("current", (doc) => { + if (doc) send("setCurrent", { id: doc.id }); + }) + ); + // Attach file handlers listeners.push( filesystem.on("open", (document) => { - let { id, path, content, saved } = document; + let { id, path, content, fileStatus } = document; + let { saved } = fileStatus; let docListeners: typeof listeners = []; docsListeners[id] = docListeners; @@ -172,6 +179,23 @@ const createWindow = ( window.loadFile("./build/renderer/index.html"); + window.on("close", async (event) => { + let docs = [...filesystem.docs.values()]; + + if (!docs.some((doc) => doc.needsSave)) return; + + event.preventDefault(); + + try { + await closeAll(window); + window.close(); + } catch (error) { + if (!(error instanceof CancelledError)) { + console.log("Unexpected Error: " + (error as Error).message); + } + } + }); + window.on("closed", () => { for (let listener of listeners) { listener(); @@ -279,9 +303,14 @@ async function close({ window, id }: CloseOptions) { id = id ?? filesystem.currentDocID; let document = id ? filesystem.getDoc(id) : filesystem.currentDoc; - if (!id || !document) throw Error("Tried to close a non-existent document"); + if (!id || !document) { + if (id) { + send("close", { id }); + } + return; + } - if (!document.saved) { + if (document.needsSave) { let { response } = await dialog.showMessageBox(window, { type: "warning", message: "Do you want to save your changes?", @@ -312,6 +341,56 @@ async function close({ window, id }: CloseOptions) { send("close", { id }); } +class CancelledError extends Error { + constructor() { + super("Close All action was cancelled"); + } +} + +async function closeAll(window?: BrowserWindow) { + if (!window) return; + + let [send] = wrapIPC(window.webContents); + + let docs = [...filesystem.docs.values()]; + + if (docs.some((doc) => doc.needsSave)) { + let { response } = await dialog.showMessageBox(window, { + type: "warning", + message: "Do you want to save your changes?", + buttons: ["Save", "Don't Save", "Cancel"], + }); + + // Cancelled + if (response === 2) throw new CancelledError(); + + // Save + if (response === 0) { + for (let doc of docs) { + if (doc.needsSave) { + if (doc.path !== null) { + doc.save(); + } else { + filesystem.currentDocID = doc.id; + let { canceled, filePath } = await dialog.showSaveDialog(window); + + if (!canceled && filePath) { + await doc.save(filePath); + } else { + throw new CancelledError(); + } + } + } + } + } + } + + // Close all documents + await Promise.all( + docs.map((doc) => doc.close().then(() => send("close", { id: doc.id }))) + ); +} + menu.on("about", showAbout); function showAbout(window?: BrowserWindow) { if (window) { diff --git a/app/desktop/src/renderer/file.ts b/app/desktop/src/renderer/file.ts index 97d8dbe..4cb0189 100644 --- a/app/desktop/src/renderer/file.ts +++ b/app/desktop/src/renderer/file.ts @@ -1,6 +1,12 @@ import { ElectronAPI } from "../preload"; -import { EditorState, StateField, StateEffect, Facet } from "@codemirror/state"; +import { + EditorState, + StateField, + StateEffect, + Facet, + Text, +} from "@codemirror/state"; import { ViewPlugin } from "@codemirror/view"; import { SavedStatus } from "../main/filesystem"; @@ -79,7 +85,13 @@ export function fileSync( } export function getSaveStatus(state: EditorState) { - const { version, thisVersion, saved } = state.field(saveState); + const { version, thisVersion, path, saved } = state.field(saveState); + + // Empty docs are never unsaved + // TODO: Have this check be done in the main process + if (!path && state.doc.eq(Text.empty)) { + return true; + } return version === thisVersion ? saved : false; }