aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorLibravatar Vijay A <avijayr@protonmail.com>2021-09-11 07:30:56 +0530
committerLibravatar Vijay A <avijayr@protonmail.com>2021-09-11 07:30:56 +0530
commita5f3f635525d270bf2b7326fe1c3846b05f87b5d (patch)
tree403a37786e37ab59ecdd78ff74bc0629ce1d17eb /src/lib
parentdocs: updated Changelog with cumulative changes for 5.6.1-beta.3 [skip ci] (diff)
parent5.6.1-nightly.56 [skip ci] (diff)
downloadferdium-app-a5f3f635525d270bf2b7326fe1c3846b05f87b5d.tar.gz
ferdium-app-a5f3f635525d270bf2b7326fe1c3846b05f87b5d.tar.zst
ferdium-app-a5f3f635525d270bf2b7326fe1c3846b05f87b5d.zip
Merge branch 'nightly' into release
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Menu.js552
-rw-r--r--src/lib/Tray.js24
2 files changed, 334 insertions, 242 deletions
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 5a99299c5..2c24b696b 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -2,18 +2,32 @@ import { clipboard } from 'electron';
2import { app, Menu, dialog, systemPreferences } from '@electron/remote'; 2import { app, Menu, dialog, systemPreferences } from '@electron/remote';
3import { autorun, observable } from 'mobx'; 3import { autorun, observable } from 'mobx';
4import { defineMessages } from 'react-intl'; 4import { defineMessages } from 'react-intl';
5import { CUSTOM_WEBSITE_RECIPE_ID, GITHUB_FERDI_URL, LIVE_API_FERDI_WEBSITE } from '../config';
6import { 5import {
7 cmdOrCtrlShortcutKey, altKey, shiftKey, settingsShortcutKey, isLinux, isMac, aboutAppDetails, lockFerdiShortcutKey, todosToggleShortcutKey, workspaceToggleShortcutKey, addNewServiceShortcutKey, muteFerdiShortcutKey, 6 CUSTOM_WEBSITE_RECIPE_ID,
7 GITHUB_FERDI_URL,
8 LIVE_API_FERDI_WEBSITE,
9} from '../config';
10import {
11 cmdOrCtrlShortcutKey,
12 altKey,
13 shiftKey,
14 settingsShortcutKey,
15 isLinux,
16 isMac,
17 aboutAppDetails,
18 lockFerdiShortcutKey,
19 todosToggleShortcutKey,
20 workspaceToggleShortcutKey,
21 addNewServiceShortcutKey,
22 muteFerdiShortcutKey,
8} from '../environment'; 23} from '../environment';
9import { announcementsStore } from '../features/announcements';
10import { announcementActions } from '../features/announcements/actions';
11import { todosStore } from '../features/todos'; 24import { todosStore } from '../features/todos';
12import { todoActions } from '../features/todos/actions'; 25import { todoActions } from '../features/todos/actions';
13import { workspaceActions } from '../features/workspaces/actions'; 26import { workspaceActions } from '../features/workspaces/actions';
14import { workspaceStore } from '../features/workspaces/index'; 27import { workspaceStore } from '../features/workspaces/index';
15import apiBase, { termsBase } from '../api/apiBase'; 28import apiBase, { termsBase } from '../api/apiBase';
16import { openExternalUrl } from '../helpers/url-helpers'; 29import { openExternalUrl } from '../helpers/url-helpers';
30import globalMessages from '../i18n/globalMessages';
17 31
18const menuItems = defineMessages({ 32const menuItems = defineMessages({
19 edit: { 33 edit: {
@@ -174,7 +188,8 @@ const menuItems = defineMessages({
174 }, 188 },
175 debugInfoCopiedBody: { 189 debugInfoCopiedBody: {
176 id: 'menu.help.debugInfoCopiedBody', 190 id: 'menu.help.debugInfoCopiedBody',
177 defaultMessage: '!!!Your Debug Information has been copied to your clipboard.', 191 defaultMessage:
192 '!!!Your Debug Information has been copied to your clipboard.',
178 }, 193 },
179 touchId: { 194 touchId: {
180 id: 'locked.touchId', 195 id: 'locked.touchId',
@@ -216,14 +231,6 @@ const menuItems = defineMessages({
216 id: 'menu.app.about', 231 id: 'menu.app.about',
217 defaultMessage: '!!!About Ferdi', 232 defaultMessage: '!!!About Ferdi',
218 }, 233 },
219 announcement: {
220 id: 'menu.app.announcement',
221 defaultMessage: '!!!What\'s new?',
222 },
223 settings: {
224 id: 'menu.app.settings',
225 defaultMessage: '!!!Settings',
226 },
227 checkForUpdates: { 234 checkForUpdates: {
228 id: 'menu.app.checkForUpdates', 235 id: 'menu.app.checkForUpdates',
229 defaultMessage: '!!!Check for updates', 236 defaultMessage: '!!!Check for updates',
@@ -244,10 +251,6 @@ const menuItems = defineMessages({
244 id: 'menu.app.autohideMenuBar', 251 id: 'menu.app.autohideMenuBar',
245 defaultMessage: '!!!Auto-hide menu bar', 252 defaultMessage: '!!!Auto-hide menu bar',
246 }, 253 },
247 quit: {
248 id: 'menu.app.quit',
249 defaultMessage: '!!!Quit',
250 },
251 addNewService: { 254 addNewService: {
252 id: 'menu.services.addNewService', 255 id: 'menu.services.addNewService',
253 defaultMessage: '!!!Add New Service...', 256 defaultMessage: '!!!Add New Service...',
@@ -310,8 +313,8 @@ const menuItems = defineMessages({
310 }, 313 },
311}); 314});
312 315
313function getActiveWebview() { 316function getActiveService() {
314 return window.ferdi.stores.services.active.webview; 317 return window.ferdi.stores.services.active;
315} 318}
316 319
317const _titleBarTemplateFactory = (intl, locked) => [ 320const _titleBarTemplateFactory = (intl, locked) => [
@@ -349,9 +352,6 @@ const _titleBarTemplateFactory = (intl, locked) => [
349 label: intl.formatMessage(menuItems.pasteAndMatchStyle), 352 label: intl.formatMessage(menuItems.pasteAndMatchStyle),
350 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+V`, // Override the accelerator since this adds new key combo in macos 353 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+V`, // Override the accelerator since this adds new key combo in macos
351 role: 'pasteAndMatchStyle', 354 role: 'pasteAndMatchStyle',
352 click() {
353 getActiveWebview().pasteAndMatchStyle();
354 },
355 }, 355 },
356 { 356 {
357 label: intl.formatMessage(menuItems.delete), 357 label: intl.formatMessage(menuItems.delete),
@@ -386,18 +386,18 @@ const _titleBarTemplateFactory = (intl, locked) => [
386 label: intl.formatMessage(menuItems.findInPage), 386 label: intl.formatMessage(menuItems.findInPage),
387 accelerator: `${cmdOrCtrlShortcutKey()}+F`, 387 accelerator: `${cmdOrCtrlShortcutKey()}+F`,
388 click() { 388 click() {
389 const service = getActiveService();
389 // Check if there is a service active 390 // Check if there is a service active
390 if (!window.ferdi.stores.services.active) return; 391 if (service) {
392 // Focus webview so find in page popup gets focused
393 service.webview.focus();
391 394
392 // Focus webview so find in page popup gets focused 395 window.ferdi.actions.service.sendIPCMessage({
393 window.ferdi.stores.services.active.webview.focus(); 396 serviceId: service.id,
394 397 channel: 'find-in-page',
395 const currentService = window.ferdi.stores.services.active.id; 398 args: {},
396 window.ferdi.actions.service.sendIPCMessage({ 399 });
397 serviceId: currentService, 400 }
398 channel: 'find-in-page',
399 args: {},
400 });
401 }, 401 },
402 }, 402 },
403 { 403 {
@@ -407,14 +407,14 @@ const _titleBarTemplateFactory = (intl, locked) => [
407 label: intl.formatMessage(menuItems.back), 407 label: intl.formatMessage(menuItems.back),
408 accelerator: `${cmdOrCtrlShortcutKey()}+Left`, 408 accelerator: `${cmdOrCtrlShortcutKey()}+Left`,
409 click() { 409 click() {
410 getActiveWebview().goBack(); 410 getActiveService().webview.goBack();
411 }, 411 },
412 }, 412 },
413 { 413 {
414 label: intl.formatMessage(menuItems.forward), 414 label: intl.formatMessage(menuItems.forward),
415 accelerator: `${cmdOrCtrlShortcutKey()}+Right`, 415 accelerator: `${cmdOrCtrlShortcutKey()}+Right`,
416 click() { 416 click() {
417 getActiveWebview().goForward(); 417 getActiveService().webview.goForward();
418 }, 418 },
419 }, 419 },
420 { 420 {
@@ -423,17 +423,15 @@ const _titleBarTemplateFactory = (intl, locked) => [
423 { 423 {
424 label: intl.formatMessage(menuItems.resetZoom), 424 label: intl.formatMessage(menuItems.resetZoom),
425 accelerator: `${cmdOrCtrlShortcutKey()}+0`, 425 accelerator: `${cmdOrCtrlShortcutKey()}+0`,
426 role: 'resetZoom',
427 click() { 426 click() {
428 getActiveWebview().setZoomLevel(0); 427 getActiveService().webview.setZoomLevel(0);
429 }, 428 },
430 }, 429 },
431 { 430 {
432 label: intl.formatMessage(menuItems.zoomIn), 431 label: intl.formatMessage(menuItems.zoomIn),
433 accelerator: `${cmdOrCtrlShortcutKey()}+plus`, 432 accelerator: `${cmdOrCtrlShortcutKey()}+plus`,
434 role: 'zoomIn',
435 click() { 433 click() {
436 const activeService = getActiveWebview(); 434 const activeService = getActiveService().webview;
437 const level = activeService.getZoomLevel(); 435 const level = activeService.getZoomLevel();
438 436
439 // level 9 =~ +300% and setZoomLevel wouldnt zoom in further 437 // level 9 =~ +300% and setZoomLevel wouldnt zoom in further
@@ -443,9 +441,8 @@ const _titleBarTemplateFactory = (intl, locked) => [
443 { 441 {
444 label: intl.formatMessage(menuItems.zoomOut), 442 label: intl.formatMessage(menuItems.zoomOut),
445 accelerator: `${cmdOrCtrlShortcutKey()}+-`, 443 accelerator: `${cmdOrCtrlShortcutKey()}+-`,
446 role: 'zoomOut',
447 click() { 444 click() {
448 const activeService = getActiveWebview(); 445 const activeService = getActiveService().webview;
449 const level = activeService.getZoomLevel(); 446 const level = activeService.getZoomLevel();
450 447
451 // level -9 =~ -50% and setZoomLevel wouldnt zoom out further 448 // level -9 =~ -50% and setZoomLevel wouldnt zoom out further
@@ -513,44 +510,49 @@ const _titleBarTemplateFactory = (intl, locked) => [
513 submenu: [ 510 submenu: [
514 { 511 {
515 label: intl.formatMessage(menuItems.learnMore), 512 label: intl.formatMessage(menuItems.learnMore),
516 click() { openExternalUrl(LIVE_API_FERDI_WEBSITE, true); }, 513 click() {
514 openExternalUrl(LIVE_API_FERDI_WEBSITE, true);
515 },
517 }, 516 },
518 { 517 {
519 label: intl.formatMessage(menuItems.changelog), 518 label: intl.formatMessage(menuItems.changelog),
520 click() { openExternalUrl(`${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`, true); }, 519 click() {
520 openExternalUrl(
521 `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`,
522 true,
523 );
524 },
521 }, 525 },
522 { 526 {
523 label: intl.formatMessage(menuItems.importExportData), 527 label: intl.formatMessage(menuItems.importExportData),
524 click() { openExternalUrl(apiBase(false), true); }, 528 click() {
525 enabled: !locked, 529 openExternalUrl(apiBase(false), true);
526 },
527 {
528 type: 'separator',
529 },
530 {
531 label: intl.formatMessage(menuItems.announcement),
532 click: () => {
533 announcementActions.show();
534 }, 530 },
535 enabled: !locked && window.ferdi.stores.user.isLoggedIn && announcementsStore.areNewsAvailable, 531 enabled: !locked,
536 }, 532 },
537 { 533 {
538 type: 'separator', 534 type: 'separator',
539 }, 535 },
540 { 536 {
541 label: intl.formatMessage(menuItems.support), 537 label: intl.formatMessage(menuItems.support),
542 click() { openExternalUrl(`${LIVE_API_FERDI_WEBSITE}/contact`, true); }, 538 click() {
539 openExternalUrl(`${LIVE_API_FERDI_WEBSITE}/contact`, true);
540 },
543 }, 541 },
544 { 542 {
545 type: 'separator', 543 type: 'separator',
546 }, 544 },
547 { 545 {
548 label: intl.formatMessage(menuItems.tos), 546 label: intl.formatMessage(menuItems.tos),
549 click() { openExternalUrl(`${termsBase()}/terms`, true); }, 547 click() {
548 openExternalUrl(`${termsBase()}/terms`, true);
549 },
550 }, 550 },
551 { 551 {
552 label: intl.formatMessage(menuItems.privacy), 552 label: intl.formatMessage(menuItems.privacy),
553 click() { openExternalUrl(`${termsBase()}/privacy`, true); }, 553 click() {
554 openExternalUrl(`${termsBase()}/privacy`, true);
555 },
554 }, 556 },
555 ], 557 ],
556 }, 558 },
@@ -590,6 +592,23 @@ export default class FranzMenu {
590 const tpl = _titleBarTemplateFactory(intl, this.stores.settings.app.locked); 592 const tpl = _titleBarTemplateFactory(intl, this.stores.settings.app.locked);
591 const { actions } = this; 593 const { actions } = this;
592 594
595 // TODO: Extract this into a reusable component and remove the duplications
596 const quitApp = () => {
597 const yesButtonIndex = 0;
598 let selection = yesButtonIndex;
599 if (window.ferdi.stores.settings.app.confirmOnQuit) {
600 selection = dialog.showMessageBoxSync(app.mainWindow, {
601 type: 'question',
602 message: intl.formatMessage(globalMessages.quit),
603 detail: intl.formatMessage(globalMessages.quitConfirmation),
604 buttons: [intl.formatMessage(globalMessages.yes), intl.formatMessage(globalMessages.no)],
605 });
606 }
607 if (selection === yesButtonIndex) {
608 app.quit();
609 }
610 };
611
593 if (!isMac) { 612 if (!isMac) {
594 tpl[1].submenu.push({ 613 tpl[1].submenu.push({
595 label: intl.formatMessage(menuItems.autohideMenuBar), 614 label: intl.formatMessage(menuItems.autohideMenuBar),
@@ -599,7 +618,8 @@ export default class FranzMenu {
599 window.ferdi.actions.settings.update({ 618 window.ferdi.actions.settings.update({
600 type: 'app', 619 type: 'app',
601 data: { 620 data: {
602 autohideMenuBar: !window.ferdi.stores.settings.app.autohideMenuBar, 621 autohideMenuBar:
622 !window.ferdi.stores.settings.app.autohideMenuBar,
603 }, 623 },
604 }); 624 });
605 }, 625 },
@@ -607,22 +627,28 @@ export default class FranzMenu {
607 } 627 }
608 628
609 if (!this.stores.settings.app.locked) { 629 if (!this.stores.settings.app.locked) {
610 tpl[1].submenu.push({ 630 tpl[1].submenu.push(
611 type: 'separator', 631 {
612 }, { 632 type: 'separator',
613 label: intl.formatMessage(menuItems.toggleDevTools),
614 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+I`,
615 click: (menuItem, browserWindow) => {
616 browserWindow.webContents.toggleDevTools();
617 }, 633 },
618 }, { 634 {
619 label: intl.formatMessage(menuItems.toggleServiceDevTools), 635 label: intl.formatMessage(menuItems.toggleDevTools),
620 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+I`, 636 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+I`,
621 click: () => { 637 click: (menuItem, browserWindow) => {
622 this.actions.service.openDevToolsForActiveService(); 638 browserWindow.webContents.toggleDevTools();
639 },
623 }, 640 },
624 enabled: this.stores.user.isLoggedIn && this.stores.services.enabled.length > 0, 641 {
625 }); 642 label: intl.formatMessage(menuItems.toggleServiceDevTools),
643 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+I`,
644 click: () => {
645 this.actions.service.openDevToolsForActiveService();
646 },
647 enabled:
648 this.stores.user.isLoggedIn &&
649 this.stores.services.enabled.length > 0,
650 },
651 );
626 652
627 if (this.stores.features.features.isTodosEnabled) { 653 if (this.stores.features.features.isTodosEnabled) {
628 tpl[1].submenu.push({ 654 tpl[1].submenu.push({
@@ -635,49 +661,58 @@ export default class FranzMenu {
635 }); 661 });
636 } 662 }
637 663
638 tpl[1].submenu.unshift({ 664 tpl[1].submenu.unshift(
639 label: intl.formatMessage(menuItems.reloadService), 665 {
640 id: 'reloadService', // TODO: needed? 666 label: intl.formatMessage(menuItems.reloadService),
641 accelerator: `${cmdOrCtrlShortcutKey()}+R`, 667 accelerator: `${cmdOrCtrlShortcutKey()}+R`,
642 click: () => { 668 click: () => {
643 if (this.stores.user.isLoggedIn 669 if (
644 && this.stores.services.enabled.length > 0) { 670 this.stores.user.isLoggedIn &&
645 if (this.stores.services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID) { 671 this.stores.services.enabled.length > 0
646 this.stores.services.active.webview.reload(); 672 ) {
673 if (getActiveService().recipe.id === CUSTOM_WEBSITE_RECIPE_ID) {
674 getActiveService().webview.reload();
675 } else {
676 this.actions.service.reloadActive();
677 }
647 } else { 678 } else {
648 this.actions.service.reloadActive(); 679 window.location.reload();
649 } 680 }
650 } else { 681 },
682 },
683 {
684 label: intl.formatMessage(menuItems.reloadFerdi),
685 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+R`,
686 click: () => {
651 window.location.reload(); 687 window.location.reload();
652 } 688 },
653 }, 689 },
654 }, { 690 {
655 label: intl.formatMessage(menuItems.reloadFerdi), 691 label: intl.formatMessage(menuItems.reloadTodos),
656 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+R`, 692 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+R`,
657 click: () => { 693 click: () => {
658 window.location.reload(); 694 this.actions.todos.reload();
695 },
659 }, 696 },
660 }, { 697 {
661 label: intl.formatMessage(menuItems.reloadTodos), 698 type: 'separator',
662 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+${altKey()}+R`,
663 click: () => {
664 this.actions.todos.reload();
665 }, 699 },
666 }, { 700 {
667 type: 'separator', 701 label: intl.formatMessage(menuItems.lockFerdi),
668 }, { 702 accelerator: `${lockFerdiShortcutKey()}`,
669 label: intl.formatMessage(menuItems.lockFerdi), 703 enabled:
670 accelerator: `${lockFerdiShortcutKey()}`, 704 this.stores.user.isLoggedIn &&
671 enabled: this.stores.user.isLoggedIn && this.stores.settings.app.lockingFeatureEnabled, 705 this.stores.settings.app.lockingFeatureEnabled,
672 click() { 706 click() {
673 actions.settings.update({ 707 actions.settings.update({
674 type: 'app', 708 type: 'app',
675 data: { 709 data: {
676 locked: true, 710 locked: true,
677 }, 711 },
678 }); 712 });
713 },
679 }, 714 },
680 }); 715 );
681 716
682 if (serviceTpl.length > 0) { 717 if (serviceTpl.length > 0) {
683 tpl[2].submenu = serviceTpl; 718 tpl[2].submenu = serviceTpl;
@@ -691,26 +726,34 @@ export default class FranzMenu {
691 tpl[4].submenu = this.todosMenu(); 726 tpl[4].submenu = this.todosMenu();
692 } 727 }
693 } else { 728 } else {
694 const touchIdEnabled = isMac ? (this.stores.settings.app.useTouchIdToUnlock && systemPreferences.canPromptTouchID()) : false; 729 const touchIdEnabled = isMac
730 ? this.stores.settings.app.useTouchIdToUnlock &&
731 systemPreferences.canPromptTouchID()
732 : false;
695 733
696 tpl[0].submenu.unshift({ 734 tpl[0].submenu.unshift(
697 label: intl.formatMessage(menuItems.touchId), 735 {
698 accelerator: `${lockFerdiShortcutKey()}`, 736 label: intl.formatMessage(menuItems.touchId),
699 visible: touchIdEnabled, 737 accelerator: `${lockFerdiShortcutKey()}`,
700 click() { 738 visible: touchIdEnabled,
701 systemPreferences.promptTouchID(intl.formatMessage(menuItems.touchIdPrompt)).then(() => { 739 click() {
702 actions.settings.update({ 740 systemPreferences
703 type: 'app', 741 .promptTouchID(intl.formatMessage(menuItems.touchIdPrompt))
704 data: { 742 .then(() => {
705 locked: false, 743 actions.settings.update({
706 }, 744 type: 'app',
707 }); 745 data: {
708 }); 746 locked: false,
747 },
748 });
749 });
750 },
709 }, 751 },
710 }, { 752 {
711 type: 'separator', 753 type: 'separator',
712 visible: touchIdEnabled, 754 visible: touchIdEnabled,
713 }); 755 },
756 );
714 } 757 }
715 758
716 tpl.unshift({ 759 tpl.unshift({
@@ -725,7 +768,7 @@ export default class FranzMenu {
725 type: 'separator', 768 type: 'separator',
726 }, 769 },
727 { 770 {
728 label: intl.formatMessage(menuItems.settings), 771 label: intl.formatMessage(globalMessages.settings),
729 accelerator: `${settingsShortcutKey()}`, 772 accelerator: `${settingsShortcutKey()}`,
730 click: () => { 773 click: () => {
731 this.actions.ui.openSettings({ path: 'app' }); 774 this.actions.ui.openSettings({ path: 'app' });
@@ -768,18 +811,15 @@ export default class FranzMenu {
768 type: 'separator', 811 type: 'separator',
769 }, 812 },
770 { 813 {
771 label: intl.formatMessage(menuItems.quit), 814 label: intl.formatMessage(globalMessages.quit),
772 role: 'quit', 815 accelerator: `${cmdOrCtrlShortcutKey()}+Q`,
773 click() { 816 click: quitApp,
774 app.quit();
775 },
776 }, 817 },
777 ], 818 ],
778 }); 819 });
779 820
780 const about = { 821 const about = {
781 label: intl.formatMessage(menuItems.about), 822 label: intl.formatMessage(menuItems.about),
782 role: 'about',
783 click: () => { 823 click: () => {
784 dialog.showMessageBox({ 824 dialog.showMessageBox({
785 type: 'info', 825 type: 'info',
@@ -829,18 +869,18 @@ export default class FranzMenu {
829 type: 'separator', 869 type: 'separator',
830 }, 870 },
831 { 871 {
832 label: intl.formatMessage(menuItems.quit), 872 label: intl.formatMessage(globalMessages.quit),
833 role: 'quit',
834 accelerator: `${cmdOrCtrlShortcutKey()}+Q`, 873 accelerator: `${cmdOrCtrlShortcutKey()}+Q`,
835 click() { 874 click: quitApp,
836 app.quit();
837 },
838 }, 875 },
839 ]; 876 ];
840 877
841 tpl[tpl.length - 1].submenu.push({ 878 tpl[tpl.length - 1].submenu.push(
842 type: 'separator', 879 {
843 }, about); 880 type: 'separator',
881 },
882 about,
883 );
844 } 884 }
845 885
846 if (!this.stores.settings.app.locked) { 886 if (!this.stores.settings.app.locked) {
@@ -856,9 +896,12 @@ export default class FranzMenu {
856 tpl[5].submenu = this.todosMenu(); 896 tpl[5].submenu = this.todosMenu();
857 } 897 }
858 898
859 tpl[tpl.length - 1].submenu.push({ 899 tpl[tpl.length - 1].submenu.push(
860 type: 'separator', 900 {
861 }, ...this.debugMenu()); 901 type: 'separator',
902 },
903 ...this.debugMenu(),
904 );
862 } 905 }
863 this.currentTemplate = tpl; 906 this.currentTemplate = tpl;
864 const menu = Menu.buildFromTemplate(tpl); 907 const menu = Menu.buildFromTemplate(tpl);
@@ -872,73 +915,95 @@ export default class FranzMenu {
872 const menu = []; 915 const menu = [];
873 const cmdAltShortcutsVisibile = !isLinux; 916 const cmdAltShortcutsVisibile = !isLinux;
874 917
875 menu.push({ 918 menu.push(
876 label: intl.formatMessage(menuItems.addNewService), 919 {
877 accelerator: `${addNewServiceShortcutKey()}`, 920 label: intl.formatMessage(menuItems.addNewService),
878 click: () => { 921 accelerator: `${addNewServiceShortcutKey()}`,
879 this.actions.ui.openSettings({ path: 'recipes' }); 922 click: () => {
923 this.actions.ui.openSettings({ path: 'recipes' });
924 },
880 }, 925 },
881 }, { 926 {
882 type: 'separator', 927 type: 'separator',
883 }, { 928 },
884 label: intl.formatMessage(menuItems.activateNextService), 929 {
885 accelerator: `${cmdOrCtrlShortcutKey()}+tab`, 930 label: intl.formatMessage(menuItems.activateNextService),
886 click: () => this.actions.service.setActiveNext(), 931 accelerator: `${cmdOrCtrlShortcutKey()}+tab`,
887 visible: !cmdAltShortcutsVisibile, 932 click: () => this.actions.service.setActiveNext(),
888 }, { 933 visible: !cmdAltShortcutsVisibile,
889 label: intl.formatMessage(menuItems.activateNextService), 934 },
890 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+right`, 935 {
891 click: () => this.actions.service.setActiveNext(), 936 label: intl.formatMessage(menuItems.activateNextService),
892 visible: cmdAltShortcutsVisibile, 937 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+right`,
893 }, { 938 click: () => this.actions.service.setActiveNext(),
894 label: intl.formatMessage(menuItems.activatePreviousService), 939 visible: cmdAltShortcutsVisibile,
895 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`, 940 },
896 click: () => this.actions.service.setActivePrev(), 941 {
897 visible: !cmdAltShortcutsVisibile, 942 label: intl.formatMessage(menuItems.activatePreviousService),
898 }, { 943 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+tab`,
899 label: intl.formatMessage(menuItems.activatePreviousService), 944 click: () => this.actions.service.setActivePrev(),
900 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+left`, 945 visible: !cmdAltShortcutsVisibile,
901 click: () => this.actions.service.setActivePrev(), 946 },
902 visible: cmdAltShortcutsVisibile, 947 {
903 }, { 948 label: intl.formatMessage(menuItems.activatePreviousService),
904 label: intl.formatMessage( 949 accelerator: `${cmdOrCtrlShortcutKey()}+${altKey()}+left`,
905 settings.all.app.isAppMuted ? menuItems.unmuteApp : menuItems.muteApp, 950 click: () => this.actions.service.setActivePrev(),
906 ).replace('&', '&&'), 951 visible: cmdAltShortcutsVisibile,
907 accelerator: `${muteFerdiShortcutKey()}`, 952 },
908 click: () => this.actions.app.toggleMuteApp(), 953 {
909 }, { 954 label: intl
910 type: 'separator', 955 .formatMessage(
911 }); 956 settings.all.app.isAppMuted
912 957 ? menuItems.unmuteApp
913 services.allDisplayed.forEach((service, i) => (menu.push({ 958 : menuItems.muteApp,
914 label: this._getServiceName(service), 959 )
915 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${i + 1}` : null, 960 .replace('&', '&&'),
916 type: 'radio', 961 accelerator: `${muteFerdiShortcutKey()}`,
917 checked: service.isActive, 962 click: () => this.actions.app.toggleMuteApp(),
918 click: () => { 963 },
919 this.actions.service.setActive({ serviceId: service.id }); 964 {
920 965 type: 'separator',
921 if (isMac && i === 0) {
922 app.mainWindow.restore();
923 }
924 }, 966 },
925 }))); 967 );
926 968
927 if (services.active && services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID) { 969 services.allDisplayed.forEach((service, i) =>
928 menu.push({ 970 menu.push({
929 type: 'separator', 971 label: this._getServiceName(service),
930 }, { 972 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${i + 1}` : null,
931 label: intl.formatMessage(menuItems.serviceGoHome), 973 type: 'radio',
932 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+H`, 974 checked: service.isActive,
933 click: () => this.actions.service.reloadActive(), 975 click: () => {
934 }); 976 this.actions.service.setActive({ serviceId: service.id });
977
978 if (isMac && i === 0) {
979 app.mainWindow.restore();
980 }
981 },
982 }),
983 );
984
985 if (
986 services.active &&
987 services.active.recipe.id === CUSTOM_WEBSITE_RECIPE_ID
988 ) {
989 menu.push(
990 {
991 type: 'separator',
992 },
993 {
994 label: intl.formatMessage(menuItems.serviceGoHome),
995 accelerator: `${cmdOrCtrlShortcutKey()}+${shiftKey()}+H`,
996 click: () => this.actions.service.reloadActive(),
997 },
998 );
935 } 999 }
936 1000
937 return menu; 1001 return menu;
938 } 1002 }
939 1003
940 workspacesMenu() { 1004 workspacesMenu() {
941 const { workspaces, activeWorkspace, isWorkspaceDrawerOpen } = workspaceStore; 1005 const { workspaces, activeWorkspace, isWorkspaceDrawerOpen } =
1006 workspaceStore;
942 const { intl } = window.ferdi; 1007 const { intl } = window.ferdi;
943 const menu = []; 1008 const menu = [];
944 1009
@@ -954,9 +1019,9 @@ export default class FranzMenu {
954 1019
955 // Open workspace drawer: 1020 // Open workspace drawer:
956 if (!this.stores.settings.app.alwaysShowWorkspaces) { 1021 if (!this.stores.settings.app.alwaysShowWorkspaces) {
957 const drawerLabel = ( 1022 const drawerLabel = isWorkspaceDrawerOpen
958 isWorkspaceDrawerOpen ? menuItems.closeWorkspaceDrawer : menuItems.openWorkspaceDrawer 1023 ? menuItems.closeWorkspaceDrawer
959 ); 1024 : menuItems.openWorkspaceDrawer;
960 menu.push({ 1025 menu.push({
961 label: intl.formatMessage(drawerLabel), 1026 label: intl.formatMessage(drawerLabel),
962 accelerator: `${workspaceToggleShortcutKey()}`, 1027 accelerator: `${workspaceToggleShortcutKey()}`,
@@ -983,15 +1048,18 @@ export default class FranzMenu {
983 }); 1048 });
984 1049
985 // Workspace items 1050 // Workspace items
986 workspaces.forEach((workspace, i) => menu.push({ 1051 workspaces.forEach((workspace, i) =>
987 label: workspace.name, 1052 menu.push({
988 accelerator: i < 9 ? `${cmdOrCtrlShortcutKey()}+${altKey()}+${i + 1}` : null, 1053 label: workspace.name,
989 type: 'radio', 1054 accelerator:
990 checked: activeWorkspace ? workspace.id === activeWorkspace.id : false, 1055 i < 9 ? `${cmdOrCtrlShortcutKey()}+${altKey()}+${i + 1}` : null,
991 click: () => { 1056 type: 'radio',
992 workspaceActions.activate({ workspace }); 1057 checked: activeWorkspace ? workspace.id === activeWorkspace.id : false,
993 }, 1058 click: () => {
994 })); 1059 workspaceActions.activate({ workspace });
1060 },
1061 }),
1062 );
995 1063
996 return menu; 1064 return menu;
997 } 1065 }
@@ -1001,7 +1069,9 @@ export default class FranzMenu {
1001 const { intl } = window.ferdi; 1069 const { intl } = window.ferdi;
1002 const menu = []; 1070 const menu = [];
1003 1071
1004 const drawerLabel = isTodosPanelVisible ? menuItems.closeTodosDrawer : menuItems.openTodosDrawer; 1072 const drawerLabel = isTodosPanelVisible
1073 ? menuItems.closeTodosDrawer
1074 : menuItems.openTodosDrawer;
1005 1075
1006 menu.push({ 1076 menu.push({
1007 label: intl.formatMessage(drawerLabel), 1077 label: intl.formatMessage(drawerLabel),
@@ -1013,14 +1083,17 @@ export default class FranzMenu {
1013 }); 1083 });
1014 1084
1015 if (!isFeatureEnabledByUser) { 1085 if (!isFeatureEnabledByUser) {
1016 menu.push({ 1086 menu.push(
1017 type: 'separator', 1087 {
1018 }, { 1088 type: 'separator',
1019 label: intl.formatMessage(menuItems.enableTodos),
1020 click: () => {
1021 todoActions.toggleTodosFeatureVisibility();
1022 }, 1089 },
1023 }); 1090 {
1091 label: intl.formatMessage(menuItems.enableTodos),
1092 click: () => {
1093 todoActions.toggleTodosFeatureVisibility();
1094 },
1095 },
1096 );
1024 } 1097 }
1025 1098
1026 return menu; 1099 return menu;
@@ -1029,28 +1102,31 @@ export default class FranzMenu {
1029 debugMenu() { 1102 debugMenu() {
1030 const { intl } = window.ferdi; 1103 const { intl } = window.ferdi;
1031 1104
1032 return [{ 1105 return [
1033 label: intl.formatMessage(menuItems.debugInfo), 1106 {
1034 click: () => { 1107 label: intl.formatMessage(menuItems.debugInfo),
1035 const { debugInfo } = this.stores.app; 1108 click: () => {
1109 const { debugInfo } = this.stores.app;
1036 1110
1037 clipboard.write({ 1111 clipboard.write({
1038 text: JSON.stringify(debugInfo), 1112 text: JSON.stringify(debugInfo),
1039 }); 1113 });
1040 1114
1041 this.actions.app.notify({ 1115 this.actions.app.notify({
1042 title: intl.formatMessage(menuItems.debugInfoCopiedHeadline), 1116 title: intl.formatMessage(menuItems.debugInfoCopiedHeadline),
1043 options: { 1117 options: {
1044 body: intl.formatMessage(menuItems.debugInfoCopiedBody), 1118 body: intl.formatMessage(menuItems.debugInfoCopiedBody),
1045 }, 1119 },
1046 }); 1120 });
1121 },
1047 }, 1122 },
1048 }, { 1123 {
1049 label: intl.formatMessage(menuItems.publishDebugInfo), 1124 label: intl.formatMessage(menuItems.publishDebugInfo),
1050 click: () => { 1125 click: () => {
1051 window.ferdi.features.publishDebugInfo.state.isModalVisible = true; 1126 window.ferdi.features.publishDebugInfo.state.isModalVisible = true;
1127 },
1052 }, 1128 },
1053 }]; 1129 ];
1054 } 1130 }
1055 1131
1056 _getServiceName(service) { 1132 _getServiceName(service) {
diff --git a/src/lib/Tray.js b/src/lib/Tray.js
index f5970f7e7..c897d597a 100644
--- a/src/lib/Tray.js
+++ b/src/lib/Tray.js
@@ -1,5 +1,5 @@
1import { 1import {
2 app, Menu, nativeImage, nativeTheme, systemPreferences, Tray, ipcMain, 2 app, Menu, nativeImage, nativeTheme, systemPreferences, Tray, ipcMain, dialog,
3} from 'electron'; 3} from 'electron';
4import { join } from 'path'; 4import { join } from 'path';
5import macosVersion from 'macos-version'; 5import macosVersion from 'macos-version';
@@ -43,9 +43,7 @@ export default class TrayIcon {
43 }, 43 },
44 { 44 {
45 label: 'Quit Ferdi', 45 label: 'Quit Ferdi',
46 click() { 46 click: this.quitApp,
47 app.quit();
48 },
49 }, 47 },
50 ]; 48 ];
51 49
@@ -178,4 +176,22 @@ export default class TrayIcon {
178 __dirname, '..', 'assets', 'images', type, platform, `${asset}.${FILE_EXTENSION}`, 176 __dirname, '..', 'assets', 'images', type, platform, `${asset}.${FILE_EXTENSION}`,
179 )); 177 ));
180 } 178 }
179
180 // TODO: Extract this into a reusable component and remove the duplications
181 quitApp = () => {
182 const yesButtonIndex = 0;
183 let selection = yesButtonIndex;
184 if (window.ferdi.stores.settings.app.confirmOnQuit) {
185 selection = dialog.showMessageBoxSync(app.mainWindow, {
186 // TODO: Externalize strings
187 type: 'question',
188 message: 'Quit',
189 detail: 'Do you really want to quit Ferdi?',
190 buttons: ['Yes', 'No'],
191 });
192 }
193 if (selection === yesButtonIndex) {
194 app.quit();
195 }
196 };
181} 197}