diff options
-rw-r--r-- | .vscode/launch.json | 2 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/components/layout/AppLayout.js | 4 | ||||
-rw-r--r-- | src/environment.js | 1 | ||||
-rw-r--r-- | src/index.js | 4 | ||||
-rw-r--r-- | src/lib/Menu.js | 318 |
6 files changed, 263 insertions, 68 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json index f6c20b520..98408278c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json | |||
@@ -10,7 +10,7 @@ | |||
10 | "protocol": "inspector", | 10 | "protocol": "inspector", |
11 | "env": { | 11 | "env": { |
12 | "NODE_ENV": "development", | 12 | "NODE_ENV": "development", |
13 | "OS_PLATFORM": "win32" | 13 | // "OS_PLATFORM": "win32" |
14 | } | 14 | } |
15 | }, | 15 | }, |
16 | { | 16 | { |
diff --git a/package.json b/package.json index 7922eff03..78f7a3291 100644 --- a/package.json +++ b/package.json | |||
@@ -13,7 +13,7 @@ | |||
13 | "start": "electron ./build", | 13 | "start": "electron ./build", |
14 | "start:local": "cross-env LOCAL_API=1 yarn start", | 14 | "start:local": "cross-env LOCAL_API=1 yarn start", |
15 | "start:live": "cross-env LIVE_API=1 yarn start", | 15 | "start:live": "cross-env LIVE_API=1 yarn start", |
16 | "dev": "cross-env NODE_ENV=development OS_PLATFORM=win32 gulp dev", | 16 | "dev": "cross-env NODE_ENV=development gulp dev", |
17 | "lint": "eslint src", | 17 | "lint": "eslint src", |
18 | "sign": "gulp sign", | 18 | "sign": "gulp sign", |
19 | "prebuild": "gulp build", | 19 | "prebuild": "gulp build", |
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 6162840be..686476317 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -7,6 +7,8 @@ import { TitleBar } from 'electron-react-titlebar'; | |||
7 | import InfoBar from '../ui/InfoBar'; | 7 | import InfoBar from '../ui/InfoBar'; |
8 | import globalMessages from '../../i18n/globalMessages'; | 8 | import globalMessages from '../../i18n/globalMessages'; |
9 | 9 | ||
10 | import { isMac } from '../../environment'; | ||
11 | |||
10 | function createMarkup(HTMLString) { | 12 | function createMarkup(HTMLString) { |
11 | return { __html: HTMLString }; | 13 | return { __html: HTMLString }; |
12 | } | 14 | } |
@@ -88,7 +90,7 @@ export default class AppLayout extends Component { | |||
88 | return ( | 90 | return ( |
89 | <div> | 91 | <div> |
90 | <div className="app"> | 92 | <div className="app"> |
91 | <TitleBar menu={window.franz.menu.template} icon={'assets/images/logo.svg'} /> | 93 | {!isMac && <TitleBar menu={window.franz.menu.template} icon={'assets/images/logo.svg'} />} |
92 | <div className="app__content"> | 94 | <div className="app__content"> |
93 | {sidebar} | 95 | {sidebar} |
94 | <div className="app__service"> | 96 | <div className="app__service"> |
diff --git a/src/environment.js b/src/environment.js index 4dca0807d..e1762129b 100644 --- a/src/environment.js +++ b/src/environment.js | |||
@@ -14,6 +14,7 @@ export const isWindows = platform === 'win32'; | |||
14 | export const isLinux = platform === 'linux'; | 14 | export const isLinux = platform === 'linux'; |
15 | 15 | ||
16 | export const ctrlKey = isMac ? '⌘' : 'Ctrl'; | 16 | export const ctrlKey = isMac ? '⌘' : 'Ctrl'; |
17 | export const cmdKey = isMac ? 'Cmd' : 'Ctrl'; | ||
17 | 18 | ||
18 | let api; | 19 | let api; |
19 | if (!isDevMode || (isDevMode && useLiveAPI)) { | 20 | if (!isDevMode || (isDevMode && useLiveAPI)) { |
diff --git a/src/index.js b/src/index.js index e8818af96..2acb8e2cb 100644 --- a/src/index.js +++ b/src/index.js | |||
@@ -4,7 +4,7 @@ import path from 'path'; | |||
4 | 4 | ||
5 | import windowStateKeeper from 'electron-window-state'; | 5 | import windowStateKeeper from 'electron-window-state'; |
6 | 6 | ||
7 | import { isDevMode, isWindows } from './environment'; | 7 | import { isDevMode, isMac, isWindows } from './environment'; |
8 | import ipcApi from './electron/ipc-api'; | 8 | import ipcApi from './electron/ipc-api'; |
9 | import Tray from './lib/Tray'; | 9 | import Tray from './lib/Tray'; |
10 | import Settings from './electron/Settings'; | 10 | import Settings from './electron/Settings'; |
@@ -72,7 +72,7 @@ const createWindow = () => { | |||
72 | height: mainWindowState.height, | 72 | height: mainWindowState.height, |
73 | minWidth: 600, | 73 | minWidth: 600, |
74 | minHeight: 500, | 74 | minHeight: 500, |
75 | titleBarStyle: process.env.OS_PLATFORM || process.platform === 'win32' ? '' : 'hidden', | 75 | titleBarStyle: isMac ? 'hidden' : '', |
76 | frame: false, | 76 | frame: false, |
77 | backgroundColor: '#3498db', | 77 | backgroundColor: '#3498db', |
78 | autoHideMenuBar: true, | 78 | autoHideMenuBar: true, |
diff --git a/src/lib/Menu.js b/src/lib/Menu.js index 2bdd36b2c..475352bba 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js | |||
@@ -1,10 +1,14 @@ | |||
1 | import { remote, shell } from 'electron'; | 1 | import { remote, shell } from 'electron'; |
2 | import { autorun, computed, observable, toJS } from 'mobx'; | 2 | import { autorun, computed, observable, toJS } from 'mobx'; |
3 | 3 | ||
4 | import { isMac } from '../environment'; | 4 | import { isMac, ctrlKey, cmdKey } from '../environment'; |
5 | 5 | ||
6 | const { app, Menu, dialog } = remote; | 6 | const { app, Menu, dialog } = remote; |
7 | 7 | ||
8 | function getActiveWebview() { | ||
9 | return window.franz.stores.services.active.webview; | ||
10 | } | ||
11 | |||
8 | const template = [ | 12 | const template = [ |
9 | { | 13 | { |
10 | label: 'Edit', | 14 | label: 'Edit', |
@@ -114,8 +118,161 @@ const template = [ | |||
114 | }, | 118 | }, |
115 | ]; | 119 | ]; |
116 | 120 | ||
121 | const titleBarTemplate = [ | ||
122 | { | ||
123 | label: 'Edit', | ||
124 | submenu: [ | ||
125 | { | ||
126 | label: 'Undo', | ||
127 | accelerator: `${ctrlKey}+Z`, | ||
128 | click() { | ||
129 | getActiveWebview().undo(); | ||
130 | }, | ||
131 | }, | ||
132 | { | ||
133 | label: 'Redo', | ||
134 | accelerator: `${ctrlKey}+Y`, | ||
135 | click() { | ||
136 | getActiveWebview().redo(); | ||
137 | }, | ||
138 | }, | ||
139 | { | ||
140 | type: 'separator', | ||
141 | }, | ||
142 | { | ||
143 | label: 'Cut', | ||
144 | accelerator: `${ctrlKey}+X`, | ||
145 | click() { | ||
146 | getActiveWebview().cut(); | ||
147 | }, | ||
148 | }, | ||
149 | { | ||
150 | label: 'Copy', | ||
151 | accelerator: `${ctrlKey}+C`, | ||
152 | click() { | ||
153 | getActiveWebview().copy(); | ||
154 | }, | ||
155 | }, | ||
156 | { | ||
157 | label: 'Paste', | ||
158 | accelerator: `${ctrlKey}+V`, | ||
159 | click() { | ||
160 | getActiveWebview().paste(); | ||
161 | }, | ||
162 | }, | ||
163 | { | ||
164 | label: 'Paste and Match Style', | ||
165 | accelerator: `${ctrlKey}+Shift+V`, | ||
166 | click() { | ||
167 | getActiveWebview().pasteAndMatchStyle(); | ||
168 | }, | ||
169 | }, | ||
170 | { | ||
171 | label: 'Delete', | ||
172 | click() { | ||
173 | getActiveWebview().delete(); | ||
174 | }, | ||
175 | }, | ||
176 | { | ||
177 | label: 'Select All', | ||
178 | accelerator: `${ctrlKey}+A`, | ||
179 | click() { | ||
180 | getActiveWebview().selectAll(); | ||
181 | }, | ||
182 | }, | ||
183 | ], | ||
184 | }, | ||
185 | { | ||
186 | label: 'View', | ||
187 | submenu: [ | ||
188 | { | ||
189 | type: 'separator', | ||
190 | }, | ||
191 | { | ||
192 | label: 'Reset Zoom', | ||
193 | accelerator: `${ctrlKey}+0`, | ||
194 | click() { | ||
195 | getActiveWebview().setZoomLevel(0); | ||
196 | }, | ||
197 | }, | ||
198 | { | ||
199 | label: 'Zoom in', | ||
200 | accelerator: `${ctrlKey}+=`, | ||
201 | click() { | ||
202 | getActiveWebview().getZoomLevel((zoomLevel) => { | ||
203 | getActiveWebview().setZoomLevel(zoomLevel === 5 ? zoomLevel : zoomLevel + 1); | ||
204 | }); | ||
205 | }, | ||
206 | }, | ||
207 | { | ||
208 | label: 'Zoom out', | ||
209 | accelerator: `${ctrlKey}+-`, | ||
210 | click() { | ||
211 | getActiveWebview().getZoomLevel((zoomLevel) => { | ||
212 | getActiveWebview().setZoomLevel(zoomLevel === -5 ? zoomLevel : zoomLevel - 1); | ||
213 | }); | ||
214 | }, | ||
215 | }, | ||
216 | ], | ||
217 | }, | ||
218 | { | ||
219 | label: 'Services', | ||
220 | submenu: [], | ||
221 | }, | ||
222 | { | ||
223 | label: 'Window', | ||
224 | submenu: [ | ||
225 | { | ||
226 | label: 'Minimize', | ||
227 | accelerator: 'Alt+M', | ||
228 | click(menuItem, browserWindow) { | ||
229 | browserWindow.minimize(); | ||
230 | }, | ||
231 | }, | ||
232 | { | ||
233 | label: 'Close', | ||
234 | accelerator: 'Alt+W', | ||
235 | click(menuItem, browserWindow) { | ||
236 | browserWindow.close(); | ||
237 | }, | ||
238 | }, | ||
239 | ], | ||
240 | }, | ||
241 | { | ||
242 | label: '?', | ||
243 | submenu: [ | ||
244 | { | ||
245 | label: 'Learn More', | ||
246 | click() { shell.openExternal('http://meetfranz.com'); }, | ||
247 | }, | ||
248 | { | ||
249 | label: 'Changelog', | ||
250 | click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); }, | ||
251 | }, | ||
252 | { | ||
253 | type: 'separator', | ||
254 | }, | ||
255 | { | ||
256 | label: 'Support', | ||
257 | click() { shell.openExternal('http://meetfranz.com/support'); }, | ||
258 | }, | ||
259 | { | ||
260 | type: 'separator', | ||
261 | }, | ||
262 | { | ||
263 | label: 'Terms of Service', | ||
264 | click() { shell.openExternal('https://meetfranz.com/terms'); }, | ||
265 | }, | ||
266 | { | ||
267 | label: 'Privacy Statement', | ||
268 | click() { shell.openExternal('https://meetfranz.com/privacy'); }, | ||
269 | }, | ||
270 | ], | ||
271 | }, | ||
272 | ]; | ||
273 | |||
117 | export default class FranzMenu { | 274 | export default class FranzMenu { |
118 | @observable tpl = template; | 275 | @observable tpl = isMac ? template : titleBarTemplate; |
119 | @observable currentTemplate = null; | 276 | @observable currentTemplate = null; |
120 | 277 | ||
121 | constructor(stores, actions) { | 278 | constructor(stores, actions) { |
@@ -133,10 +290,16 @@ export default class FranzMenu { | |||
133 | const tpl = toJS(this.tpl); | 290 | const tpl = toJS(this.tpl); |
134 | 291 | ||
135 | tpl[1].submenu.push({ | 292 | tpl[1].submenu.push({ |
136 | role: 'toggledevtools', | 293 | type: 'separator', |
294 | }, { | ||
295 | label: 'Toggle Developer Tools', | ||
296 | accelerator: `${cmdKey}+Alt+I`, | ||
297 | click: (menuItem, browserWindow) => { | ||
298 | browserWindow.webContents.toggleDevTools(); | ||
299 | }, | ||
137 | }, { | 300 | }, { |
138 | label: 'Toggle Service Developer Tools', | 301 | label: 'Open Service Developer Tools', |
139 | accelerator: 'CmdOrCtrl+Shift+Alt+i', | 302 | accelerator: `${cmdKey}+Shift+Alt+I`, |
140 | click: () => { | 303 | click: () => { |
141 | this.actions.service.openDevToolsForActiveService(); | 304 | this.actions.service.openDevToolsForActiveService(); |
142 | }, | 305 | }, |
@@ -145,7 +308,7 @@ export default class FranzMenu { | |||
145 | tpl[1].submenu.unshift({ | 308 | tpl[1].submenu.unshift({ |
146 | label: 'Reload Service', | 309 | label: 'Reload Service', |
147 | id: 'reloadService', | 310 | id: 'reloadService', |
148 | accelerator: 'CmdOrCtrl+R', | 311 | accelerator: `${cmdKey}+R`, |
149 | click: () => { | 312 | click: () => { |
150 | if (this.stores.user.isLoggedIn | 313 | if (this.stores.user.isLoggedIn |
151 | && this.stores.services.enabled.length > 0) { | 314 | && this.stores.services.enabled.length > 0) { |
@@ -156,56 +319,69 @@ export default class FranzMenu { | |||
156 | }, | 319 | }, |
157 | }, { | 320 | }, { |
158 | label: 'Reload Franz', | 321 | label: 'Reload Franz', |
159 | accelerator: 'CmdOrCtrl+Shift+R', | 322 | accelerator: `${cmdKey}+Shift+R`, |
160 | click: () => { | 323 | click: () => { |
161 | window.location.reload(); | 324 | window.location.reload(); |
162 | }, | 325 | }, |
163 | }); | 326 | }); |
164 | 327 | ||
165 | if (isMac) { | 328 | tpl.unshift({ |
166 | tpl.unshift({ | 329 | label: isMac ? app.getName() : 'File', |
167 | label: app.getName(), | 330 | submenu: [ |
168 | submenu: [ | 331 | { |
169 | { | 332 | role: 'about', |
170 | role: 'about', | 333 | }, |
171 | }, | 334 | { |
172 | { | 335 | type: 'separator', |
173 | type: 'separator', | 336 | }, |
174 | }, | 337 | { |
175 | { | 338 | label: 'Settings', |
176 | label: 'Settings', | 339 | accelerator: 'CmdOrCtrl+,', |
177 | accelerator: 'CmdOrCtrl+,', | 340 | click: () => { |
178 | click: () => { | 341 | this.actions.ui.openSettings({ path: 'app' }); |
179 | this.actions.ui.openSettings({ path: 'app' }); | ||
180 | }, | ||
181 | }, | ||
182 | { | ||
183 | type: 'separator', | ||
184 | }, | ||
185 | { | ||
186 | role: 'services', | ||
187 | submenu: [], | ||
188 | }, | ||
189 | { | ||
190 | type: 'separator', | ||
191 | }, | ||
192 | { | ||
193 | role: 'hide', | ||
194 | }, | ||
195 | { | ||
196 | role: 'hideothers', | ||
197 | }, | ||
198 | { | ||
199 | role: 'unhide', | ||
200 | }, | ||
201 | { | ||
202 | type: 'separator', | ||
203 | }, | ||
204 | { | ||
205 | role: 'quit', | ||
206 | }, | 342 | }, |
207 | ], | 343 | }, |
208 | }); | 344 | { |
345 | type: 'separator', | ||
346 | }, | ||
347 | { | ||
348 | role: 'services', | ||
349 | submenu: [], | ||
350 | }, | ||
351 | { | ||
352 | type: 'separator', | ||
353 | }, | ||
354 | { | ||
355 | role: 'hide', | ||
356 | }, | ||
357 | { | ||
358 | role: 'hideothers', | ||
359 | }, | ||
360 | { | ||
361 | role: 'unhide', | ||
362 | }, | ||
363 | { | ||
364 | type: 'separator', | ||
365 | }, | ||
366 | { | ||
367 | role: 'quit', | ||
368 | }, | ||
369 | ], | ||
370 | }); | ||
371 | |||
372 | const about = { | ||
373 | label: 'About Franz', | ||
374 | click: () => { | ||
375 | dialog.showMessageBox({ | ||
376 | type: 'info', | ||
377 | title: 'Franz', | ||
378 | message: 'Franz', | ||
379 | detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`, | ||
380 | }); | ||
381 | }, | ||
382 | }; | ||
383 | |||
384 | if (isMac) { | ||
209 | // Edit menu. | 385 | // Edit menu. |
210 | tpl[1].submenu.push( | 386 | tpl[1].submenu.push( |
211 | { | 387 | { |
@@ -223,25 +399,41 @@ export default class FranzMenu { | |||
223 | ], | 399 | ], |
224 | }, | 400 | }, |
225 | ); | 401 | ); |
402 | |||
403 | tpl[4].submenu.unshift(about, { | ||
404 | type: 'separator', | ||
405 | }); | ||
226 | } else { | 406 | } else { |
227 | tpl[4].submenu.unshift({ | 407 | tpl[0].submenu = [ |
228 | role: 'about', | 408 | { |
229 | click: () => { | 409 | label: 'Preferences...', |
230 | dialog.showMessageBox({ | 410 | accelerator: 'Ctrl+P', |
231 | type: 'info', | 411 | click: () => { |
232 | title: 'Franz', | 412 | this.actions.ui.openSettings({ path: 'app' }); |
233 | message: 'Franz', | 413 | }, |
234 | detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`, | ||
235 | }); | ||
236 | }, | 414 | }, |
237 | }); | 415 | { |
416 | type: 'separator', | ||
417 | }, | ||
418 | { | ||
419 | label: 'Quit', | ||
420 | accelerator: 'Alt+F4', | ||
421 | click: () => { | ||
422 | app.quit(); | ||
423 | }, | ||
424 | }, | ||
425 | ]; | ||
426 | |||
427 | tpl[5].submenu.push({ | ||
428 | type: 'separator', | ||
429 | }, about); | ||
238 | } | 430 | } |
239 | 431 | ||
240 | const serviceTpl = this.serviceTpl; | 432 | const serviceTpl = this.serviceTpl; |
241 | 433 | ||
242 | serviceTpl.unshift({ | 434 | serviceTpl.unshift({ |
243 | label: 'Add new Service', | 435 | label: 'Add new Service', |
244 | accelerator: 'CmdOrCtrl+N', | 436 | accelerator: `${cmdKey}+N`, |
245 | click: () => { | 437 | click: () => { |
246 | this.actions.ui.openSettings({ path: 'recipes' }); | 438 | this.actions.ui.openSettings({ path: 'recipes' }); |
247 | }, | 439 | }, |
@@ -250,7 +442,7 @@ export default class FranzMenu { | |||
250 | }); | 442 | }); |
251 | 443 | ||
252 | if (serviceTpl.length > 0) { | 444 | if (serviceTpl.length > 0) { |
253 | tpl[isMac ? 3 : 2].submenu = toJS(this.serviceTpl); | 445 | tpl[3].submenu = toJS(this.serviceTpl); |
254 | } | 446 | } |
255 | 447 | ||
256 | this.currentTemplate = tpl; | 448 | this.currentTemplate = tpl; |
@@ -264,7 +456,7 @@ export default class FranzMenu { | |||
264 | if (this.stores.user.isLoggedIn) { | 456 | if (this.stores.user.isLoggedIn) { |
265 | return services.map((service, i) => ({ | 457 | return services.map((service, i) => ({ |
266 | label: this._getServiceName(service), | 458 | label: this._getServiceName(service), |
267 | accelerator: i <= 9 ? `CmdOrCtrl+${i + 1}` : null, | 459 | accelerator: i <= 9 ? `${cmdKey}+${i + 1}` : null, |
268 | type: 'radio', | 460 | type: 'radio', |
269 | checked: service.isActive, | 461 | checked: service.isActive, |
270 | click: () => { | 462 | click: () => { |