From e25f5d85403f4a7bd281460667eef155f4fe270c Mon Sep 17 00:00:00 2001 From: Ran Luo Date: Mon, 2 Sep 2019 21:46:51 +0800 Subject: [PATCH] Update: update dialog API to Electron 6.0 (#1317) --- src/main/app/index.js | 11 +- src/main/dataCenter/index.js | 17 ++- src/main/exceptionHandler.js | 6 +- src/main/menu/actions/file.js | 274 +++++++++++++++++----------------- src/main/windows/editor.js | 20 +-- 5 files changed, 165 insertions(+), 163 deletions(-) diff --git a/src/main/app/index.js b/src/main/app/index.js index b2df1b0e..26230baf 100644 --- a/src/main/app/index.js +++ b/src/main/app/index.js @@ -488,19 +488,18 @@ class App { // --- renderer ------------------- - ipcMain.on('mt::select-default-directory-to-open', e => { + ipcMain.on('mt::select-default-directory-to-open', async e => { const { preferences } = this._accessor const { defaultDirectoryToOpen } = preferences.getAll() const win = BrowserWindow.fromWebContents(e.sender) - dialog.showOpenDialog(win, { + const { filePaths } = await dialog.showOpenDialog(win, { defaultPath: defaultDirectoryToOpen, properties: ['openDirectory', 'createDirectory'] - }, paths => { - if (paths) { - preferences.setItems({ defaultDirectoryToOpen: paths[0] }) - } }) + if (filePaths) { + preferences.setItems({ defaultDirectoryToOpen: filePaths[0] }) + } }) ipcMain.on('mt::open-setting-window', () => { diff --git a/src/main/dataCenter/index.js b/src/main/dataCenter/index.js index 0a4e2f62..ee0343e5 100644 --- a/src/main/dataCenter/index.js +++ b/src/main/dataCenter/index.js @@ -168,13 +168,13 @@ class DataCenter extends EventEmitter { win.webContents.send('AGANI::user-preference', userData) }) - ipcMain.on('mt::ask-for-modify-image-folder-path', e => { + ipcMain.on('mt::ask-for-modify-image-folder-path', async e => { const win = BrowserWindow.fromWebContents(e.sender) - const folder = dialog.showOpenDialog(win, { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openDirectory', 'createDirectory'] }) - if (folder && folder[0]) { - this.setItem('imageFolderPath', folder[0]) + if (filePaths && filePaths[0]) { + this.setItem('imageFolderPath', filePaths[0]) } }) @@ -182,17 +182,18 @@ class DataCenter extends EventEmitter { this.setItems(userData) }) - ipcMain.on('mt::ask-for-image-path', e => { + ipcMain.on('mt::ask-for-image-path', async e => { const win = BrowserWindow.fromWebContents(e.sender) - const files = dialog.showOpenDialog(win, { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openFile'], filters: [{ name: 'Images', extensions: IMAGE_EXTENSIONS }] }) - if (files && files[0]) { - e.returnValue = files[0] + + if (filePaths && filePaths[0]) { + e.returnValue = filePaths[0] } else { e.returnValue = '' } diff --git a/src/main/exceptionHandler.js b/src/main/exceptionHandler.js index ddb81fe0..74bace97 100644 --- a/src/main/exceptionHandler.js +++ b/src/main/exceptionHandler.js @@ -32,7 +32,7 @@ const exceptionToString = (error, type) => { `Stack: ${stack}\n` } -const handleError = (title, error, type) => { +const handleError = async (title, error, type) => { const { message, stack } = error // Write error into file @@ -52,7 +52,7 @@ const handleError = (title, error, type) => { // show error dialog if (app.isReady()) { // Blocking message box - const result = dialog.showMessageBox({ + const { response } = await dialog.showMessageBox({ type: 'error', buttons: [ 'OK', @@ -65,7 +65,7 @@ const handleError = (title, error, type) => { detail: stack }) - switch (result) { + switch (response) { case 1: { clipboard.writeText(`${title}\n${stack}`) break diff --git a/src/main/menu/actions/file.js b/src/main/menu/actions/file.js index 1ca3c9a4..208c4a83 100644 --- a/src/main/menu/actions/file.js +++ b/src/main/menu/actions/file.js @@ -1,6 +1,5 @@ import fs from 'fs' import path from 'path' -import { promisify } from 'util' import { BrowserWindow, dialog, ipcMain, shell } from 'electron' import log from 'electron-log' import { isDirectory, isFile } from 'common/filesystem' @@ -26,36 +25,36 @@ const handleResponseForExport = async (e, { type, content, pathname, markdown }) } const defaultPath = path.join(dirname, `${nakedFilename}${extension}`) - dialog.showSaveDialog(win, { + const { filePath, canceled } = await dialog.showSaveDialog(win, { defaultPath - }, async filePath => { - if (filePath) { - let data = content - try { - if (!content && type === 'pdf') { - data = await promisify(win.webContents.printToPDF.bind(win.webContents))({ printBackground: true }) - removePrintServiceFromWindow(win) - } - if (data) { - await writeFile(filePath, data, extension) - win.webContents.send('AGANI::export-success', { type, filePath }) - } - } catch (err) { - log.error(err) - const ERROR_MSG = err.message || `Error happened when export ${filePath}` - win.webContents.send('AGANI::show-notification', { - title: 'Export failure', - type: 'error', - message: ERROR_MSG - }) - } - } else { - // User canceled save dialog - if (type === 'pdf') { + }) + + if (filePath && !canceled) { + let data = content + try { + if (!content && type === 'pdf') { + data = await win.webContents.printToPDF({ printBackground: true }) removePrintServiceFromWindow(win) } + if (data) { + await writeFile(filePath, data, extension) + win.webContents.send('AGANI::export-success', { type, filePath }) + } + } catch (err) { + log.error(err) + const ERROR_MSG = err.message || `Error happened when export ${filePath}` + win.webContents.send('AGANI::show-notification', { + title: 'Export failure', + type: 'error', + message: ERROR_MSG + }) } - }) + } else { + // User canceled save dialog + if (type === 'pdf') { + removePrintServiceFromWindow(win) + } + } } const handleResponseForPrint = e => { @@ -69,7 +68,7 @@ const handleResponseForPrint = e => { noLink: true, message: 'Printing doesn\'t work', detail: 'Printing is disabled due to an Electron upstream issue. Please export the document as PDF and print the PDF file. We apologize for the inconvenience!' - }, () => {}) + }) // win.webContents.print({ printBackground: true }, () => { // removePrintServiceFromWindow(win) // }) @@ -88,13 +87,15 @@ const handleResponseForSave = async (e, { id, markdown, pathname, options }) => const alreadyExistOnDisk = !!pathname let filePath = pathname + if (!filePath) { - filePath = await new Promise((resolve, reject) => { - // TODO: Use asynchronous version that returns a "Promise" with Electron 6. - dialog.showSaveDialog(win, { - defaultPath: path.join(getPath('documents'), `${recommendFilename}.md`) - }, resolve) + const { filePath: dialogPath, canceled } = await dialog.showSaveDialog(win, { + defaultPath: path.join(getPath('documents'), `${recommendFilename}.md`) }) + + if (dialogPath && !canceled) { + filePath = dialogPath + } } // Save dialog canceled by user - no error. @@ -123,29 +124,27 @@ const handleResponseForSave = async (e, { id, markdown, pathname, options }) => }) } -const showUnsavedFilesMessage = (win, files) => { - return new Promise((resolve, reject) => { - dialog.showMessageBox(win, { - type: 'warning', - buttons: ['Save', 'Cancel', 'Don\'t save'], - defaultId: 0, - message: `Do you want to save the changes you made to ${files.length} ${files.length === 1 ? 'file' : 'files'}?\n\n${files.map(f => f.filename).join('\n')}`, - detail: 'Your changes will be lost if you don\'t save them.', - cancelId: 1, - noLink: true - }, index => { - switch (index) { - case 2: - resolve({ needSave: false }) - break - case 0: - setTimeout(() => { - resolve({ needSave: true }) - }) - break - } - }) +const showUnsavedFilesMessage = async (win, files) => { + const { response } = await dialog.showMessageBox(win, { + type: 'warning', + buttons: ['Save', 'Cancel', 'Don\'t save'], + defaultId: 0, + message: `Do you want to save the changes you made to ${files.length} ${files.length === 1 ? 'file' : 'files'}?\n\n${files.map(f => f.filename).join('\n')}`, + detail: 'Your changes will be lost if you don\'t save them.', + cancelId: 1, + noLink: true }) + + switch (response) { + case 2: + return { needSave: false } + case 0: + return new Promise((resolve, reject) => { + setTimeout(() => { + resolve({ needSave: true }) + }) + }) + } } const noticePandocNotFound = win => { @@ -197,7 +196,7 @@ ipcMain.on('mt::save-and-close-tabs', async (e, unsavedFiles) => { } }) -ipcMain.on('AGANI::response-file-save-as', (e, { id, markdown, pathname, options }) => { +ipcMain.on('AGANI::response-file-save-as', async (e, { id, markdown, pathname, options }) => { const win = BrowserWindow.fromWebContents(e.sender) let recommendFilename = getRecommendTitleFromMarkdownString(markdown) if (!recommendFilename) { @@ -209,36 +208,36 @@ ipcMain.on('AGANI::response-file-save-as', (e, { id, markdown, pathname, options // on disk nevertheless but is already tracked by Mark Text. const alreadyExistOnDisk = !!pathname - dialog.showSaveDialog(win, { + let { filePath, canceled } = await dialog.showSaveDialog(win, { defaultPath: pathname || getPath('documents') + `/${recommendFilename}.md` - }, filePath => { - if (filePath) { - filePath = path.resolve(filePath) - writeMarkdownFile(filePath, markdown, options, win) - .then(() => { - if (!alreadyExistOnDisk) { - ipcMain.emit('window-add-file-path', win.id, filePath) - ipcMain.emit('menu-add-recently-used', filePath) - - const filename = path.basename(filePath) - win.webContents.send('mt::set-pathname', { id, pathname: filePath, filename }) - } else if (pathname !== filePath) { - // Update window file list and watcher. - ipcMain.emit('window-change-file-path', win.id, filePath, pathname) - - const filename = path.basename(filePath) - win.webContents.send('mt::set-pathname', { id, pathname: filePath, filename }) - } else { - ipcMain.emit('window-file-saved', win.id, filePath) - win.webContents.send('mt::tab-saved', id) - } - }) - .catch(err => { - log.error(err) - win.webContents.send('mt::tab-save-failure', id, err.message) - }) - } }) + + if (filePath && !canceled) { + filePath = path.resolve(filePath) + writeMarkdownFile(filePath, markdown, options, win) + .then(() => { + if (!alreadyExistOnDisk) { + ipcMain.emit('window-add-file-path', win.id, filePath) + ipcMain.emit('menu-add-recently-used', filePath) + + const filename = path.basename(filePath) + win.webContents.send('mt::set-pathname', { id, pathname: filePath, filename }) + } else if (pathname !== filePath) { + // Update window file list and watcher. + ipcMain.emit('window-change-file-path', win.id, filePath, pathname) + + const filename = path.basename(filePath) + win.webContents.send('mt::set-pathname', { id, pathname: filePath, filename }) + } else { + ipcMain.emit('window-file-saved', win.id, filePath) + win.webContents.send('mt::tab-saved', id) + } + }) + .catch(err => { + log.error(err) + win.webContents.send('mt::tab-save-failure', id, err.message) + }) + } }) ipcMain.on('mt::close-window-confirm', async (e, unsavedFiles) => { @@ -259,11 +258,12 @@ ipcMain.on('mt::close-window-confirm', async (e, unsavedFiles) => { buttons: ['Close', 'Keep It Open'], message: 'Failure while saving files', detail: err.message - }, code => { - if (win.id && code === 0) { - ipcMain.emit('window-close-by-id', win.id) - } }) + .then(({ response }) => { + if (win.id && response === 0) { + ipcMain.emit('window-close-by-id', win.id) + } + }) }) } else { ipcMain.emit('window-close-by-id', win.id) @@ -297,7 +297,7 @@ ipcMain.on('AGANI::window::drop', async (e, fileList) => { } }) -ipcMain.on('mt::rename', (e, { id, pathname, newPathname }) => { +ipcMain.on('mt::rename', async (e, { id, pathname, newPathname }) => { if (pathname === newPathname) return const win = BrowserWindow.fromWebContents(e.sender) @@ -320,51 +320,51 @@ ipcMain.on('mt::rename', (e, { id, pathname, newPathname }) => { if (!isFile(newPathname)) { doRename() } else { - dialog.showMessageBox(win, { + const { response } = await dialog.showMessageBox(win, { type: 'warning', buttons: ['Replace', 'Cancel'], defaultId: 1, message: `The file "${path.basename(newPathname)}" already exists. Do you want to replace it?`, cancelId: 1, noLink: true - }, index => { - if (index === 0) { - doRename() + }) + + if (response === 0) { + doRename() + } + } +}) + +ipcMain.on('AGANI::response-file-move-to', async (e, { id, pathname }) => { + const win = BrowserWindow.fromWebContents(e.sender) + const { filePath, canceled } = await dialog.showSaveDialog(win, { + buttonLabel: 'Move to', + nameFieldLabel: 'Filename:', + defaultPath: pathname + }) + + if (filePath && !canceled) { + fs.rename(pathname, filePath, err => { + if (err) { + log.error(`mt::rename: Cannot rename "${pathname}" to "${filePath}".\n${err.stack}`) + return } + + ipcMain.emit('window-change-file-path', win.id, filePath, pathname) + e.sender.send('mt::set-pathname', { id, pathname: filePath, filename: path.basename(filePath) }) }) } }) -ipcMain.on('AGANI::response-file-move-to', (e, { id, pathname }) => { +ipcMain.on('mt::ask-for-open-project-in-sidebar', async e => { const win = BrowserWindow.fromWebContents(e.sender) - dialog.showSaveDialog(win, { - buttonLabel: 'Move to', - nameFieldLabel: 'Filename:', - defaultPath: pathname - }, newPath => { - if (newPath) { - fs.rename(pathname, newPath, err => { - if (err) { - log.error(`mt::rename: Cannot rename "${pathname}" to "${newPath}".\n${err.stack}`) - return - } - - ipcMain.emit('window-change-file-path', win.id, newPath, pathname) - e.sender.send('mt::set-pathname', { id, pathname: newPath, filename: path.basename(newPath) }) - }) - } - }) -}) - -ipcMain.on('mt::ask-for-open-project-in-sidebar', e => { - const win = BrowserWindow.fromWebContents(e.sender) - dialog.showOpenDialog(win, { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openDirectory', 'createDirectory'] - }, directories => { - if (directories && directories[0]) { - ipcMain.emit('app-open-directory-by-id', win.id, directories[0], true) - } }) + + if (filePaths && filePaths[0]) { + ipcMain.emit('app-open-directory-by-id', win.id, filePaths[0], true) + } }) ipcMain.on('AGANI::format-link-click', (e, { data, dirname }) => { @@ -397,45 +397,45 @@ export const importFile = async win => { return noticePandocNotFound(win) } - dialog.showOpenDialog(win, { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openFile'], filters: [{ name: 'All Files', extensions: PANDOC_EXTENSIONS }] - }, filePath => { - if (filePath) { - openPandocFile(win.id, filePath) - } }) + + if (filePaths && filePaths[0]) { + openPandocFile(win.id, filePaths[0]) + } } export const print = win => { win.webContents.send('AGANI::print') } -export const openFile = win => { - dialog.showOpenDialog(win, { +export const openFile = async win => { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openFile', 'multiSelections'], filters: [{ name: 'text', extensions: MARKDOWN_EXTENSIONS }] - }, paths => { - if (paths && Array.isArray(paths)) { - ipcMain.emit('app-open-files-by-id', win.id, paths) - } }) + + if (filePaths && Array.isArray(filePaths)) { + ipcMain.emit('app-open-files-by-id', win.id, filePaths) + } } -export const openFolder = win => { - dialog.showOpenDialog(win, { +export const openFolder = async win => { + const { filePaths } = await dialog.showOpenDialog(win, { properties: ['openDirectory', 'createDirectory'] - }, directories => { - if (directories && directories[0]) { - openFileOrFolder(win, directories[0]) - } }) + + if (filePaths && filePaths[0]) { + openFileOrFolder(win, filePaths[0]) + } } export const openFileOrFolder = (win, pathname) => { diff --git a/src/main/windows/editor.js b/src/main/windows/editor.js index b15d995f..c2174396 100644 --- a/src/main/windows/editor.js +++ b/src/main/windows/editor.js @@ -100,23 +100,25 @@ class EditorWindow extends BaseWindow { log.error(`The window failed to load or was cancelled: ${errorCode}; ${errorDescription}`) }) - win.webContents.once('crashed', (event, killed) => { + win.webContents.once('crashed', async (event, killed) => { const msg = `The renderer process has crashed unexpected or is killed (${killed}).` log.error(msg) - dialog.showMessageBox(win, { + const { response } = await dialog.showMessageBox(win, { type: 'warning', buttons: ['Close', 'Reload', 'Keep It Open'], message: 'Mark Text has crashed', detail: msg - }, code => { - if (win.id) { - switch (code) { - case 0: return this.destroy() - case 1: return this.reload() - } - } }) + + if (win.id) { + switch (response) { + case 0: + return this.destroy() + case 1: + return this.reload() + } + } }) win.on('focus', () => {