mirror of
https://github.com/zulip/zulip-desktop.git
synced 2025-11-01 20:43:33 +00:00
602 lines
15 KiB
TypeScript
602 lines
15 KiB
TypeScript
'use strict';
|
|
import { app, shell, BrowserWindow, Menu, dialog } from 'electron';
|
|
import { appUpdater } from './autoupdater';
|
|
|
|
import AdmZip = require('adm-zip');
|
|
import fs = require('fs-extra');
|
|
import path = require('path');
|
|
import DNDUtil = require('../renderer/js/utils/dnd-util');
|
|
import Logger = require('../renderer/js/utils/logger-util');
|
|
import ConfigUtil = require('../renderer/js/utils/config-util');
|
|
import t = require('../renderer/js/utils/translation-util');
|
|
|
|
const appName = app.getName();
|
|
|
|
const logger = new Logger({
|
|
file: 'errors.log',
|
|
timestamp: true
|
|
});
|
|
|
|
class AppMenu {
|
|
getHistorySubmenu(enableMenu: boolean): Electron.MenuItemConstructorOptions[] {
|
|
return [{
|
|
label: t.__('Back'),
|
|
accelerator: process.platform === 'darwin' ? 'Command+Left' : 'Alt+Left',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('back');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Forward'),
|
|
accelerator: process.platform === 'darwin' ? 'Command+Right' : 'Alt+Right',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('forward');
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
getToolsSubmenu(): Electron.MenuItemConstructorOptions[] {
|
|
return [{
|
|
label: t.__(`Check for Updates`),
|
|
click() {
|
|
AppMenu.checkForUpdate();
|
|
}
|
|
},
|
|
{
|
|
label: t.__(`Release Notes`),
|
|
click() {
|
|
shell.openExternal(`https://github.com/zulip/zulip-desktop/releases/tag/v${app.getVersion()}`);
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: t.__('Factory Reset'),
|
|
accelerator: process.platform === 'darwin' ? 'Command+Shift+D' : 'Ctrl+Shift+D',
|
|
click() {
|
|
AppMenu.resetAppSettings();
|
|
}
|
|
},
|
|
{
|
|
label: t.__('Download App Logs'),
|
|
click() {
|
|
const zip = new AdmZip();
|
|
const date = new Date();
|
|
const dateString = date.toLocaleDateString().replace(/\//g, '-');
|
|
|
|
// Create a zip file of all the logs and config data
|
|
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/Logs`);
|
|
zip.addLocalFolder(`${app.getPath('appData')}/${appName}/config`);
|
|
|
|
// Put the log file in downloads folder
|
|
const logFilePath = `${app.getPath('downloads')}/Zulip-logs-${dateString}.zip`;
|
|
zip.writeZip(logFilePath);
|
|
|
|
// Open and select the log file
|
|
shell.showItemInFolder(logFilePath);
|
|
}
|
|
},
|
|
{
|
|
type: 'separator'
|
|
},
|
|
{
|
|
label: t.__('Toggle DevTools for Zulip App'),
|
|
accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
focusedWindow.webContents.openDevTools({mode: 'undocked'});
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: t.__('Toggle DevTools for Active Tab'),
|
|
accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('tab-devtools');
|
|
}
|
|
}
|
|
}];
|
|
}
|
|
|
|
getViewSubmenu(): Electron.MenuItemConstructorOptions[] {
|
|
return [{
|
|
label: t.__('Reload'),
|
|
accelerator: 'CommandOrControl+R',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('reload-current-viewer');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Hard Reload'),
|
|
accelerator: 'CommandOrControl+Shift+R',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('hard-reload');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Toggle Full Screen'),
|
|
role: 'togglefullscreen'
|
|
}, {
|
|
label: t.__('Zoom In'),
|
|
role: 'zoomin',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('zoomIn');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Zoom Out'),
|
|
accelerator: 'CommandOrControl+-',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('zoomOut');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Actual Size'),
|
|
accelerator: 'CommandOrControl+0',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('zoomActualSize');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Toggle Tray Icon'),
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
focusedWindow.webContents.send('toggletray');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Toggle Sidebar'),
|
|
accelerator: 'CommandOrControl+Shift+S',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
const newValue = !ConfigUtil.getConfigItem('showSidebar');
|
|
focusedWindow.webContents.send('toggle-sidebar', newValue);
|
|
ConfigUtil.setConfigItem('showSidebar', newValue);
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Auto hide Menu bar'),
|
|
checked: ConfigUtil.getConfigItem('autoHideMenubar', false),
|
|
visible: process.platform !== 'darwin',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
const newValue = !ConfigUtil.getConfigItem('autoHideMenubar');
|
|
focusedWindow.setAutoHideMenuBar(newValue);
|
|
focusedWindow.setMenuBarVisibility(!newValue);
|
|
focusedWindow.webContents.send('toggle-autohide-menubar', newValue);
|
|
ConfigUtil.setConfigItem('autoHideMenubar', newValue);
|
|
}
|
|
},
|
|
type: 'checkbox'
|
|
}];
|
|
}
|
|
|
|
getHelpSubmenu(): Electron.MenuItemConstructorOptions[] {
|
|
return [
|
|
{
|
|
label: `${appName + ' Desktop'} v${app.getVersion()}`,
|
|
enabled: false
|
|
},
|
|
{
|
|
label: t.__('About Zulip'),
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('open-about');
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: t.__(`Help Center`),
|
|
click(focusedWindow) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('open-help');
|
|
}
|
|
}
|
|
},
|
|
{
|
|
label: t.__('Report an Issue'),
|
|
click() {
|
|
// the goal is to notify the main.html BrowserWindow
|
|
// which may not be the focused window.
|
|
BrowserWindow.getAllWindows().forEach(window => {
|
|
window.webContents.send('open-feedback-modal');
|
|
});
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
getWindowSubmenu(tabs: any[], activeTabIndex: number, enableMenu: boolean): Electron.MenuItemConstructorOptions[] {
|
|
const initialSubmenu: any[] = [{
|
|
label: t.__('Minimize'),
|
|
role: 'minimize'
|
|
}, {
|
|
label: t.__('Close'),
|
|
role: 'close'
|
|
}];
|
|
|
|
if (tabs.length > 0) {
|
|
const ShortcutKey = process.platform === 'darwin' ? 'Cmd' : 'Ctrl';
|
|
initialSubmenu.push({
|
|
type: 'separator'
|
|
});
|
|
tabs.forEach(tab => {
|
|
// Do not add functional tab settings to list of windows in menu bar
|
|
if (tab.props.role === 'function' && tab.props.name === 'Settings') {
|
|
return;
|
|
}
|
|
|
|
initialSubmenu.push({
|
|
label: tab.props.name,
|
|
accelerator: tab.props.role === 'function' ? '' : `${ShortcutKey} + ${tab.props.index + 1}`,
|
|
checked: tab.props.index === activeTabIndex,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('switch-server-tab', tab.props.index);
|
|
}
|
|
},
|
|
type: 'checkbox'
|
|
});
|
|
});
|
|
initialSubmenu.push({
|
|
type: 'separator'
|
|
});
|
|
initialSubmenu.push({
|
|
label: t.__('Switch to Next Organization'),
|
|
accelerator: `Ctrl+Tab`,
|
|
enabled: tabs.length > 1,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('switch-server-tab', AppMenu.getNextServer(tabs, activeTabIndex));
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Switch to Previous Organization'),
|
|
accelerator: `Ctrl+Shift+Tab`,
|
|
enabled: tabs.length > 1,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('switch-server-tab', AppMenu.getPreviousServer(tabs, activeTabIndex));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
return initialSubmenu;
|
|
}
|
|
|
|
getDarwinTpl(props: any): Electron.MenuItemConstructorOptions[] {
|
|
const { tabs, activeTabIndex, enableMenu } = props;
|
|
|
|
return [{
|
|
label: `${app.getName()}`,
|
|
submenu: [{
|
|
label: t.__('Add Organization'),
|
|
accelerator: 'Cmd+Shift+N',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('new-server');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Toggle Do Not Disturb'),
|
|
accelerator: 'Cmd+Shift+M',
|
|
click() {
|
|
const dndUtil = DNDUtil.toggle();
|
|
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
|
}
|
|
}, {
|
|
label: t.__('Desktop Settings'),
|
|
accelerator: 'Cmd+,',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('open-settings');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Keyboard Shortcuts'),
|
|
accelerator: 'Cmd+Shift+K',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('shortcut');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Copy Zulip URL'),
|
|
accelerator: 'Cmd+Shift+C',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('copy-zulip-url');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Log Out of Organization'),
|
|
accelerator: 'Cmd+L',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('log-out');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Services'),
|
|
role: 'services',
|
|
submenu: []
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Hide'),
|
|
role: 'hide'
|
|
}, {
|
|
label: t.__('Hide Others'),
|
|
role: 'hideothers'
|
|
}, {
|
|
label: t.__('Unhide'),
|
|
role: 'unhide'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Minimize'),
|
|
role: 'minimize'
|
|
}, {
|
|
label: t.__('Close'),
|
|
role: 'close'
|
|
}, {
|
|
label: t.__('Quit'),
|
|
role: 'quit'
|
|
}]
|
|
}, {
|
|
label: t.__('Edit'),
|
|
submenu: [{
|
|
label: t.__('Undo'),
|
|
role: 'undo'
|
|
}, {
|
|
label: t.__('Redo'),
|
|
role: 'redo'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Cut'),
|
|
role: 'cut'
|
|
}, {
|
|
label: t.__('Copy'),
|
|
role: 'copy'
|
|
}, {
|
|
label: t.__('Paste'),
|
|
role: 'paste'
|
|
}, {
|
|
label: t.__('Paste and Match Style'),
|
|
role: 'pasteandmatchstyle'
|
|
}, {
|
|
label: t.__('Select All'),
|
|
role: 'selectall'
|
|
}]
|
|
}, {
|
|
label: t.__('View'),
|
|
submenu: this.getViewSubmenu()
|
|
}, {
|
|
label: t.__('History'),
|
|
submenu: this.getHistorySubmenu(enableMenu)
|
|
}, {
|
|
label: t.__('Window'),
|
|
submenu: this.getWindowSubmenu(tabs, activeTabIndex, enableMenu)
|
|
}, {
|
|
label: t.__('Tools'),
|
|
submenu: this.getToolsSubmenu()
|
|
}, {
|
|
label: t.__('Help'),
|
|
role: 'help',
|
|
submenu: this.getHelpSubmenu()
|
|
}];
|
|
}
|
|
|
|
getOtherTpl(props: any): Electron.MenuItemConstructorOptions[] {
|
|
const { tabs, activeTabIndex, enableMenu } = props;
|
|
return [{
|
|
label: t.__('File'),
|
|
submenu: [{
|
|
label: t.__('Add Organization'),
|
|
accelerator: 'Ctrl+Shift+N',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('new-server');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Toggle Do Not Disturb'),
|
|
accelerator: 'Ctrl+Shift+M',
|
|
click() {
|
|
const dndUtil = DNDUtil.toggle();
|
|
AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings);
|
|
}
|
|
}, {
|
|
label: t.__('Desktop Settings'),
|
|
accelerator: 'Ctrl+,',
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('open-settings');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Keyboard Shortcuts'),
|
|
accelerator: 'Ctrl+Shift+K',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('shortcut');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Copy Zulip URL'),
|
|
accelerator: 'Ctrl+Shift+C',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('copy-zulip-url');
|
|
}
|
|
}
|
|
}, {
|
|
label: t.__('Log Out of Organization'),
|
|
accelerator: 'Ctrl+L',
|
|
enabled: enableMenu,
|
|
click(_item: any, focusedWindow: any) {
|
|
if (focusedWindow) {
|
|
AppMenu.sendAction('log-out');
|
|
}
|
|
}
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Minimize'),
|
|
role: 'minimize'
|
|
}, {
|
|
label: t.__('Close'),
|
|
role: 'close'
|
|
}, {
|
|
label: t.__('Quit'),
|
|
role: 'quit',
|
|
accelerator: 'Ctrl+Q'
|
|
}]
|
|
}, {
|
|
label: t.__('Edit'),
|
|
submenu: [{
|
|
label: t.__('Undo'),
|
|
role: 'undo'
|
|
}, {
|
|
label: t.__('Redo'),
|
|
role: 'redo'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Cut'),
|
|
role: 'cut'
|
|
}, {
|
|
label: t.__('Copy'),
|
|
role: 'copy'
|
|
}, {
|
|
label: t.__('Paste'),
|
|
role: 'paste'
|
|
}, {
|
|
label: t.__('Paste and Match Style'),
|
|
role: 'pasteandmatchstyle'
|
|
}, {
|
|
type: 'separator'
|
|
}, {
|
|
label: t.__('Select All'),
|
|
role: 'selectall'
|
|
}]
|
|
}, {
|
|
label: t.__('View'),
|
|
submenu: this.getViewSubmenu()
|
|
}, {
|
|
label: t.__('History'),
|
|
submenu: this.getHistorySubmenu(enableMenu)
|
|
}, {
|
|
label: t.__('Window'),
|
|
submenu: this.getWindowSubmenu(tabs, activeTabIndex, enableMenu)
|
|
}, {
|
|
label: t.__('Tools'),
|
|
submenu: this.getToolsSubmenu()
|
|
}, {
|
|
label: t.__('Help'),
|
|
role: 'help',
|
|
submenu: this.getHelpSubmenu()
|
|
}];
|
|
}
|
|
|
|
static sendAction(action: any, ...params: any[]): void {
|
|
const win = BrowserWindow.getAllWindows()[0];
|
|
|
|
if (process.platform === 'darwin') {
|
|
win.restore();
|
|
}
|
|
|
|
win.webContents.send(action, ...params);
|
|
}
|
|
|
|
static checkForUpdate(): void {
|
|
appUpdater(true);
|
|
}
|
|
|
|
static getNextServer(tabs: any[], activeTabIndex: number): number {
|
|
do {
|
|
activeTabIndex = (activeTabIndex + 1) % tabs.length;
|
|
}
|
|
while (tabs[activeTabIndex].props.role !== 'server');
|
|
return activeTabIndex;
|
|
}
|
|
|
|
static getPreviousServer(tabs: any[], activeTabIndex: number): number {
|
|
do {
|
|
activeTabIndex = (activeTabIndex - 1 + tabs.length) % tabs.length;
|
|
}
|
|
while (tabs[activeTabIndex].props.role !== 'server');
|
|
return activeTabIndex;
|
|
}
|
|
|
|
static resetAppSettings(): void {
|
|
const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';
|
|
|
|
// We save App's settings/configurations in following files
|
|
const settingFiles = ['config/window-state.json', 'config/domain.json', 'config/settings.json', 'config/certificates.json'];
|
|
|
|
dialog.showMessageBox({
|
|
type: 'warning',
|
|
buttons: ['YES', 'NO'],
|
|
defaultId: 0,
|
|
message: 'Are you sure?',
|
|
detail: resetAppSettingsMessage
|
|
}, response => {
|
|
if (response === 0) {
|
|
settingFiles.forEach(settingFileName => {
|
|
const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
|
|
fs.access(getSettingFilesPath, (error: any) => {
|
|
if (error) {
|
|
logger.error('Error while resetting app settings.');
|
|
logger.error(error);
|
|
} else {
|
|
fs.unlink(getSettingFilesPath, () => {
|
|
AppMenu.sendAction('clear-app-data');
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
setMenu(props: any): void {
|
|
const tpl = process.platform === 'darwin' ? this.getDarwinTpl(props) : this.getOtherTpl(props);
|
|
const menu = Menu.buildFromTemplate(tpl);
|
|
Menu.setApplicationMenu(menu);
|
|
}
|
|
}
|
|
|
|
export = new AppMenu();
|