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.js598
1 files changed, 510 insertions, 88 deletions
diff --git a/src/lib/Menu.js b/src/lib/Menu.js
index 703060dc1..76b0f28d5 100644
--- a/src/lib/Menu.js
+++ b/src/lib/Menu.js
@@ -1,113 +1,477 @@
1import { remote, shell } from 'electron'; 1import { remote, shell } from 'electron';
2import { autorun, computed, observable, toJS } from 'mobx'; 2import { observable, autorun, computed } from 'mobx';
3import { defineMessages } from 'react-intl';
3 4
4import { isMac } from '../environment'; 5import { isMac, ctrlKey, cmdKey } from '../environment';
5 6
6const { app, Menu, dialog } = remote; 7const { app, Menu, dialog } = remote;
7 8
8const template = [ 9const menuItems = defineMessages({
10 edit: {
11 id: 'menu.edit',
12 defaultMessage: '!!!Edit',
13 },
14 undo: {
15 id: 'menu.edit.undo',
16 defaultMessage: '!!!Undo',
17 },
18 redo: {
19 id: 'menu.edit.redo',
20 defaultMessage: '!!!Redo',
21 },
22 cut: {
23 id: 'menu.edit.cut',
24 defaultMessage: '!!!Cut',
25 },
26 copy: {
27 id: 'menu.edit.copy',
28 defaultMessage: '!!!Copy',
29 },
30 paste: {
31 id: 'menu.edit.paste',
32 defaultMessage: '!!!Paste',
33 },
34 pasteAndMatchStyle: {
35 id: 'menu.edit.pasteAndMatchStyle',
36 defaultMessage: '!!!Paste And Match Style',
37 },
38 delete: {
39 id: 'menu.edit.delete',
40 defaultMessage: '!!!Delete',
41 },
42 selectAll: {
43 id: 'menu.edit.selectAll',
44 defaultMessage: '!!!Select All',
45 },
46 speech: {
47 id: 'menu.edit.speech',
48 defaultMessage: '!!!Speech',
49 },
50 startSpeaking: {
51 id: 'menu.edit.startSpeaking',
52 defaultMessage: '!!!Start Speaking',
53 },
54 stopSpeaking: {
55 id: 'menu.edit.stopSpeaking',
56 defaultMessage: '!!!Stop Speaking',
57 },
58 startDictation: {
59 id: 'menu.edit.startDictation',
60 defaultMessage: '!!!Start Dictation',
61 },
62 emojiSymbols: {
63 id: 'menu.edit.emojiSymbols',
64 defaultMessage: '!!!Emoji & Symbols',
65 },
66 resetZoom: {
67 id: 'menu.view.resetZoom',
68 defaultMessage: '!!!Actual Size',
69 },
70 zoomIn: {
71 id: 'menu.view.zoomIn',
72 defaultMessage: '!!!Zoom In',
73 },
74 zoomOut: {
75 id: 'menu.view.zoomOut',
76 defaultMessage: '!!!Zoom Out',
77 },
78 enterFullScreen: {
79 id: 'menu.view.enterFullScreen',
80 defaultMessage: '!!!Enter Full Screen',
81 },
82 exitFullScreen: {
83 id: 'menu.view.exitFullScreen',
84 defaultMessage: '!!!Exit Full Screen',
85 },
86 toggleFullScreen: {
87 id: 'menu.view.toggleFullScreen',
88 defaultMessage: '!!!Toggle Full Screen',
89 },
90 toggleDevTools: {
91 id: 'menu.view.toggleDevTools',
92 defaultMessage: '!!!Toggle Developer Tools',
93 },
94 toggleServiceDevTools: {
95 id: 'menu.view.toggleServiceDevTools',
96 defaultMessage: '!!!Toggle Service Developer Tools',
97 },
98 reloadService: {
99 id: 'menu.view.reloadService',
100 defaultMessage: '!!!Reload Service',
101 },
102 reloadFranz: {
103 id: 'menu.view.reloadFranz',
104 defaultMessage: '!!!Reload Franz',
105 },
106 minimize: {
107 id: 'menu.window.minimize',
108 defaultMessage: '!!!Minimize',
109 },
110 close: {
111 id: 'menu.window.close',
112 defaultMessage: '!!!Close',
113 },
114 learnMore: {
115 id: 'menu.help.learnMore',
116 defaultMessage: '!!!Learn More',
117 },
118 changelog: {
119 id: 'menu.help.changelog',
120 defaultMessage: '!!!Changelog',
121 },
122 support: {
123 id: 'menu.help.support',
124 defaultMessage: '!!!Support',
125 },
126 tos: {
127 id: 'menu.help.tos',
128 defaultMessage: '!!!Terms of Service',
129 },
130 privacy: {
131 id: 'menu.help.privacy',
132 defaultMessage: '!!!Privacy Statement',
133 },
134 file: {
135 id: 'menu.file',
136 defaultMessage: '!!!File',
137 },
138 view: {
139 id: 'menu.view',
140 defaultMessage: '!!!View',
141 },
142 services: {
143 id: 'menu.services',
144 defaultMessage: '!!!Services',
145 },
146 window: {
147 id: 'menu.window',
148 defaultMessage: '!!!Window',
149 },
150 help: {
151 id: 'menu.help',
152 defaultMessage: '!!!Help',
153 },
154 about: {
155 id: 'menu.app.about',
156 defaultMessage: '!!!About Franz',
157 },
158 settings: {
159 id: 'menu.app.settings',
160 defaultMessage: '!!!Settings',
161 },
162 hide: {
163 id: 'menu.app.hide',
164 defaultMessage: '!!!Hide',
165 },
166 hideOthers: {
167 id: 'menu.app.hideOthers',
168 defaultMessage: '!!!Hide Others',
169 },
170 unhide: {
171 id: 'menu.app.unhide',
172 defaultMessage: '!!!Unhide',
173 },
174 quit: {
175 id: 'menu.app.quit',
176 defaultMessage: '!!!Quit',
177 },
178 addNewService: {
179 id: 'menu.services.addNewService',
180 defaultMessage: '!!!Add New Service...',
181 },
182});
183
184function getActiveWebview() {
185 return window.franz.stores.services.active.webview;
186}
187
188const _templateFactory = intl => [
9 { 189 {
10 label: 'Edit', 190 label: intl.formatMessage(menuItems.edit),
11 submenu: [ 191 submenu: [
12 { 192 {
193 label: intl.formatMessage(menuItems.undo),
13 role: 'undo', 194 role: 'undo',
14 }, 195 },
15 { 196 {
197 label: intl.formatMessage(menuItems.redo),
16 role: 'redo', 198 role: 'redo',
17 }, 199 },
18 { 200 {
19 type: 'separator', 201 type: 'separator',
20 }, 202 },
21 { 203 {
22 role: 'cut', 204 label: intl.formatMessage(menuItems.cut),
205 accelerator: 'Cmd+X',
206 selector: 'cut:',
23 }, 207 },
24 { 208 {
25 label: 'Copy', 209 label: intl.formatMessage(menuItems.copy),
26 accelerator: 'Cmd+C', 210 accelerator: 'Cmd+C',
27 selector: 'copy:', 211 selector: 'copy:',
28 }, 212 },
29 { 213 {
30 label: 'Paste', 214 label: intl.formatMessage(menuItems.paste),
31 accelerator: 'Cmd+V', 215 accelerator: 'Cmd+V',
32 selector: 'paste:', 216 selector: 'paste:',
33 }, 217 },
34 { 218 {
35 role: 'pasteandmatchstyle', 219 label: intl.formatMessage(menuItems.pasteAndMatchStyle),
220 accelerator: 'Cmd+Shift+V',
221 selector: 'pasteAndMatchStyle:',
36 }, 222 },
37 { 223 {
224 label: intl.formatMessage(menuItems.delete),
38 role: 'delete', 225 role: 'delete',
39 }, 226 },
40 { 227 {
41 role: 'selectall', 228 label: intl.formatMessage(menuItems.selectAll),
229 accelerator: 'Cmd+A',
230 selector: 'selectAll:',
42 }, 231 },
43 ], 232 ],
44 }, 233 },
45 { 234 {
46 label: 'View', 235 label: intl.formatMessage(menuItems.view),
47 submenu: [ 236 submenu: [
48 { 237 {
49 type: 'separator', 238 type: 'separator',
50 }, 239 },
51 { 240 {
241 label: intl.formatMessage(menuItems.resetZoom),
52 role: 'resetzoom', 242 role: 'resetzoom',
53 }, 243 },
54 { 244 {
245 label: intl.formatMessage(menuItems.zoomIn),
246 // accelerator: 'Cmd+=',
55 role: 'zoomin', 247 role: 'zoomin',
56 accelerator: 'CommandOrControl+=',
57 }, 248 },
58 { 249 {
250 label: intl.formatMessage(menuItems.zoomOut),
59 role: 'zoomout', 251 role: 'zoomout',
60 }, 252 },
61 { 253 {
62 type: 'separator', 254 type: 'separator',
63 }, 255 },
64 { 256 {
257 label: app.mainWindow.isFullScreen() // label doesn't work, gets overridden by Electron
258 ? intl.formatMessage(menuItems.exitFullScreen)
259 : intl.formatMessage(menuItems.enterFullScreen),
65 role: 'togglefullscreen', 260 role: 'togglefullscreen',
66 }, 261 },
67 ], 262 ],
68 }, 263 },
69 { 264 {
70 label: 'Services', 265 label: intl.formatMessage(menuItems.services),
71 submenu: [], 266 submenu: [],
72 }, 267 },
73 { 268 {
269 label: intl.formatMessage(menuItems.window),
74 role: 'window', 270 role: 'window',
75 submenu: [ 271 submenu: [
76 { 272 {
273 label: intl.formatMessage(menuItems.minimize),
77 role: 'minimize', 274 role: 'minimize',
78 }, 275 },
79 { 276 {
277 label: intl.formatMessage(menuItems.close),
80 role: 'close', 278 role: 'close',
81 }, 279 },
82 ], 280 ],
83 }, 281 },
84 { 282 {
283 label: intl.formatMessage(menuItems.help),
85 role: 'help', 284 role: 'help',
86 submenu: [ 285 submenu: [
87 { 286 {
88 label: 'Learn More', 287 label: intl.formatMessage(menuItems.learnMore),
89 click() { shell.openExternal('http://meetfranz.com'); }, 288 click() { shell.openExternal('https://meetfranz.com'); },
90 }, 289 },
91 { 290 {
92 label: 'Changelog', 291 label: intl.formatMessage(menuItems.changelog),
93 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); }, 292 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); },
94 }, 293 },
95 { 294 {
96 type: 'separator', 295 type: 'separator',
97 }, 296 },
98 { 297 {
99 label: 'Support', 298 label: intl.formatMessage(menuItems.support),
100 click() { shell.openExternal('http://meetfranz.com/support'); }, 299 click() { shell.openExternal('https://meetfranz.com/support'); },
101 }, 300 },
102 { 301 {
103 type: 'separator', 302 type: 'separator',
104 }, 303 },
105 { 304 {
106 label: 'Terms of Service', 305 label: intl.formatMessage(menuItems.tos),
107 click() { shell.openExternal('https://meetfranz.com/terms'); }, 306 click() { shell.openExternal('https://meetfranz.com/terms'); },
108 }, 307 },
109 { 308 {
110 label: 'Privacy Statement', 309 label: intl.formatMessage(menuItems.privacy),
310 click() { shell.openExternal('https://meetfranz.com/privacy'); },
311 },
312 ],
313 },
314];
315
316const _titleBarTemplateFactory = intl => [
317 {
318 label: intl.formatMessage(menuItems.edit),
319 submenu: [
320 {
321 label: intl.formatMessage(menuItems.undo),
322 accelerator: `${ctrlKey}+Z`,
323 click() {
324 getActiveWebview().undo();
325 },
326 },
327 {
328 label: intl.formatMessage(menuItems.redo),
329 accelerator: `${ctrlKey}+Y`,
330 click() {
331 getActiveWebview().redo();
332 },
333 },
334 {
335 type: 'separator',
336 },
337 {
338 label: intl.formatMessage(menuItems.cut),
339 accelerator: `${ctrlKey}+X`,
340 click() {
341 getActiveWebview().cut();
342 },
343 },
344 {
345 label: intl.formatMessage(menuItems.copy),
346 accelerator: `${ctrlKey}+C`,
347 click() {
348 getActiveWebview().copy();
349 },
350 },
351 {
352 label: intl.formatMessage(menuItems.paste),
353 accelerator: `${ctrlKey}+V`,
354 click() {
355 getActiveWebview().paste();
356 },
357 },
358 {
359 label: intl.formatMessage(menuItems.pasteAndMatchStyle),
360 accelerator: `${ctrlKey}+Shift+V`,
361 click() {
362 getActiveWebview().pasteAndMatchStyle();
363 },
364 },
365 {
366 label: intl.formatMessage(menuItems.delete),
367 click() {
368 getActiveWebview().delete();
369 },
370 },
371 {
372 label: intl.formatMessage(menuItems.selectAll),
373 accelerator: `${ctrlKey}+A`,
374 click() {
375 getActiveWebview().selectAll();
376 },
377 },
378 ],
379 },
380 {
381 label: intl.formatMessage(menuItems.view),
382 submenu: [
383 {
384 type: 'separator',
385 },
386 {
387 label: intl.formatMessage(menuItems.resetZoom),
388 accelerator: `${ctrlKey}+0`,
389 click() {
390 getActiveWebview().setZoomLevel(0);
391 },
392 },
393 {
394 label: intl.formatMessage(menuItems.zoomIn),
395 accelerator: `${ctrlKey}+Plus`,
396 click() {
397 getActiveWebview().getZoomLevel((zoomLevel) => {
398 getActiveWebview().setZoomLevel(zoomLevel === 5 ? zoomLevel : zoomLevel + 1);
399 });
400 },
401 },
402 {
403 label: intl.formatMessage(menuItems.zoomOut),
404 accelerator: `${ctrlKey}+-`,
405 click() {
406 getActiveWebview().getZoomLevel((zoomLevel) => {
407 getActiveWebview().setZoomLevel(zoomLevel === -5 ? zoomLevel : zoomLevel - 1);
408 });
409 },
410 },
411 {
412 type: 'separator',
413 },
414 {
415 label: app.mainWindow.isFullScreen() // label doesn't work, gets overridden by Electron
416 ? intl.formatMessage(menuItems.exitFullScreen)
417 : intl.formatMessage(menuItems.enterFullScreen),
418 accelerator: 'F11',
419 click(menuItem, browserWindow) {
420 browserWindow.setFullScreen(!browserWindow.isFullScreen());
421 },
422 },
423 ],
424 },
425 {
426 label: intl.formatMessage(menuItems.services),
427 submenu: [],
428 },
429 {
430 label: intl.formatMessage(menuItems.window),
431 submenu: [
432 {
433 label: intl.formatMessage(menuItems.minimize),
434 accelerator: 'Ctrl+M',
435 click(menuItem, browserWindow) {
436 browserWindow.minimize();
437 },
438 },
439 {
440 label: intl.formatMessage(menuItems.close),
441 accelerator: 'Ctrl+W',
442 click(menuItem, browserWindow) {
443 browserWindow.close();
444 },
445 },
446 ],
447 },
448 {
449 label: '?',
450 submenu: [
451 {
452 label: intl.formatMessage(menuItems.learnMore),
453 click() { shell.openExternal('https://meetfranz.com'); },
454 },
455 {
456 label: intl.formatMessage(menuItems.changelog),
457 click() { shell.openExternal('https://github.com/meetfranz/franz/blob/master/CHANGELOG.md'); },
458 },
459 {
460 type: 'separator',
461 },
462 {
463 label: intl.formatMessage(menuItems.support),
464 click() { shell.openExternal('https://meetfranz.com/support'); },
465 },
466 {
467 type: 'separator',
468 },
469 {
470 label: intl.formatMessage(menuItems.tos),
471 click() { shell.openExternal('https://meetfranz.com/terms'); },
472 },
473 {
474 label: intl.formatMessage(menuItems.privacy),
111 click() { shell.openExternal('https://meetfranz.com/privacy'); }, 475 click() { shell.openExternal('https://meetfranz.com/privacy'); },
112 }, 476 },
113 ], 477 ],
@@ -115,7 +479,7 @@ const template = [
115]; 479];
116 480
117export default class FranzMenu { 481export default class FranzMenu {
118 @observable tpl = template; 482 @observable currentTemplate = [];
119 483
120 constructor(stores, actions) { 484 constructor(stores, actions) {
121 this.stores = stores; 485 this.stores = stores;
@@ -124,23 +488,45 @@ export default class FranzMenu {
124 autorun(this._build.bind(this)); 488 autorun(this._build.bind(this));
125 } 489 }
126 490
491 rebuild() {
492 this._build();
493 }
494
495 get template() {
496 return this.currentTemplate.toJS();
497 }
498
127 _build() { 499 _build() {
128 const tpl = toJS(this.tpl); 500 // console.log(window.franz);
501 const serviceTpl = Object.assign([], this.serviceTpl); // need to clone object so we don't modify computed (cached) object
502
503 if (window.franz === undefined) {
504 return;
505 }
506
507 const intl = window.franz.intl;
508 const tpl = isMac ? _templateFactory(intl) : _titleBarTemplateFactory(intl);
129 509
130 tpl[1].submenu.push({ 510 tpl[1].submenu.push({
131 role: 'toggledevtools', 511 type: 'separator',
132 }, { 512 }, {
133 label: 'Toggle Service Developer Tools', 513 label: intl.formatMessage(menuItems.toggleDevTools),
134 accelerator: 'CmdOrCtrl+Shift+Alt+i', 514 accelerator: `${cmdKey}+Alt+I`,
515 click: (menuItem, browserWindow) => {
516 browserWindow.webContents.toggleDevTools();
517 },
518 }, {
519 label: intl.formatMessage(menuItems.toggleServiceDevTools),
520 accelerator: `${cmdKey}+Shift+Alt+I`,
135 click: () => { 521 click: () => {
136 this.actions.service.openDevToolsForActiveService(); 522 this.actions.service.openDevToolsForActiveService();
137 }, 523 },
138 }); 524 });
139 525
140 tpl[1].submenu.unshift({ 526 tpl[1].submenu.unshift({
141 label: 'Reload Service', 527 label: intl.formatMessage(menuItems.reloadService),
142 id: 'reloadService', 528 id: 'reloadService', // TODO: needed?
143 accelerator: 'CmdOrCtrl+R', 529 accelerator: `${cmdKey}+R`,
144 click: () => { 530 click: () => {
145 if (this.stores.user.isLoggedIn 531 if (this.stores.user.isLoggedIn
146 && this.stores.services.enabled.length > 0) { 532 && this.stores.services.enabled.length > 0) {
@@ -150,93 +536,128 @@ export default class FranzMenu {
150 } 536 }
151 }, 537 },
152 }, { 538 }, {
153 label: 'Reload Franz', 539 label: intl.formatMessage(menuItems.reloadFranz),
154 accelerator: 'CmdOrCtrl+Shift+R', 540 accelerator: `${cmdKey}+Shift+R`,
155 click: () => { 541 click: () => {
156 window.location.reload(); 542 window.location.reload();
157 }, 543 },
158 }); 544 });
159 545
160 if (isMac) { 546 tpl.unshift({
161 tpl.unshift({ 547 label: isMac ? app.getName() : intl.formatMessage(menuItems.file),
162 label: app.getName(), 548 submenu: [
163 submenu: [ 549 {
164 { 550 label: intl.formatMessage(menuItems.about),
165 role: 'about', 551 role: 'about',
166 }, 552 },
167 { 553 {
168 type: 'separator', 554 type: 'separator',
169 }, 555 },
170 { 556 {
171 label: 'Settings', 557 label: intl.formatMessage(menuItems.settings),
172 accelerator: 'CmdOrCtrl+,', 558 accelerator: 'CmdOrCtrl+,',
173 click: () => { 559 click: () => {
174 this.actions.ui.openSettings({ path: 'app' }); 560 this.actions.ui.openSettings({ path: 'app' });
175 },
176 },
177 {
178 type: 'separator',
179 },
180 {
181 role: 'services',
182 submenu: [],
183 },
184 {
185 type: 'separator',
186 },
187 {
188 role: 'hide',
189 },
190 {
191 role: 'hideothers',
192 },
193 {
194 role: 'unhide',
195 },
196 {
197 type: 'separator',
198 }, 561 },
199 { 562 },
200 role: 'quit', 563 {
564 type: 'separator',
565 },
566 {
567 label: intl.formatMessage(menuItems.services),
568 role: 'services',
569 submenu: [],
570 },
571 {
572 type: 'separator',
573 },
574 {
575 label: intl.formatMessage(menuItems.hide),
576 role: 'hide',
577 },
578 {
579 label: intl.formatMessage(menuItems.hideOthers),
580 role: 'hideothers',
581 },
582 {
583 label: intl.formatMessage(menuItems.unhide),
584 role: 'unhide',
585 },
586 {
587 type: 'separator',
588 },
589 {
590 label: intl.formatMessage(menuItems.quit),
591 role: 'quit',
592 click: () => {
593 app.quit();
201 }, 594 },
202 ], 595 },
203 }); 596 ],
597 });
598
599 const about = {
600 label: intl.formatMessage(menuItems.about),
601 click: () => {
602 dialog.showMessageBox({
603 type: 'info',
604 title: 'Franz',
605 message: 'Franz',
606 detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`,
607 });
608 },
609 };
610
611 if (isMac) {
204 // Edit menu. 612 // Edit menu.
205 tpl[1].submenu.push( 613 tpl[1].submenu.push(
206 { 614 {
207 type: 'separator', 615 type: 'separator',
208 }, 616 },
209 { 617 {
210 label: 'Speech', 618 label: intl.formatMessage(menuItems.speech),
211 submenu: [ 619 submenu: [
212 { 620 {
621 label: intl.formatMessage(menuItems.startSpeaking),
213 role: 'startspeaking', 622 role: 'startspeaking',
214 }, 623 },
215 { 624 {
625 label: intl.formatMessage(menuItems.stopSpeaking),
216 role: 'stopspeaking', 626 role: 'stopspeaking',
217 }, 627 },
218 ], 628 ],
219 }, 629 },
220 ); 630 );
631
632 tpl[4].submenu.unshift(about, {
633 type: 'separator',
634 });
221 } else { 635 } else {
222 tpl[4].submenu.unshift({ 636 tpl[0].submenu = [
223 role: 'about', 637 {
224 click: () => { 638 label: intl.formatMessage(menuItems.settings),
225 dialog.showMessageBox({ 639 accelerator: 'Ctrl+P',
226 type: 'info', 640 click: () => {
227 title: 'Franz', 641 this.actions.ui.openSettings({ path: 'app' });
228 message: 'Franz', 642 },
229 detail: `Version: ${remote.app.getVersion()}\nRelease: ${process.versions.electron} / ${process.platform} / ${process.arch}`,
230 });
231 }, 643 },
232 }); 644 {
233 } 645 type: 'separator',
646 },
647 {
648 label: intl.formatMessage(menuItems.quit),
649 role: 'quit',
650 },
651 ];
234 652
235 const serviceTpl = this.serviceTpl; 653 tpl[5].submenu.push({
654 type: 'separator',
655 }, about);
656 }
236 657
237 serviceTpl.unshift({ 658 serviceTpl.unshift({
238 label: 'Add new Service', 659 label: intl.formatMessage(menuItems.addNewService),
239 accelerator: 'CmdOrCtrl+N', 660 accelerator: `${cmdKey}+N`,
240 click: () => { 661 click: () => {
241 this.actions.ui.openSettings({ path: 'recipes' }); 662 this.actions.ui.openSettings({ path: 'recipes' });
242 }, 663 },
@@ -245,9 +666,10 @@ export default class FranzMenu {
245 }); 666 });
246 667
247 if (serviceTpl.length > 0) { 668 if (serviceTpl.length > 0) {
248 tpl[isMac ? 3 : 2].submenu = toJS(this.serviceTpl); 669 tpl[3].submenu = serviceTpl;
249 } 670 }
250 671
672 this.currentTemplate = tpl;
251 const menu = Menu.buildFromTemplate(tpl); 673 const menu = Menu.buildFromTemplate(tpl);
252 Menu.setApplicationMenu(menu); 674 Menu.setApplicationMenu(menu);
253 } 675 }
@@ -258,7 +680,7 @@ export default class FranzMenu {
258 if (this.stores.user.isLoggedIn) { 680 if (this.stores.user.isLoggedIn) {
259 return services.map((service, i) => ({ 681 return services.map((service, i) => ({
260 label: this._getServiceName(service), 682 label: this._getServiceName(service),
261 accelerator: i <= 9 ? `CmdOrCtrl+${i + 1}` : null, 683 accelerator: i <= 9 ? `${cmdKey}+${i + 1}` : null,
262 type: 'radio', 684 type: 'radio',
263 checked: service.isActive, 685 checked: service.isActive,
264 click: () => { 686 click: () => {