diff --git a/src/electron/baseMenu.ts b/src/electron/baseMenu.ts new file mode 100644 index 0000000000..77a9aff3bd --- /dev/null +++ b/src/electron/baseMenu.ts @@ -0,0 +1,272 @@ +import { + app, + clipboard, + dialog, + Menu, + MenuItemConstructorOptions, +} from 'electron'; + +import { I18nService } from '../abstractions/i18n.service'; +import { WindowMain } from './window.main'; + +import { isMacAppStore } from './utils'; + +export class BaseMenu { + protected logOutMenuItemOptions: MenuItemConstructorOptions; + protected aboutMenuItemOptions: MenuItemConstructorOptions; + protected editMenuItemOptions: MenuItemConstructorOptions; + protected viewSubMenuItemOptions: MenuItemConstructorOptions[]; + protected windowMenuItemOptions: MenuItemConstructorOptions; + protected macAppMenuItemOptions: MenuItemConstructorOptions[]; + protected macWindowSubmenuOptions: MenuItemConstructorOptions[]; + + constructor(protected i18nService: I18nService, protected windowMain: WindowMain, + protected appName: string, private onLogOut: () => void) { } + + protected initProperties() { + this.logOutMenuItemOptions = { + label: this.i18nService.t('logOut'), + id: 'logOut', + click: () => { + const result = dialog.showMessageBox(this.windowMain.win, { + title: this.i18nService.t('logOut'), + message: this.i18nService.t('logOut'), + detail: this.i18nService.t('logOutConfirmation'), + buttons: [this.i18nService.t('logOut'), this.i18nService.t('cancel')], + cancelId: 1, + defaultId: 0, + noLink: true, + }); + if (result === 0) { + this.onLogOut(); + } + }, + }; + + this.aboutMenuItemOptions = { + label: this.i18nService.t('aboutBitwarden'), + click: () => { + const aboutInformation = this.i18nService.t('version', app.getVersion()) + + '\nShell ' + process.versions.electron + + '\nRenderer ' + process.versions.chrome + + '\nNode ' + process.versions.node + + '\nArchitecture ' + process.arch; + const result = dialog.showMessageBox(this.windowMain.win, { + title: this.appName, + message: this.appName, + detail: aboutInformation, + type: 'info', + noLink: true, + buttons: [this.i18nService.t('ok'), this.i18nService.t('copy')], + }); + if (result === 1) { + clipboard.writeText(aboutInformation); + } + }, + }; + + this.editMenuItemOptions = { + label: this.i18nService.t('edit'), + submenu: [ + { + label: this.i18nService.t('undo'), + role: 'undo', + }, + { + label: this.i18nService.t('redo'), + role: 'redo', + }, + { type: 'separator' }, + { + label: this.i18nService.t('cut'), + role: 'cut', + }, + { + label: this.i18nService.t('copy'), + role: 'copy', + }, + { + label: this.i18nService.t('paste'), + role: 'paste', + }, + { type: 'separator' }, + { + label: this.i18nService.t('selectAll'), + role: 'selectall', + }, + ], + }; + + this.viewSubMenuItemOptions = [ + { + label: this.i18nService.t('zoomIn'), + role: 'zoomin', accelerator: 'CmdOrCtrl+=', + }, + { + label: this.i18nService.t('zoomOut'), + role: 'zoomout', accelerator: 'CmdOrCtrl+-', + }, + { + label: this.i18nService.t('resetZoom'), + role: 'resetzoom', accelerator: 'CmdOrCtrl+0', + }, + { type: 'separator' }, + { + label: this.i18nService.t('toggleFullScreen'), + role: 'togglefullscreen', + }, + { type: 'separator' }, + { + label: this.i18nService.t('reload'), + role: 'forcereload', + }, + { + label: this.i18nService.t('toggleDevTools'), + role: 'toggledevtools', + accelerator: 'F12', + }, + ]; + + this.windowMenuItemOptions = { + label: this.i18nService.t('window'), + role: 'window', + submenu: [ + { + label: this.i18nService.t('minimize'), + role: 'minimize', + }, + { + label: this.i18nService.t('close'), + role: 'close', + }, + ], + }; + + if (process.platform === 'darwin') { + this.macAppMenuItemOptions = [ + { + label: this.i18nService.t('services'), + role: 'services', submenu: [], + }, + { type: 'separator' }, + { + label: this.i18nService.t('hideBitwarden'), + role: 'hide', + }, + { + label: this.i18nService.t('hideOthers'), + role: 'hideothers', + }, + { + label: this.i18nService.t('showAll'), + role: 'unhide', + }, + { type: 'separator' }, + { + label: this.i18nService.t('quitBitwarden'), + role: 'quit', + }, + ]; + + this.macWindowSubmenuOptions = [ + { + label: this.i18nService.t('close'), + role: isMacAppStore() ? 'quit' : 'close', + }, + { + label: this.i18nService.t('minimize'), + role: 'minimize', + }, + { + label: this.i18nService.t('zoom'), + role: 'zoom', + }, + { type: 'separator' }, + { + label: this.i18nService.t('bringAllToFront'), + role: 'front', + }, + ]; + } + } + + protected initContextMenu() { + if (this.windowMain.win == null) { + return; + } + + const selectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t('copy'), + role: 'copy', + }, + { type: 'separator' }, + { + label: this.i18nService.t('selectAll'), + role: 'selectall', + }, + ]); + + const inputMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t('undo'), + role: 'undo', + }, + { + label: this.i18nService.t('redo'), + role: 'redo', + }, + { type: 'separator' }, + { + label: this.i18nService.t('cut'), + role: 'cut', + enabled: false, + }, + { + label: this.i18nService.t('copy'), + role: 'copy', + enabled: false, + }, + { + label: this.i18nService.t('paste'), + role: 'paste', + }, + { type: 'separator' }, + { + label: this.i18nService.t('selectAll'), + role: 'selectall', + }, + ]); + + const inputSelectionMenu = Menu.buildFromTemplate([ + { + label: this.i18nService.t('cut'), + role: 'cut', + }, + { + label: this.i18nService.t('copy'), + role: 'copy', + }, + { + label: this.i18nService.t('paste'), + role: 'paste', + }, + { type: 'separator' }, + { + label: this.i18nService.t('selectAll'), + role: 'selectall', + }, + ]); + + this.windowMain.win.webContents.on('context-menu', (e, props) => { + const selected = props.selectionText && props.selectionText.trim() !== ''; + if (props.isEditable && selected) { + inputSelectionMenu.popup(this.windowMain.win); + } else if (props.isEditable) { + inputMenu.popup(this.windowMain.win); + } else if (selected) { + selectionMenu.popup(this.windowMain.win); + } + }); + } +}