diff --git a/main/create-window.js b/main/actions/create-window.js similarity index 51% rename from main/create-window.js rename to main/actions/create-window.js index 6d7f4b9..32a5a49 100644 --- a/main/create-window.js +++ b/main/actions/create-window.js @@ -1,24 +1,12 @@ -const { app, BrowserWindow } = require('electron') +const { BrowserWindow } = require('electron') const { resolve } = require('app-root-path') const dev = require('electron-is-dev') -const startServer = require('./server') -const setMenu = require('./menu') -const setIPCEvents = require('./ipc-events') - -async function createWindow () { - let server - - try { - // when starting the window run the server - server = await startServer() - } catch (error) { - console.error(error) - app.exit(error) - } +const setMenu = require('../menu') +async function createWindow (_windows) { // after the server starts create the electron browser window - global.win = new BrowserWindow({ + let win = new BrowserWindow({ title: 'Pulse', backgroundColor: '#058ecd', height: 768, @@ -32,25 +20,24 @@ async function createWindow () { textAreasAreResizable: false } }) + const id = win.id // open our server URL or the build directory in production - global.win.loadURL(dev ? 'http://localhost:8000' : `file://${resolve('./build')}/index.html`) + win.loadURL(dev ? 'http://localhost:8000' : `file://${resolve('./build')}/index.html`) // in development open devtools if (dev) { - global.win.webContents.openDevTools() + win.webContents.openDevTools() } - global.win.once('ready-to-show', () => { - global.win.show() + win.once('ready-to-show', () => { + win.show() }) - global.win.on('close', () => { - global.win = null - if (server) server.close() + win.on('closed', () => { + _windows.delete(id) }) - setIPCEvents() setMenu() // TODO: implement a way to get the Markdown data @@ -58,6 +45,8 @@ async function createWindow () { // const url = request.url.substr(8) // console.log(url) // }) + _windows.set(id, win) + return win } module.exports = createWindow diff --git a/main/assets/icon.icns b/main/assets/icon.icns index a9eabc2..0d24384 100644 Binary files a/main/assets/icon.icns and b/main/assets/icon.icns differ diff --git a/main/assets/icon.ico b/main/assets/icon.ico index bb1103d..f1f69f5 100644 Binary files a/main/assets/icon.ico and b/main/assets/icon.ico differ diff --git a/main/assets/icon.png b/main/assets/icon.png index fe53d87..d791f1b 100644 Binary files a/main/assets/icon.png and b/main/assets/icon.png differ diff --git a/main/index.js b/main/index.js index 9d86f7b..71fefce 100644 --- a/main/index.js +++ b/main/index.js @@ -1,17 +1,45 @@ +const WindowManager = require('./window-manager') const { app } = require('electron') -const createWindow = require('./create-window') +const dev = require('electron-is-dev') +const startServer = require('./server') +const setIPCEvents = require('./ipc-events') -app.on('ready', createWindow) +class Main { + constructor () { + this.server = null + this._windowManager = new WindowManager() + } + get windowManager () { + return this._windowManager + } -app.on('window-all-closed', () => { - if (process.platform !== 'darwin') { + async onReady () { + if (dev) { + try { + this.server = await startServer() + } catch (error) { + console.error(error) + app.exit(error) + } + } + this._windowManager.createNewWindow() + } + + onWindowAllClosed () { app.quit() } +} +const main = new Main() + +app.once('ready', () => { + main.onReady() + setIPCEvents() }) -app.on('activate', () => { - if (!global.win) { - createWindow() +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + main.onWindowAllClosed() } + if (this.server) this.server.close() }) diff --git a/main/menu.js b/main/menu.js index 19e8d2d..308079f 100644 --- a/main/menu.js +++ b/main/menu.js @@ -16,6 +16,16 @@ const template = [ } } }, + { + label: 'New window', + accelerator: 'CmdOrCtrl+Alt+N', + click () { + const webContent = webContents.getFocusedWebContents() + if (webContent) { + webContent.send('shortcut-press') + } + } + }, { label: 'Open...', accelerator: 'CmdOrCtrl+O', @@ -29,7 +39,7 @@ const template = [ { label: 'Save file', accelerator: 'CmdOrCtrl+S', - click() { + click () { const webContent = webContents.getFocusedWebContents() if (webContent) { webContent.send('trying-to-save') diff --git a/main/window-manager.js b/main/window-manager.js new file mode 100644 index 0000000..93e268e --- /dev/null +++ b/main/window-manager.js @@ -0,0 +1,27 @@ +const { ipcMain, BrowserWindow } = require('electron') +const createWindow = require('./actions/create-window') + +class WindowManager { + constructor () { + this._windows = new Map() + ipcMain.on('create-new-window', this._onRequestCreateNewWindow.bind(this)) + } + + reload () { + const w = BrowserWindow.getFocusedWindow() + if (w) { + w.reload() + } + } + + createNewWindow (value = '', fileName = undefined) { + createWindow(this._windows, value, fileName) + } + + _onRequestCreateNewWindow (event) { + this.createNewWindow() + event.sender.send('created-new-window') + } +} + +module.exports = WindowManager diff --git a/renderer/components/window-button.js b/renderer/components/window-button.js new file mode 100644 index 0000000..d547414 --- /dev/null +++ b/renderer/components/window-button.js @@ -0,0 +1,48 @@ +import { Component } from 'react' +import { Base } from 'pulse-editor/buttons' +import { ipcRenderer } from 'electron' +import { func } from 'prop-types' +import isMac from 'pulse-editor/built/utils/is-mac' +import Icon from 'react-icons/lib/fa/plus-circle' + +export default class CreateButton extends Component { + static contextTypes = { + setShortcut: func.isRequired, + removeShortcut: func.isRequired, + writeValue: func.isRequired, + setFileName: func.isRequired + } + + componentDidMount () { + ipcRenderer.on('shortcut-press', this.createWindow) + this.context.setShortcut({ + ctrlKey: !isMac(), + metaKey: isMac(), + altKey: true, + shiftKey: false, + keyName: 'n', + updater: selected => selected, + handler: event => { + this.createWindow() + return event.selection + } + }) + } + + componentWillUnmount () { + this.context.removeShortcut({ keyName: 'n' }) + ipcRenderer.removeListener('shortcut-press', this.createWindow) + } + + createWindow = () => ipcRenderer.send('create-new-window') + + handleClick = () => this.createWindow() + + render = () => ( + + + New Window + + + ) +} diff --git a/renderer/pages/index.js b/renderer/pages/index.js index 6a79976..745eaa9 100644 --- a/renderer/pages/index.js +++ b/renderer/pages/index.js @@ -21,6 +21,7 @@ import Save from '../components/save-button' import Open from '../components/open-button' import New from '../components/new-button' import Export from '../components/export-button' +import Create from '../components/window-button' import BoldIcon from 'react-icons/lib/fa/bold' import ItalicIcon from 'react-icons/lib/fa/italic' @@ -50,12 +51,11 @@ export default class extends Component { setFileName: this.setFileName } } - componentDidMount () { this.$preview = document.querySelector('.PulseEditor-preview') this.$preview.addEventListener('click', this.handlePreviewLinkClick) } - + componentWillUnmount () { this.$preview.removeEventListener('click', this.handlePreviewLinkClick) } @@ -70,7 +70,61 @@ export default class extends Component { ) } - handleDrop = event => event.preventDefault() + successMessage (fileName, successData) { + const resultMessage = { + target: { + value: `${this.editor.domField.value} ![${fileName}](${successData.data.link})` + } + } + this.editor.writeValue(resultMessage) + } + + async sendImage (file, imageData) { + const response = await fetch('https://api.imgur.com/3/image', + { + method: 'POST', + headers: { + 'Authorization': `Client-ID e3f6a51d5c12580` + }, + body: imageData} + ) + if (response.ok) { + const successData = await response.json() + return this.successMessage(file.name, successData) + } + this.errorMessage() + } + + errorMessage () { + const errorMessage = { + target: { + value: `${this.editor.domField.value} ![A problem when sending the file, please try again.]()` + } + } + this.editor.writeValue(errorMessage) + } + handleDrop = event => { + event.preventDefault() + // without 'preventDefault', when you drop the image, change the whole editor view + const defaultMessage = { + target: { + value: `${this.editor.domField.value} ![Problem with the format file](url)` + } + } + const file = event.dataTransfer.files[0] + const imageFormat = ['jpeg', 'png', 'gif', 'peg', 'apng', 'tiff', 'pdf', 'xcf'] + const validFile = imageFormat.filter((format) => { + const regExp = new RegExp("\\b(" + format + ")\\b") + return regExp.test(file.type) + }) + if (!!validFile && validFile.length !== 0) { + const imageData = new FormData() + imageData.append('image', file) + return this.sendImage(file, imageData, defaultMessage) + } + // if file format doesn't support by imgur, advice the user + return this.editor.writeValue(defaultMessage) + } handleChange= event => { if (event.markdown && this.state.fileName) { @@ -149,6 +203,7 @@ export default class extends Component { +