diff --git a/.gitignore b/.gitignore index 2812b61f45..3a35d97e61 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,7 @@ Thumbs.db compiled/* dist/* .cache +app/package.json +app/package-lock.json +app/node_modules/* +secrets \ No newline at end of file diff --git a/app/index.js b/app/index.js new file mode 100644 index 0000000000..212a9eca98 --- /dev/null +++ b/app/index.js @@ -0,0 +1,42 @@ +const electron = require('electron') +const path = require('path') +require('./menu') + +const { app, BrowserWindow } = electron +const isDevelopment = process.env.NODE_ENV !== 'production' + +// global reference to mainWindow (necessary to prevent window from being garbage collected) +let mainWindow + +function createMainWindow() { + const window = new BrowserWindow({ + webPreferences: { nodeIntegration: true } + }) + + window.loadFile(path.join(__dirname, '../compiled/index.html')) + + window.on('closed', () => { + mainWindow = null + }) + return window +} + +// quit application when all windows are closed +app.on('window-all-closed', () => { + // on macOS it is common for applications to stay open until the user explicitly quits + if (process.platform !== 'darwin') { + app.quit() + } +}) + +app.on('activate', () => { + // on macOS it is common to re-create a window even after all windows have been closed + if (mainWindow === null) { + mainWindow = createMainWindow() + } +}) + +// create main BrowserWindow when electron is ready +app.on('ready', () => { + mainWindow = createMainWindow() +}) diff --git a/app/menu.js b/app/menu.js new file mode 100644 index 0000000000..2c39f8a349 --- /dev/null +++ b/app/menu.js @@ -0,0 +1,110 @@ +const { app, Menu } = require('electron') +const { checkForUpdates } = require('./updater') + +const isMac = process.platform === 'darwin' +const isLinux = process.platform === 'linux' + +const template = [ + // { role: 'appMenu' } + ...(isMac + ? [ + { + label: app.name, + submenu: [ + { role: 'about' }, + { type: 'separator' }, + { role: 'services' }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideothers' }, + { role: 'unhide' }, + { type: 'separator' }, + { role: 'quit' } + ] + } + ] + : []), + // { role: 'fileMenu' } + { + label: 'File', + submenu: [isMac ? { role: 'close' } : { role: 'quit' }] + }, + // { role: 'editMenu' } + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'separator' }, + { role: 'cut' }, + { role: 'copy' }, + { role: 'paste' }, + ...(isMac + ? [ + { role: 'pasteAndMatchStyle' }, + { role: 'delete' }, + { role: 'selectAll' }, + { type: 'separator' }, + { + label: 'Speech', + submenu: [{ role: 'startspeaking' }, { role: 'stopspeaking' }] + } + ] + : [{ role: 'delete' }, { type: 'separator' }, { role: 'selectAll' }]) + ] + }, + // { role: 'viewMenu' } + { + label: 'View', + submenu: [ + { role: 'reload' }, + { role: 'forcereload' }, + { role: 'toggledevtools' }, + { type: 'separator' }, + { role: 'resetzoom' }, + { role: 'zoomin' }, + { role: 'zoomout' }, + { type: 'separator' }, + { role: 'togglefullscreen' } + ] + }, + // { role: 'windowMenu' } + { + label: 'Window', + submenu: [ + { role: 'minimize' }, + { role: 'zoom' }, + ...(isMac + ? [ + { type: 'separator' }, + { role: 'front' }, + { type: 'separator' }, + { role: 'window' } + ] + : [{ role: 'close' }]) + ] + }, + { + role: 'help', + submenu: [ + ...(isLinux + ? [] + : [ + { + label: 'Check For Updates', + click: checkForUpdates + } + ]), + { + label: 'Learn More', + click: async () => { + const { shell } = require('electron') + await shell.openExternal('https://boostnote.io') + } + } + ] + } +] + +const menu = Menu.buildFromTemplate(template) +Menu.setApplicationMenu(menu) diff --git a/app/updater.js b/app/updater.js new file mode 100644 index 0000000000..bdb35a7762 --- /dev/null +++ b/app/updater.js @@ -0,0 +1,110 @@ +const { dialog, Notification } = require('electron') +const { autoUpdater } = require('electron-updater') +const logger = require('electron-log') + +autoUpdater.logger = logger +autoUpdater.logger.transports.file.level = 'info' + +let updater +autoUpdater.autoDownload = false + +let updating = false +let foundUpdates = false + +autoUpdater.on('error', error => { + dialog.showErrorBox( + 'Error: ', + error == null ? 'unknown' : (error.stack || error).toString() + ) +}) + +autoUpdater.on('update-available', () => { + foundUpdates = true + if (updater == null) { + const notification = new Notification({ + title: 'Found Updates!', + body: 'Click here to update' + }) + notification.addListener('click', () => { + dialog + .showMessageBox({ + type: 'info', + title: 'Found Updates', + message: 'Found updates, do you want update now?', + buttons: ['Sure', 'No'] + }) + .then(({ response }) => { + if (response === 0) { + autoUpdater.downloadUpdate() + } + }) + }) + notification.show() + } else { + dialog + .showMessageBox({ + type: 'info', + title: 'Found Updates', + message: 'Found updates, do you want update now?', + buttons: ['Sure', 'No'] + }) + .then(({ response }) => { + if (response === 0) { + autoUpdater.downloadUpdate() + } else { + updater.enabled = true + updater = null + } + }) + } +}) + +autoUpdater.on('update-not-available', () => { + if (updater != null) { + dialog.showMessageBox({ + title: 'No Updates', + message: 'Current version is up-to-date.' + }) + updater.enabled = true + updater = null + } +}) + +autoUpdater.on('update-downloaded', () => { + dialog + .showMessageBox({ + title: 'Updates downloaded', + message: 'To install the update, the app must be restarted.', + buttons: ['Restart and Install', 'Not Yet'] + }) + .then(({ response }) => { + if (response === 0) { + setImmediate(() => autoUpdater.quitAndInstall()) + } else { + if (updater != null) { + updater.enabled = true + updater = null + } + } + }) +}) + +function checkForUpdates(menuItem, focusedWindow, event) { + updater = menuItem + updater.enabled = false + autoUpdater.checkForUpdates() +} + +setTimeout(() => { + if (updater == null) { + autoUpdater.checkForUpdates() + } +}, 10 * 1000) // After 10 secs + +setInterval(() => { + if (!foundUpdates && updater == null) { + autoUpdater.checkForUpdates() + } +}, 24 * 3600 * 1000) // Everyday + +module.exports.checkForUpdates = checkForUpdates diff --git a/index.html b/index.html index fb6fc55980..8dfefd7863 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,12 @@ -
- - -