From b9fd1615516d75c03588e2810e079778d5b203d9 Mon Sep 17 00:00:00 2001 From: Jonatan Heyman Date: Fri, 5 Jan 2024 00:41:46 +0100 Subject: [PATCH] Add functionality for selecting a custom path for the buffer file. (#130) Automatically reload the buffer file (if one exists) when buffer path is changed. Move code buffer related code from main/index.ts into buffer.js --- electron/main/buffer.js | 84 +++++++++++++++++++++++++++- electron/main/index.ts | 44 ++++----------- electron/preload/index.ts | 4 ++ src/components/Editor.vue | 2 +- src/components/settings/Settings.vue | 61 ++++++++++++++++++++ 5 files changed, 159 insertions(+), 36 deletions(-) diff --git a/electron/main/buffer.js b/electron/main/buffer.js index cec63d79..37ccf3a8 100644 --- a/electron/main/buffer.js +++ b/electron/main/buffer.js @@ -1,17 +1,23 @@ import fs from "fs" import { join, dirname, basename } from "path" -import { app } from "electron" +import { app, ipcMain, dialog } from "electron" import * as jetpack from "fs-jetpack"; import CONFIG from "../config" import { isDev } from "../detect-platform" +import { win } from "./index" +import { eraseInitialContent, initialContent, initialDevContent } from '../initial-content' +export function constructBufferFilePath(directoryPath) { + return join(directoryPath, isDev ? "buffer-dev.txt" : "buffer.txt") +} + export function getBufferFilePath() { let defaultPath = app.getPath("userData") let configPath = CONFIG.get("settings.bufferPath") let bufferPath = configPath.length ? configPath : defaultPath - let bufferFilePath = join(bufferPath, isDev ? "buffer-dev.txt" : "buffer.txt") + let bufferFilePath = constructBufferFilePath(bufferPath) try { // use realpathSync to resolve a potential symlink return fs.realpathSync(bufferFilePath) @@ -73,10 +79,82 @@ export class Buffer { if (this._lastSavedContent !== content) { // file has changed on disk, trigger onChange - this.onChange({filename, eventType, content}) + this.onChange(content) } } ) } } + + close() { + if (this.watcher) { + this.watcher.close() + this.watcher = null + } + } +} + + +// Buffer +let buffer +export function loadBuffer() { + if (buffer) { + buffer.close() + } + buffer = new Buffer({ + filePath: getBufferFilePath(), + onChange: (content) => { + win?.webContents.send("buffer-content:change", content) + }, + }) + return buffer +} + +ipcMain.handle('buffer-content:load', async () => { + if (buffer.exists() && !(eraseInitialContent && isDev)) { + return await buffer.load() + } else { + return isDev ? initialDevContent : initialContent + } +}); + +async function save(content) { + return await buffer.save(content) } + +ipcMain.handle('buffer-content:save', async (event, content) => { + return await save(content) +}); + +export let contentSaved = false +ipcMain.handle('buffer-content:saveAndQuit', async (event, content) => { + await save(content) + contentSaved = true + app.quit() +}) + +ipcMain.handle("buffer-content:selectLocation", async () => { + let result = await dialog.showOpenDialog({ + title: "Select directory to store buffer", + properties: [ + "openDirectory", + "createDirectory", + "noResolveAliases", + ], + }) + if (result.canceled) { + return + } + const filePath = result.filePaths[0] + if (fs.existsSync(constructBufferFilePath(filePath))) { + if (dialog.showMessageBoxSync({ + type: "question", + message: "The selected directory already contains a buffer file. It will be loaded. Do you want to continue?", + buttons: ["Cancel", "Continue"], + }) === 0) { + return + } + } + return filePath +}) + diff --git a/electron/main/index.ts b/electron/main/index.ts index b764c723..41fdfb54 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -1,16 +1,16 @@ import { app, BrowserWindow, Tray, shell, ipcMain, Menu, nativeTheme, globalShortcut, nativeImage } from 'electron' import { release } from 'node:os' import { join } from 'node:path' +import fs from "fs" import { menu, getTrayMenu } from './menu' -import { eraseInitialContent, initialContent, initialDevContent } from '../initial-content' import { WINDOW_CLOSE_EVENT, SETTINGS_CHANGE_EVENT } from '../constants'; import CONFIG from "../config" import { onBeforeInputEvent } from "../keymap" import { isDev, isMac, isWindows } from '../detect-platform'; import { initializeAutoUpdate, checkForUpdates } from './auto-update'; import { fixElectronCors } from './cors'; -import { getBufferFilePath, Buffer } from './buffer'; +import { loadBuffer, contentSaved } from './buffer'; // The built directory structure @@ -57,7 +57,6 @@ const url = process.env.VITE_DEV_SERVER_URL const indexHtml = join(process.env.DIST, 'index.html') let currentKeymap = CONFIG.get("settings.keymap") -let contentSaved = false // if this version is a beta version, set the release channel to beta const isBetaVersion = app.getVersion().includes("beta") @@ -250,43 +249,18 @@ ipcMain.handle('dark-mode:set', (event, mode) => { ipcMain.handle('dark-mode:get', () => nativeTheme.themeSource) +// load buffer on app start +loadBuffer() -const buffer = new Buffer({ - filePath: getBufferFilePath(), - onChange: (eventData) => { - win?.webContents.send("buffer-content:change", eventData) - }, -}) - -ipcMain.handle('buffer-content:load', async () => { - if (buffer.exists() && !(eraseInitialContent && isDev)) { - return await buffer.load() - } else { - return isDev ? initialDevContent : initialContent - } -}); - -async function save(content) { - return await buffer.save(content) -} -ipcMain.handle('buffer-content:save', async (event, content) => { - return await save(content) -}); - -ipcMain.handle('buffer-content:saveAndQuit', async (event, content) => { - await save(content) - contentSaved = true - app.quit() -}) - -ipcMain.handle('settings:set', (event, settings) => { +ipcMain.handle('settings:set', async (event, settings) => { if (settings.keymap !== CONFIG.get("settings.keymap")) { currentKeymap = settings.keymap } let globalHotkeyChanged = settings.enableGlobalHotkey !== CONFIG.get("settings.enableGlobalHotkey") || settings.globalHotkey !== CONFIG.get("settings.globalHotkey") let showInDockChanged = settings.showInDock !== CONFIG.get("settings.showInDock"); let showInMenuChanged = settings.showInMenu !== CONFIG.get("settings.showInMenu"); + let bufferPathChanged = settings.bufferPath !== CONFIG.get("settings.bufferPath"); CONFIG.set("settings", settings) win?.webContents.send(SETTINGS_CHANGE_EVENT, settings) @@ -300,4 +274,10 @@ ipcMain.handle('settings:set', (event, settings) => { if (showInMenuChanged) { registerShowInMenu() } + if (bufferPathChanged) { + const buffer = loadBuffer() + if (buffer.exists()) { + win?.webContents.send("buffer-content:change", await buffer.load()) + } + } }) diff --git a/electron/preload/index.ts b/electron/preload/index.ts index bf0f7f09..35cb5b16 100644 --- a/electron/preload/index.ts +++ b/electron/preload/index.ts @@ -58,6 +58,10 @@ contextBridge.exposeInMainWorld("heynote", { onChangeCallback(callback) { ipcRenderer.on("buffer-content:change", callback) }, + + async selectLocation() { + return await ipcRenderer.invoke("buffer-content:selectLocation") + } }, settings: CONFIG.get("settings"), diff --git a/src/components/Editor.vue b/src/components/Editor.vue index 49db4b73..35522c2d 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -71,7 +71,7 @@ window.document.addEventListener("currenciesLoaded", this.onCurrenciesLoaded) // set up buffer change listener - window.heynote.buffer.onChangeCallback((event, {filename, content, eventType}) => { + window.heynote.buffer.onChangeCallback((event, content) => { diskContent = content this.editor.setContent(content) }) diff --git a/src/components/settings/Settings.vue b/src/components/settings/Settings.vue index 5f330ca1..541ed67f 100644 --- a/src/components/settings/Settings.vue +++ b/src/components/settings/Settings.vue @@ -32,9 +32,11 @@ showInMenu: this.initialSettings.showInMenu, bracketClosing: this.initialSettings.bracketClosing, autoUpdate: this.initialSettings.autoUpdate, + bufferPath: this.initialSettings.bufferPath, activeTab: "general", isWebApp: window.heynote.isWebApp, + customBufferLocation: !!this.initialSettings.bufferPath, } }, @@ -66,8 +68,24 @@ showInMenu: this.showInMenu || !this.showInDock, autoUpdate: this.autoUpdate, bracketClosing: this.bracketClosing, + bufferPath: this.bufferPath, }) }, + + async selectBufferLocation() { + const path = await window.heynote.buffer.selectLocation() + if (path) { + this.bufferPath = path + this.updateSettings() + } + }, + + onCustomBufferLocationChange() { + if (!this.customBufferLocation) { + this.bufferPath = "" + this.updateSettings() + } + }, } } @@ -166,6 +184,26 @@ +
+
+

Buffer File Path

+ +
+ + {{ bufferPath }} +
+
+
@@ -318,6 +356,8 @@ .entry margin-bottom: 24px margin-right: 20px + &:last-child + margin-right: 0 h2 font-weight: 600 margin-bottom: 10px @@ -335,6 +375,27 @@ position: relative top: 2px left: -3px + .buffer-location + width: 100% + .file-path + display: flex + > button + flex-shrink: 0 + padding: 3px 8px + .path + flex-grow: 1 + margin-left: 10px + font-size: 12px + font-family: "Hack" + padding: 5px 8px + border-radius: 3px + background: #f1f1f1 + color: #555 + white-space: nowrap + overflow-x: auto + +dark-mode + background: #222 + color: #aaa .bottom-bar border-radius: 0 0 5px 5px background: #eee