aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/Menu.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Menu.js')
-rw-r--r--src/lib/Menu.js218
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 @@
1import { remote, shell } from 'electron'; 1import { remote, shell } from 'electron';
2import { observable, autorun, computed } from 'mobx'; 2import { observable, autorun } from 'mobx';
3import { defineMessages } from 'react-intl'; 3import { defineMessages } from 'react-intl';
4 4
5import { isMac, ctrlKey, cmdKey } from '../environment'; 5import { isMac, ctrlKey, cmdKey } from '../environment';
6import { GA_CATEGORY_WORKSPACES, workspaceStore } from '../features/workspaces/index';
7import { workspaceActions } from '../features/workspaces/actions';
8import { gaEvent } from './analytics';
9import { announcementActions } from '../features/announcements/actions';
10import { announcementsStore } from '../features/announcements';
6 11
7const { app, Menu, dialog } = remote; 12const { 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
184function getActiveWebview() { 229function 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) {