diff options
Diffstat (limited to 'src/lib/Menu.js')
-rw-r--r-- | src/lib/Menu.js | 218 |
1 files changed, 186 insertions, 32 deletions
diff --git a/src/lib/Menu.js b/src/lib/Menu.js index c378619ad..da2540274 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js | |||
@@ -1,8 +1,13 @@ | |||
1 | import { remote, shell } from 'electron'; | 1 | import { remote, shell } from 'electron'; |
2 | import { observable, autorun, computed } from 'mobx'; | 2 | import { observable, autorun } from 'mobx'; |
3 | import { defineMessages } from 'react-intl'; | 3 | import { defineMessages } from 'react-intl'; |
4 | 4 | ||
5 | import { isMac, ctrlKey, cmdKey } from '../environment'; | 5 | import { isMac, ctrlKey, cmdKey } from '../environment'; |
6 | import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../features/workspaces/index'; | ||
7 | import { workspaceActions } from '../features/workspaces/actions'; | ||
8 | import { gaEvent } from './analytics'; | ||
9 | import { announcementActions } from '../features/announcements/actions'; | ||
10 | import { announcementsStore } from '../features/announcements'; | ||
6 | 11 | ||
7 | const { app, Menu, dialog } = remote; | 12 | const { app, Menu, dialog } = remote; |
8 | 13 | ||
@@ -155,6 +160,10 @@ const menuItems = defineMessages({ | |||
155 | id: 'menu.app.about', | 160 | id: 'menu.app.about', |
156 | defaultMessage: '!!!About Franz', | 161 | defaultMessage: '!!!About Franz', |
157 | }, | 162 | }, |
163 | announcement: { | ||
164 | id: 'menu.app.announcement', | ||
165 | defaultMessage: '!!!What\'s new?', | ||
166 | }, | ||
158 | settings: { | 167 | settings: { |
159 | id: 'menu.app.settings', | 168 | id: 'menu.app.settings', |
160 | defaultMessage: '!!!Settings', | 169 | defaultMessage: '!!!Settings', |
@@ -179,6 +188,42 @@ const menuItems = defineMessages({ | |||
179 | id: 'menu.services.addNewService', | 188 | id: 'menu.services.addNewService', |
180 | defaultMessage: '!!!Add New Service...', | 189 | defaultMessage: '!!!Add New Service...', |
181 | }, | 190 | }, |
191 | addNewWorkspace: { | ||
192 | id: 'menu.workspaces.addNewWorkspace', | ||
193 | defaultMessage: '!!!Add New Workspace...', | ||
194 | }, | ||
195 | openWorkspaceDrawer: { | ||
196 | id: 'menu.workspaces.openWorkspaceDrawer', | ||
197 | defaultMessage: '!!!Open workspace drawer', | ||
198 | }, | ||
199 | closeWorkspaceDrawer: { | ||
200 | id: 'menu.workspaces.closeWorkspaceDrawer', | ||
201 | defaultMessage: '!!!Close workspace drawer', | ||
202 | }, | ||
203 | activateNextService: { | ||
204 | id: 'menu.services.setNextServiceActive', | ||
205 | defaultMessage: '!!!Activate next service...', | ||
206 | }, | ||
207 | activatePreviousService: { | ||
208 | id: 'menu.services.activatePreviousService', | ||
209 | defaultMessage: '!!!Activate previous service...', | ||
210 | }, | ||
211 | muteApp: { | ||
212 | id: 'sidebar.muteApp', | ||
213 | defaultMessage: '!!!Disable notifications & audio', | ||
214 | }, | ||
215 | unmuteApp: { | ||
216 | id: 'sidebar.unmuteApp', | ||
217 | defaultMessage: '!!!Enable notifications & audio', | ||
218 | }, | ||
219 | workspaces: { | ||
220 | id: 'menu.workspaces', | ||
221 | defaultMessage: '!!!Workspaces', | ||
222 | }, | ||
223 | defaultWorkspace: { | ||
224 | id: 'menu.workspaces.defaultWorkspace', | ||
225 | defaultMessage: '!!!Default', | ||
226 | }, | ||
182 | }); | 227 | }); |
183 | 228 | ||
184 | function getActiveWebview() { | 229 | function getActiveWebview() { |
@@ -239,16 +284,32 @@ const _templateFactory = intl => [ | |||
239 | }, | 284 | }, |
240 | { | 285 | { |
241 | label: intl.formatMessage(menuItems.resetZoom), | 286 | label: intl.formatMessage(menuItems.resetZoom), |
242 | role: 'resetzoom', | 287 | accelerator: 'Cmd+0', |
288 | click() { | ||
289 | getActiveWebview().setZoomLevel(0); | ||
290 | }, | ||
243 | }, | 291 | }, |
244 | { | 292 | { |
245 | label: intl.formatMessage(menuItems.zoomIn), | 293 | label: intl.formatMessage(menuItems.zoomIn), |
246 | // accelerator: 'Cmd+=', | 294 | accelerator: 'Cmd+plus', |
247 | role: 'zoomin', | 295 | click() { |
296 | const activeService = getActiveWebview(); | ||
297 | activeService.getZoomLevel((level) => { | ||
298 | // level 9 =~ +300% and setZoomLevel wouldnt zoom in further | ||
299 | if (level < 9) activeService.setZoomLevel(level + 1); | ||
300 | }); | ||
301 | }, | ||
248 | }, | 302 | }, |
249 | { | 303 | { |
250 | label: intl.formatMessage(menuItems.zoomOut), | 304 | label: intl.formatMessage(menuItems.zoomOut), |
251 | role: 'zoomout', | 305 | accelerator: 'Cmd+-', |
306 | click() { | ||
307 | const activeService = getActiveWebview(); | ||
308 | activeService.getZoomLevel((level) => { | ||
309 | // level -9 =~ -50% and setZoomLevel wouldnt zoom out further | ||
310 | if (level > -9) activeService.setZoomLevel(level - 1); | ||
311 | }); | ||
312 | }, | ||
252 | }, | 313 | }, |
253 | { | 314 | { |
254 | type: 'separator', | 315 | type: 'separator', |
@@ -266,6 +327,11 @@ const _templateFactory = intl => [ | |||
266 | submenu: [], | 327 | submenu: [], |
267 | }, | 328 | }, |
268 | { | 329 | { |
330 | label: intl.formatMessage(menuItems.workspaces), | ||
331 | submenu: [], | ||
332 | visible: workspaceStore.isFeatureEnabled, | ||
333 | }, | ||
334 | { | ||
269 | label: intl.formatMessage(menuItems.window), | 335 | label: intl.formatMessage(menuItems.window), |
270 | role: 'window', | 336 | role: 'window', |
271 | submenu: [ | 337 | submenu: [ |
@@ -288,8 +354,11 @@ const _templateFactory = intl => [ | |||
288 | click() { shell.openExternal('https://meetfranz.com'); }, | 354 | click() { shell.openExternal('https://meetfranz.com'); }, |
289 | }, | 355 | }, |
290 | { | 356 | { |
291 | label: intl.formatMessage(menuItems.changelog), | 357 | label: intl.formatMessage(menuItems.announcement), |
292 | click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); }, | 358 | click: () => { |
359 | announcementActions.show(); | ||
360 | }, | ||
361 | visible: window.franz.stores.user.isLoggedIn && announcementsStore.areNewsAvailable, | ||
293 | }, | 362 | }, |
294 | { | 363 | { |
295 | type: 'separator', | 364 | type: 'separator', |
@@ -392,10 +461,12 @@ const _titleBarTemplateFactory = intl => [ | |||
392 | }, | 461 | }, |
393 | { | 462 | { |
394 | label: intl.formatMessage(menuItems.zoomIn), | 463 | label: intl.formatMessage(menuItems.zoomIn), |
395 | accelerator: `${ctrlKey}+Plus`, | 464 | accelerator: `${ctrlKey}+=`, |
396 | click() { | 465 | click() { |
397 | getActiveWebview().getZoomLevel((zoomLevel) => { | 466 | const activeService = getActiveWebview(); |
398 | getActiveWebview().setZoomLevel(zoomLevel === 5 ? zoomLevel : zoomLevel + 1); | 467 | activeService.getZoomLevel((level) => { |
468 | // level 9 =~ +300% and setZoomLevel wouldnt zoom in further | ||
469 | if (level < 9) activeService.setZoomLevel(level + 1); | ||
399 | }); | 470 | }); |
400 | }, | 471 | }, |
401 | }, | 472 | }, |
@@ -403,8 +474,10 @@ const _titleBarTemplateFactory = intl => [ | |||
403 | label: intl.formatMessage(menuItems.zoomOut), | 474 | label: intl.formatMessage(menuItems.zoomOut), |
404 | accelerator: `${ctrlKey}+-`, | 475 | accelerator: `${ctrlKey}+-`, |
405 | click() { | 476 | click() { |
406 | getActiveWebview().getZoomLevel((zoomLevel) => { | 477 | const activeService = getActiveWebview(); |
407 | getActiveWebview().setZoomLevel(zoomLevel === -5 ? zoomLevel : zoomLevel - 1); | 478 | activeService.getZoomLevel((level) => { |
479 | // level -9 =~ -50% and setZoomLevel wouldnt zoom out further | ||
480 | if (level > -9) activeService.setZoomLevel(level - 1); | ||
408 | }); | 481 | }); |
409 | }, | 482 | }, |
410 | }, | 483 | }, |
@@ -499,13 +572,14 @@ export default class FranzMenu { | |||
499 | } | 572 | } |
500 | 573 | ||
501 | _build() { | 574 | _build() { |
502 | const serviceTpl = Object.assign([], this.serviceTpl); // need to clone object so we don't modify computed (cached) object | 575 | // need to clone object so we don't modify computed (cached) object |
576 | const serviceTpl = Object.assign([], this.serviceTpl()); | ||
503 | 577 | ||
504 | if (window.franz === undefined) { | 578 | if (window.franz === undefined) { |
505 | return; | 579 | return; |
506 | } | 580 | } |
507 | 581 | ||
508 | const intl = window.franz.intl; | 582 | const { intl } = window.franz; |
509 | const tpl = isMac ? _templateFactory(intl) : _titleBarTemplateFactory(intl); | 583 | const tpl = isMac ? _templateFactory(intl) : _titleBarTemplateFactory(intl); |
510 | 584 | ||
511 | tpl[1].submenu.push({ | 585 | tpl[1].submenu.push({ |
@@ -632,7 +706,7 @@ export default class FranzMenu { | |||
632 | }, | 706 | }, |
633 | ); | 707 | ); |
634 | 708 | ||
635 | tpl[4].submenu.unshift(about, { | 709 | tpl[5].submenu.unshift(about, { |
636 | type: 'separator', | 710 | type: 'separator', |
637 | }); | 711 | }); |
638 | } else { | 712 | } else { |
@@ -663,42 +737,122 @@ export default class FranzMenu { | |||
663 | }, about); | 737 | }, about); |
664 | } | 738 | } |
665 | 739 | ||
666 | serviceTpl.unshift({ | 740 | if (serviceTpl.length > 0) { |
741 | tpl[3].submenu = serviceTpl; | ||
742 | } | ||
743 | |||
744 | if (workspaceStore.isFeatureEnabled) { | ||
745 | tpl[4].submenu = this.workspacesMenu(); | ||
746 | } | ||
747 | |||
748 | this.currentTemplate = tpl; | ||
749 | const menu = Menu.buildFromTemplate(tpl); | ||
750 | Menu.setApplicationMenu(menu); | ||
751 | } | ||
752 | |||
753 | serviceTpl() { | ||
754 | const { intl } = window.franz; | ||
755 | const { user, services, settings } = this.stores; | ||
756 | if (!user.isLoggedIn) return []; | ||
757 | const menu = []; | ||
758 | |||
759 | menu.push({ | ||
667 | label: intl.formatMessage(menuItems.addNewService), | 760 | label: intl.formatMessage(menuItems.addNewService), |
668 | accelerator: `${cmdKey}+N`, | 761 | accelerator: `${cmdKey}+N`, |
669 | click: () => { | 762 | click: () => { |
670 | this.actions.ui.openSettings({ path: 'recipes' }); | 763 | this.actions.ui.openSettings({ path: 'recipes' }); |
671 | }, | 764 | }, |
672 | enabled: this.stores.user.isLoggedIn, | 765 | }, { |
766 | type: 'separator', | ||
767 | }, { | ||
768 | label: intl.formatMessage(menuItems.activateNextService), | ||
769 | accelerator: `${cmdKey}+alt+right`, | ||
770 | click: () => this.actions.service.setActiveNext(), | ||
771 | }, { | ||
772 | label: intl.formatMessage(menuItems.activatePreviousService), | ||
773 | accelerator: `${cmdKey}+alt+left`, | ||
774 | click: () => this.actions.service.setActivePrev(), | ||
775 | }, { | ||
776 | label: intl.formatMessage( | ||
777 | settings.all.app.isAppMuted ? menuItems.unmuteApp : menuItems.muteApp, | ||
778 | ).replace('&', '&&'), | ||
779 | accelerator: `${cmdKey}+shift+m`, | ||
780 | click: () => this.actions.app.toggleMuteApp(), | ||
673 | }, { | 781 | }, { |
674 | type: 'separator', | 782 | type: 'separator', |
675 | }); | 783 | }); |
676 | 784 | ||
677 | if (serviceTpl.length > 0) { | 785 | services.allDisplayed.forEach((service, i) => (menu.push({ |
678 | tpl[3].submenu = serviceTpl; | 786 | label: this._getServiceName(service), |
679 | } | 787 | accelerator: i < 9 ? `${cmdKey}+${i + 1}` : null, |
788 | type: 'radio', | ||
789 | checked: service.isActive, | ||
790 | click: () => { | ||
791 | this.actions.service.setActive({ serviceId: service.id }); | ||
792 | }, | ||
793 | }))); | ||
680 | 794 | ||
681 | this.currentTemplate = tpl; | 795 | return menu; |
682 | const menu = Menu.buildFromTemplate(tpl); | ||
683 | Menu.setApplicationMenu(menu); | ||
684 | } | 796 | } |
685 | 797 | ||
686 | @computed get serviceTpl() { | 798 | workspacesMenu() { |
687 | const services = this.stores.services.allDisplayed; | 799 | const { workspaces, activeWorkspace, isWorkspaceDrawerOpen } = workspaceStore; |
800 | const { intl } = window.franz; | ||
801 | const menu = []; | ||
802 | |||
803 | // Add new workspace item: | ||
804 | menu.push({ | ||
805 | label: intl.formatMessage(menuItems.addNewWorkspace), | ||
806 | accelerator: `${cmdKey}+Shift+N`, | ||
807 | click: () => { | ||
808 | workspaceActions.openWorkspaceSettings(); | ||
809 | }, | ||
810 | enabled: this.stores.user.isLoggedIn, | ||
811 | }); | ||
812 | |||
813 | // Open workspace drawer: | ||
814 | const drawerLabel = ( | ||
815 | isWorkspaceDrawerOpen ? menuItems.closeWorkspaceDrawer : menuItems.openWorkspaceDrawer | ||
816 | ); | ||
817 | menu.push({ | ||
818 | label: intl.formatMessage(drawerLabel), | ||
819 | accelerator: `${cmdKey}+D`, | ||
820 | click: () => { | ||
821 | workspaceActions.toggleWorkspaceDrawer(); | ||
822 | gaEvent(GA_CATEGORY_WORKSPACES, 'toggleDrawer', 'menu'); | ||
823 | }, | ||
824 | enabled: this.stores.user.isLoggedIn, | ||
825 | }, { | ||
826 | type: 'separator', | ||
827 | }); | ||
828 | |||
829 | // Default workspace | ||
830 | menu.push({ | ||
831 | label: intl.formatMessage(menuItems.defaultWorkspace), | ||
832 | accelerator: `${cmdKey}+Alt+0`, | ||
833 | type: 'radio', | ||
834 | checked: !activeWorkspace, | ||
835 | click: () => { | ||
836 | workspaceActions.deactivate(); | ||
837 | gaEvent(GA_CATEGORY_WORKSPACES, 'switch', 'menu'); | ||
838 | }, | ||
839 | }); | ||
688 | 840 | ||
689 | if (this.stores.user.isLoggedIn) { | 841 | // Workspace items |
690 | return services.map((service, i) => ({ | 842 | if (this.stores.user.isPremium) { |
691 | label: this._getServiceName(service), | 843 | workspaces.forEach((workspace, i) => menu.push({ |
692 | accelerator: i < 9 ? `${cmdKey}+${i + 1}` : null, | 844 | label: workspace.name, |
845 | accelerator: i < 9 ? `${cmdKey}+Alt+${i + 1}` : null, | ||
693 | type: 'radio', | 846 | type: 'radio', |
694 | checked: service.isActive, | 847 | checked: activeWorkspace ? workspace.id === activeWorkspace.id : false, |
695 | click: () => { | 848 | click: () => { |
696 | this.actions.service.setActive({ serviceId: service.id }); | 849 | workspaceActions.activate({ workspace }); |
850 | gaEvent(GA_CATEGORY_WORKSPACES, 'switch', 'menu'); | ||
697 | }, | 851 | }, |
698 | })); | 852 | })); |
699 | } | 853 | } |
700 | 854 | ||
701 | return []; | 855 | return menu; |
702 | } | 856 | } |
703 | 857 | ||
704 | _getServiceName(service) { | 858 | _getServiceName(service) { |