mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-10-31 20:13:43 +00:00 
			
		
		
		
	Compare commits
	
		
			124 Commits
		
	
	
		
			v2.3.4-bet
			...
			windows-re
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3768ec6055 | ||
|  | c029e70046 | ||
|  | 4773ab0d15 | ||
|  | a2e84595c9 | ||
|  | 9e33ebae62 | ||
|  | 9bdc5dd9e4 | ||
|  | ac89ac1cb4 | ||
|  | 31da1131a4 | ||
|  | 4980c71e5a | ||
|  | 3e73511357 | ||
|  | 5f23c8570b | ||
|  | 12f79e18a1 | ||
|  | ad7f760444 | ||
|  | 7314c1f1dd | ||
|  | 46c3d352a1 | ||
|  | cef8eadc8f | ||
|  | d3f742719c | ||
|  | 171d88755c | ||
|  | 09ac1bd338 | ||
|  | 48dd12b738 | ||
|  | 61bbd1cf22 | ||
|  | bf722da390 | ||
|  | c24f5b3e45 | ||
|  | 3d0f4d88af | ||
|  | a2f412c3de | ||
|  | 08559c2f4a | ||
|  | 1f4509a6d2 | ||
|  | 06c8b2e8a2 | ||
|  | 2aa15ee11a | ||
|  | 3c676672ec | ||
|  | c937317ecf | ||
|  | 44dceda50e | ||
|  | e1407cb6f7 | ||
|  | 6899a6bc20 | ||
|  | 925fec71d5 | ||
|  | f847c565f9 | ||
|  | c2a380d308 | ||
|  | ac6b206583 | ||
|  | 988405c665 | ||
|  | a209804692 | ||
|  | 95c4df1898 | ||
|  | 1048c91d9e | ||
|  | d723c5cd1c | ||
|  | dcc7cd118c | ||
|  | d2649dd5d7 | ||
|  | 25e6f6d482 | ||
|  | bd805b8324 | ||
|  | e1d98e0d8e | ||
|  | 89e5d2892b | ||
|  | ab28b56377 | ||
|  | 71aa7e28ba | ||
|  | ba2bfba459 | ||
|  | d181b0c2e5 | ||
|  | bda5c62928 | ||
|  | b2b4fd1003 | ||
|  | 19770f5fbf | ||
|  | 4592396c4b | ||
|  | 19d3c0f5b5 | ||
|  | af87789c7f | ||
|  | 36e710dbfe | ||
|  | a6e1c93d81 | ||
|  | 684d88dd7f | ||
|  | 45aedbd9db | ||
|  | 6f411a819c | ||
|  | 097738332b | ||
|  | b777f8a50f | ||
|  | 50f06239bb | ||
|  | 94cbc786d6 | ||
|  | 75abf38515 | ||
|  | c1af019182 | ||
|  | 1ea8aea22a | ||
|  | a7e418885a | ||
|  | 0ac823c85b | ||
|  | 1096188ddf | ||
|  | 8d0b68316c | ||
|  | c45ad7096f | ||
|  | 5ca7937e52 | ||
|  | 481b99a2ab | ||
|  | 7eb4adf722 | ||
|  | 4d21a8f8d8 | ||
|  | 94270af265 | ||
|  | 177bff6113 | ||
|  | b4fa4cacb6 | ||
|  | 06abf6716a | ||
|  | 0ad4ea4fdf | ||
|  | 2ece432d19 | ||
|  | 10cd7850ca | ||
|  | 86e3d06e52 | ||
|  | 6ebc9acbf4 | ||
|  | 278dc686e1 | ||
|  | cec98c030e | ||
|  | f10d6eb115 | ||
|  | 8c5e928e9c | ||
|  | 8d142c8c3f | ||
|  | a09769c560 | ||
|  | bb99015fd2 | ||
|  | 24f5c9b226 | ||
|  | dc6c4be5fe | ||
|  | 50647e330b | ||
|  | 73dc3db436 | ||
|  | 09cf21bf49 | ||
|  | c30d0cc77b | ||
|  | 872ad4d3e7 | ||
|  | 6fd9e1be8b | ||
|  | 76c7f24161 | ||
|  | f9c270492c | ||
|  | 371c580934 | ||
|  | 3eec4c2209 | ||
|  | debbfb6b7d | ||
|  | 8bd1492586 | ||
|  | 1115c6d5c3 | ||
|  | 9ba279213c | ||
|  | 89c35cb1d4 | ||
|  | 21d6eb52c5 | ||
|  | aa1538837b | ||
|  | ea103380b6 | ||
|  | 60d10d88d1 | ||
|  | 124a842bbd | ||
|  | 7130103999 | ||
|  | 26a144e1c2 | ||
|  | a5c1ae8726 | ||
|  | ed5096840d | ||
|  | 3f6d256910 | ||
|  | 28421992ba | 
							
								
								
									
										17
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -1 +1,16 @@ | ||||
| * text=auto | ||||
| * text=auto eol=lf | ||||
|  | ||||
| package-lock.json binary | ||||
| app/package-lock.json binary | ||||
| *.gif binary | ||||
| *.jpg binary | ||||
| *.jpeg binary | ||||
| *.eot binary | ||||
| *.woff binary | ||||
| *.woff2 binary | ||||
| *.svg binary | ||||
| *.ttf binary | ||||
| *.png binary | ||||
| *.otf binary | ||||
| *.tif binary | ||||
| *.ogg binary | ||||
|   | ||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -33,6 +33,6 @@ yarn-error.log* | ||||
| config.gypi | ||||
|  | ||||
| # Test generated files | ||||
| tests/package.json | ||||
| # tests/package.json | ||||
|  | ||||
| .python-version | ||||
|   | ||||
							
								
								
									
										25
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,31 +2,26 @@ | ||||
| [](https://travis-ci.org/zulip/zulip-electron) | ||||
| [](https://ci.appveyor.com/project/akashnimare/zulip-electron/branch/master) | ||||
| [](https://github.com/sindresorhus/xo) | ||||
| [](https://chat.zulip.org) | ||||
|  | ||||
| Desktop client for Zulip. Available for Mac, Linux and Windows. | ||||
| Desktop client for Zulip. Available for Mac, Linux, and Windows. | ||||
|  | ||||
| <img src="http://i.imgur.com/ChzTq4F.png"/> | ||||
|  | ||||
| # Download | ||||
| Please see [installation guide](https://zulipchat.com/help/desktop-app-install-guide). | ||||
| Please see the [installation guide](https://zulipchat.com/help/desktop-app-install-guide). | ||||
|  | ||||
| # Features | ||||
| * Sign in to multiple teams | ||||
| * Desktop Notifications with inline reply support | ||||
| * Multilanguage SpellChecker | ||||
| * OSX/Win/Linux installers | ||||
| * Automatic Updates (macOS/Windows/Linux) | ||||
| * Keyboard shortcuts | ||||
|  | ||||
| # Development | ||||
| Please see our [development guide](./development.md) to get started and run app locally. | ||||
| * Sign in to multiple organizations | ||||
| * Desktop notifications with inline reply | ||||
| * Tray/dock integration | ||||
| * Multi-language spell checker | ||||
| * Automatic updates | ||||
|  | ||||
| # Contribute | ||||
|  | ||||
| If you want to contribute please make sure to read [our documentation about contributing](./CONTRIBUTING.md) first. | ||||
|  | ||||
| * [Issue Tracker](https://github.com/zulip/zulip-electron/issues) | ||||
| * [Source Code](https://github.com/zulip/zulip-electron/) | ||||
| First, join us on the [Zulip community server](https://zulip.readthedocs.io/en/latest/contributing/chat-zulip-org.html)!  | ||||
| Also see our [contribution guidelines](./CONTRIBUTING.md) and our [development guide](./development.md). | ||||
|  | ||||
| # License | ||||
| Released under the [Apache-2.0](./LICENSE) license. | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| 'use strict'; | ||||
| const path = require('path'); | ||||
| const fs = require('fs'); | ||||
|  | ||||
| const electron = require('electron'); | ||||
| const windowStateKeeper = require('electron-window-state'); | ||||
| const isDev = require('electron-is-dev'); | ||||
| @@ -30,7 +32,9 @@ let isQuitting = false; | ||||
| // Load this url in main window | ||||
| const mainURL = 'file://' + path.join(__dirname, '../renderer', 'main.html'); | ||||
|  | ||||
| const isAlreadyRunning = app.makeSingleInstance(() => { | ||||
| const singleInstanceLock = app.requestSingleInstanceLock(); | ||||
| if (singleInstanceLock) { | ||||
| 	app.on('second-instance', () => { | ||||
| 		if (mainWindow) { | ||||
| 			if (mainWindow.isMinimized()) { | ||||
| 				mainWindow.restore(); | ||||
| @@ -39,9 +43,8 @@ const isAlreadyRunning = app.makeSingleInstance(() => { | ||||
| 			mainWindow.show(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| if (isAlreadyRunning) { | ||||
| 	return app.quit(); | ||||
| } else { | ||||
| 	app.quit(); | ||||
| } | ||||
|  | ||||
| const APP_ICON = path.join(__dirname, '../resources', 'Icon'); | ||||
| @@ -73,7 +76,8 @@ function createMainWindow() { | ||||
| 		minHeight: 400, | ||||
| 		webPreferences: { | ||||
| 			plugins: true, | ||||
| 			nodeIntegration: true | ||||
| 			nodeIntegration: true, | ||||
| 			partition: 'persist:webviewsession' | ||||
| 		}, | ||||
| 		show: false | ||||
| 	}); | ||||
| @@ -82,14 +86,6 @@ function createMainWindow() { | ||||
| 		win.webContents.send('focus'); | ||||
| 	}); | ||||
|  | ||||
| 	win.once('ready-to-show', () => { | ||||
| 		if (ConfigUtil.getConfigItem('startMinimized')) { | ||||
| 			win.minimize(); | ||||
| 		} else { | ||||
| 			win.show(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	win.loadURL(mainURL); | ||||
|  | ||||
| 	// Keep the app running in background on close event | ||||
| @@ -155,6 +151,13 @@ app.on('ready', () => { | ||||
| 	}); | ||||
| 	mainWindow = createMainWindow(); | ||||
|  | ||||
| 	// Auto-hide menu bar on Windows + Linux | ||||
| 	if (process.platform !== 'darwin') { | ||||
| 		const shouldHideMenu = ConfigUtil.getConfigItem('autoHideMenubar') || false; | ||||
| 		mainWindow.setAutoHideMenuBar(shouldHideMenu); | ||||
| 		mainWindow.setMenuBarVisibility(!shouldHideMenu); | ||||
| 	} | ||||
|  | ||||
| 	// Initialize sentry for main process | ||||
| 	sentryInit(); | ||||
|  | ||||
| @@ -168,14 +171,14 @@ app.on('ready', () => { | ||||
|  | ||||
| 	page.on('dom-ready', () => { | ||||
| 		if (ConfigUtil.getConfigItem('startMinimized')) { | ||||
| 			mainWindow.minimize(); | ||||
| 			mainWindow.hide(); | ||||
| 		} else { | ||||
| 			mainWindow.show(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	page.once('did-frame-finish-load', () => { | ||||
| 		// Initate auto-updates on MacOS and Windows | ||||
| 		// Initiate auto-updates on MacOS and Windows | ||||
| 		if (ConfigUtil.getConfigItem('autoUpdate')) { | ||||
| 			appUpdater(); | ||||
| 		} | ||||
| @@ -195,6 +198,32 @@ app.on('ready', () => { | ||||
| 		app.quit(); | ||||
| 	}); | ||||
|  | ||||
| 	// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream) | ||||
| 	// ipcMain.on('pdf-view', (event, url) => { | ||||
| 	// 	// Paddings for pdfWindow so that it fits into the main browserWindow | ||||
| 	// 	const paddingWidth = 55; | ||||
| 	// 	const paddingHeight = 22; | ||||
|  | ||||
| 	// 	// Get the config of main browserWindow | ||||
| 	// 	const mainWindowState = global.mainWindowState; | ||||
|  | ||||
| 	// 	// Window to view the pdf file | ||||
| 	// 	const pdfWindow = new electron.BrowserWindow({ | ||||
| 	// 		x: mainWindowState.x + paddingWidth, | ||||
| 	// 		y: mainWindowState.y + paddingHeight, | ||||
| 	// 		width: mainWindowState.width - paddingWidth, | ||||
| 	// 		height: mainWindowState.height - paddingHeight, | ||||
| 	// 		webPreferences: { | ||||
| 	// 			plugins: true, | ||||
| 	// 			partition: 'persist:webviewsession' | ||||
| 	// 		} | ||||
| 	// 	}); | ||||
| 	// 	pdfWindow.loadURL(url); | ||||
|  | ||||
| 	// 	// We don't want to have the menu bar in pdf window | ||||
| 	// 	pdfWindow.setMenu(null); | ||||
| 	// }); | ||||
|  | ||||
| 	// Reload full app not just webview, useful in debugging | ||||
| 	ipcMain.on('reload-full-app', () => { | ||||
| 		mainWindow.reload(); | ||||
| @@ -208,10 +237,10 @@ app.on('ready', () => { | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('toggle-app', () => { | ||||
| 		if (mainWindow.isVisible()) { | ||||
| 			mainWindow.hide(); | ||||
| 		} else { | ||||
| 		if (!mainWindow.isVisible() || mainWindow.isMinimized()) { | ||||
| 			mainWindow.show(); | ||||
| 		} else { | ||||
| 			mainWindow.hide(); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| @@ -219,6 +248,11 @@ app.on('ready', () => { | ||||
| 		BadgeSettings.updateBadge(badgeCount, mainWindow); | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('toggle-menubar', (event, showMenubar) => { | ||||
| 		mainWindow.setAutoHideMenuBar(showMenubar); | ||||
| 		mainWindow.setMenuBarVisibility(!showMenubar); | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('update-badge', (event, messageCount) => { | ||||
| 		badgeCount = messageCount; | ||||
| 		BadgeSettings.updateBadge(badgeCount, mainWindow); | ||||
| @@ -235,6 +269,10 @@ app.on('ready', () => { | ||||
|  | ||||
| 	ipcMain.on('update-menu', (event, props) => { | ||||
| 		appMenu.setMenu(props); | ||||
| 		const activeTab = props.tabs[props.activeTabIndex]; | ||||
| 		if (activeTab) { | ||||
| 			mainWindow.setTitle(`Zulip - ${activeTab.webview.props.name}`); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => { | ||||
| @@ -245,7 +283,26 @@ app.on('ready', () => { | ||||
| 		page.downloadURL(url); | ||||
| 		page.session.once('will-download', (event, item) => { | ||||
| 			const filePath = path.join(downloadPath, item.getFilename()); | ||||
| 			item.setSavePath(filePath); | ||||
|  | ||||
| 			const getTimeStamp = () => { | ||||
| 				const date = new Date(); | ||||
| 				return date.getTime(); | ||||
| 			}; | ||||
|  | ||||
| 			const formatFile = filePath => { | ||||
| 				const fileExtension = path.extname(filePath); | ||||
| 				const baseName = path.basename(filePath, fileExtension); | ||||
| 				return `${baseName}-${getTimeStamp()}${fileExtension}`; | ||||
| 			}; | ||||
|  | ||||
| 			// Update the name and path of the file if it already exists | ||||
|  | ||||
| 			const updatedFilePath = path.join(downloadPath, formatFile(filePath)); | ||||
|  | ||||
| 			const setFilePath = fs.existsSync(filePath) ? updatedFilePath : filePath; | ||||
|  | ||||
| 			item.setSavePath(setFilePath); | ||||
|  | ||||
| 			item.on('updated', (event, state) => { | ||||
| 				switch (state) { | ||||
| 					case 'interrupted': { | ||||
| @@ -258,7 +315,7 @@ app.on('ready', () => { | ||||
| 						if (item.isPaused()) { | ||||
| 							item.cancel(); | ||||
| 						} | ||||
| 						// This event can also be used to show progres in percentage in future. | ||||
| 						// This event can also be used to show progress in percentage in future. | ||||
| 						break; | ||||
| 					} | ||||
| 					default: { | ||||
| @@ -267,8 +324,9 @@ app.on('ready', () => { | ||||
| 				} | ||||
| 			}); | ||||
| 			item.once('done', (event, state) => { | ||||
| 				const getFileName = fs.existsSync(filePath) ? formatFile(filePath) : item.getFilename(); | ||||
| 				if (state === 'completed') { | ||||
| 					page.send('downloadFileCompleted', item.getSavePath(), item.getFilename()); | ||||
| 					page.send('downloadFileCompleted', item.getSavePath(), getFileName); | ||||
| 				} else { | ||||
| 					console.log('Download failed state: ', state); | ||||
| 					page.send('downloadFileFailed'); | ||||
| @@ -278,6 +336,14 @@ app.on('ready', () => { | ||||
| 			}); | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('realm-name-changed', (event, serverURL, realmName) => { | ||||
| 		page.send('update-realm-name', serverURL, realmName); | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('realm-icon-changed', (event, serverURL, iconURL) => { | ||||
| 		page.send('update-realm-icon', serverURL, iconURL); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| app.on('before-quit', () => { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const { Notification } = require('electron'); | ||||
| const request = require('request'); | ||||
| const semver = require('semver'); | ||||
| const ConfigUtil = require('../renderer/js/utils/config-util'); | ||||
| const ProxyUtil = require('../renderer/js/utils/proxy-util'); | ||||
| const LinuxUpdateUtil = require('../renderer/js/utils/linux-update-util'); | ||||
| const Logger = require('../renderer/js/utils/logger-util'); | ||||
|  | ||||
| @@ -15,10 +16,13 @@ const logger = new Logger({ | ||||
| function linuxUpdateNotification() { | ||||
| 	let	url = 'https://api.github.com/repos/zulip/zulip-electron/releases'; | ||||
| 	url = ConfigUtil.getConfigItem('betaUpdate') ? url : url + '/latest'; | ||||
| 	const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy'); | ||||
|  | ||||
| 	const options = { | ||||
| 		url, | ||||
| 		headers: {'User-Agent': 'request'} | ||||
| 		headers: {'User-Agent': 'request'}, | ||||
| 		proxy: proxyEnabled ? ProxyUtil.getProxy(url) : '', | ||||
| 		ecdhCurve: 'auto' | ||||
| 	}; | ||||
|  | ||||
| 	request(options, (error, response, body) => { | ||||
|   | ||||
							
								
								
									
										180
									
								
								app/main/menu.js
									
									
									
									
									
								
							
							
						
						
									
										180
									
								
								app/main/menu.js
									
									
									
									
									
								
							| @@ -39,6 +39,65 @@ class AppMenu { | ||||
| 		}]; | ||||
| 	} | ||||
|  | ||||
| 	getToolsSubmenu() { | ||||
| 		return [{ | ||||
| 			label: `Check for Updates`, | ||||
| 			click() { | ||||
| 				AppMenu.checkForUpdate(); | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			label: `Release Notes`, | ||||
| 			click() { | ||||
| 				shell.openExternal(`https://github.com/zulip/zulip-electron/releases/tag/v${app.getVersion()}`); | ||||
| 			} | ||||
| 		}, { | ||||
| 			type: 'separator' | ||||
| 		}, { | ||||
| 			label: 'Factory Reset', | ||||
| 			accelerator: process.platform === 'darwin' ? 'Command+Shift+D' : 'Ctrl+Shift+D', | ||||
| 			click() { | ||||
| 				AppMenu.resetAppSettings(); | ||||
| 			} | ||||
| 		}, { | ||||
| 			label: 'Download App Logs', | ||||
| 			click() { | ||||
| 				const zip = new AdmZip(); | ||||
| 				let date = new Date(); | ||||
| 				date = 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-${date}.zip`; | ||||
| 				zip.writeZip(logFilePath); | ||||
|  | ||||
| 				// Open and select the log file | ||||
| 				shell.showItemInFolder(logFilePath); | ||||
| 			} | ||||
| 		}, { | ||||
| 			type: 'separator' | ||||
| 		}, { | ||||
| 			label: 'Toggle DevTools for Zulip App', | ||||
| 			accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', | ||||
| 			click(item, focusedWindow) { | ||||
| 				if (focusedWindow) { | ||||
| 					focusedWindow.webContents.toggleDevTools(); | ||||
| 				} | ||||
| 			} | ||||
| 		}, { | ||||
| 			label: 'Toggle DevTools for Active Tab', | ||||
| 			accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U', | ||||
| 			click(item, focusedWindow) { | ||||
| 				if (focusedWindow) { | ||||
| 					AppMenu.sendAction('tab-devtools'); | ||||
| 				} | ||||
| 			} | ||||
| 		}]; | ||||
| 	} | ||||
|  | ||||
| 	getViewSubmenu() { | ||||
| 		return [{ | ||||
| 			label: 'Reload', | ||||
| @@ -103,62 +162,30 @@ class AppMenu { | ||||
| 					ConfigUtil.setConfigItem('showSidebar', newValue); | ||||
| 				} | ||||
| 			} | ||||
| 		}, { | ||||
| 			label: 'Toggle DevTools for Zulip App', | ||||
| 			accelerator: process.platform === 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', | ||||
| 			click(item, focusedWindow) { | ||||
| 				if (focusedWindow) { | ||||
| 					focusedWindow.webContents.toggleDevTools(); | ||||
| 				} | ||||
| 			} | ||||
| 		}, { | ||||
| 			label: 'Toggle DevTools for Active Tab', | ||||
| 			accelerator: process.platform === 'darwin' ? 'Alt+Command+U' : 'Ctrl+Shift+U', | ||||
| 			click(item, focusedWindow) { | ||||
| 				if (focusedWindow) { | ||||
| 					AppMenu.sendAction('tab-devtools'); | ||||
| 				} | ||||
| 			} | ||||
| 		}]; | ||||
| 	} | ||||
|  | ||||
| 	getHelpSubmenu() { | ||||
| 		return [ | ||||
| 			{ | ||||
| 				label: `${appName + ' Desktop-'} v${app.getVersion()}`, | ||||
| 				label: `${appName + ' Desktop'} v${app.getVersion()}`, | ||||
| 				enabled: false | ||||
| 			}, | ||||
| 			{ | ||||
| 				label: `What's New...`, | ||||
| 				click() { | ||||
| 					shell.openExternal(`https://github.com/zulip/zulip-electron/releases/tag/v${app.getVersion()}`); | ||||
| 				label: 'About Zulip', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('open-about'); | ||||
| 					} | ||||
| 				} | ||||
| 			}, | ||||
| 			{ | ||||
| 				label: `${appName} Help`, | ||||
| 				label: `Help Center`, | ||||
| 				click() { | ||||
| 					shell.openExternal('https://zulipchat.com/help/'); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Show App Logs', | ||||
| 				click() { | ||||
| 					const zip = new AdmZip(); | ||||
| 					let date = new Date(); | ||||
| 					date = 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-${date}.zip`; | ||||
| 					zip.writeZip(logFilePath); | ||||
|  | ||||
| 					// Open and select the log file | ||||
| 					shell.showItemInFolder(logFilePath); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Report an Issue...', | ||||
| 				label: 'Report an Issue', | ||||
| 				click() { | ||||
| 					// the goal is to notify the main.html BrowserWindow | ||||
| 					// which may not be the focused window. | ||||
| @@ -205,26 +232,12 @@ class AppMenu { | ||||
| 	} | ||||
|  | ||||
| 	getDarwinTpl(props) { | ||||
| 		const { tabs, activeTabIndex } = props; | ||||
| 		const { tabs, activeTabIndex, enableMenu } = props; | ||||
|  | ||||
| 		return [{ | ||||
| 			label: `${app.getName()}`, | ||||
| 			submenu: [{ | ||||
| 				label: 'About Zulip', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('open-about'); | ||||
| 					} | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: `Check for Update`, | ||||
| 				click() { | ||||
| 					AppMenu.checkForUpdate(); | ||||
| 				} | ||||
| 			}, { | ||||
| 				type: 'separator' | ||||
| 			}, { | ||||
| 				label: 'Desktop App Settings', | ||||
| 				label: 'Desktop Settings', | ||||
| 				accelerator: 'Cmd+,', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| @@ -234,6 +247,7 @@ class AppMenu { | ||||
| 			}, { | ||||
| 				label: 'Keyboard Shortcuts', | ||||
| 				accelerator: 'Cmd+Shift+K', | ||||
| 				enabled: enableMenu, | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('shortcut'); | ||||
| @@ -248,15 +262,10 @@ class AppMenu { | ||||
| 					const dndUtil = DNDUtil.toggle(); | ||||
| 					AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Reset App Settings', | ||||
| 				accelerator: 'Command+Shift+D', | ||||
| 				click() { | ||||
| 					AppMenu.resetAppSettings(); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Log Out', | ||||
| 				accelerator: 'Cmd+L', | ||||
| 				enabled: enableMenu, | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('log-out'); | ||||
| @@ -296,8 +305,6 @@ class AppMenu { | ||||
| 				role: 'paste' | ||||
| 			}, { | ||||
| 				role: 'pasteandmatchstyle' | ||||
| 			}, { | ||||
| 				role: 'delete' | ||||
| 			}, { | ||||
| 				role: 'selectall' | ||||
| 			}] | ||||
| @@ -310,6 +317,9 @@ class AppMenu { | ||||
| 		}, { | ||||
| 			label: 'Window', | ||||
| 			submenu: this.getWindowSubmenu(tabs, activeTabIndex) | ||||
| 		}, { | ||||
| 			label: 'Tools', | ||||
| 			submenu: this.getToolsSubmenu() | ||||
| 		}, { | ||||
| 			role: 'help', | ||||
| 			submenu: this.getHelpSubmenu() | ||||
| @@ -317,26 +327,12 @@ class AppMenu { | ||||
| 	} | ||||
|  | ||||
| 	getOtherTpl(props) { | ||||
| 		const { tabs, activeTabIndex } = props; | ||||
| 		const { tabs, activeTabIndex, enableMenu } = props; | ||||
|  | ||||
| 		return [{ | ||||
| 			label: 'File', | ||||
| 			label: '&File', | ||||
| 			submenu: [{ | ||||
| 				label: 'About Zulip', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('open-about'); | ||||
| 					} | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: `Check for Update`, | ||||
| 				click() { | ||||
| 					AppMenu.checkForUpdate(); | ||||
| 				} | ||||
| 			}, { | ||||
| 				type: 'separator' | ||||
| 			}, { | ||||
| 				label: 'Desktop App Settings', | ||||
| 				label: 'Desktop Settings', | ||||
| 				accelerator: 'Ctrl+,', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| @@ -348,6 +344,7 @@ class AppMenu { | ||||
| 			}, { | ||||
| 				label: 'Keyboard Shortcuts', | ||||
| 				accelerator: 'Ctrl+Shift+K', | ||||
| 				enabled: enableMenu, | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('shortcut'); | ||||
| @@ -362,15 +359,10 @@ class AppMenu { | ||||
| 					const dndUtil = DNDUtil.toggle(); | ||||
| 					AppMenu.sendAction('toggle-dnd', dndUtil.dnd, dndUtil.newSettings); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Reset App Settings', | ||||
| 				accelerator: 'Ctrl+Shift+D', | ||||
| 				click() { | ||||
| 					AppMenu.resetAppSettings(); | ||||
| 				} | ||||
| 			}, { | ||||
| 				label: 'Log Out', | ||||
| 				accelerator: 'Ctrl+L', | ||||
| 				enabled: enableMenu, | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('log-out'); | ||||
| @@ -383,7 +375,7 @@ class AppMenu { | ||||
| 				accelerator: 'Ctrl+Q' | ||||
| 			}] | ||||
| 		}, { | ||||
| 			label: 'Edit', | ||||
| 			label: '&Edit', | ||||
| 			submenu: [{ | ||||
| 				role: 'undo' | ||||
| 			}, { | ||||
| @@ -398,23 +390,25 @@ class AppMenu { | ||||
| 				role: 'paste' | ||||
| 			}, { | ||||
| 				role: 'pasteandmatchstyle' | ||||
| 			}, { | ||||
| 				role: 'delete' | ||||
| 			}, { | ||||
| 				type: 'separator' | ||||
| 			}, { | ||||
| 				role: 'selectall' | ||||
| 			}] | ||||
| 		}, { | ||||
| 			label: 'View', | ||||
| 			label: '&View', | ||||
| 			submenu: this.getViewSubmenu() | ||||
| 		}, { | ||||
| 			label: 'History', | ||||
| 			label: '&History', | ||||
| 			submenu: this.getHistorySubmenu() | ||||
| 		}, { | ||||
| 			label: 'Window', | ||||
| 			label: '&Window', | ||||
| 			submenu: this.getWindowSubmenu(tabs, activeTabIndex) | ||||
| 		}, { | ||||
| 			label: '&Tools', | ||||
| 			submenu: this.getToolsSubmenu() | ||||
| 		}, { | ||||
| 			label: '&Help', | ||||
| 			role: 'help', | ||||
| 			submenu: this.getHelpSubmenu() | ||||
| 		}]; | ||||
|   | ||||
							
								
								
									
										344
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										344
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,13 +1,13 @@ | ||||
| { | ||||
|   "name": "zulip", | ||||
|   "version": "2.3.4-beta", | ||||
|   "version": "2.5.0", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|     "@electron-elements/send-feedback": { | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-elements/send-feedback/-/send-feedback-1.0.7.tgz", | ||||
|       "integrity": "sha1-HmTsFMvNkmZAngeu1BDoUi45deA=", | ||||
|       "version": "1.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/@electron-elements/send-feedback/-/send-feedback-1.0.8.tgz", | ||||
|       "integrity": "sha512-spIZIYSe0Bp028sbZ7bcGOaUYhMyFArk+Tv7kQ4XCRNuVWRkw3SfbR8zIljZAdUavJp0wX6prSWrqLL4Dpd3Fw==", | ||||
|       "requires": { | ||||
|         "@electron-elements/utils": "1.0.3" | ||||
|       } | ||||
| @@ -37,62 +37,105 @@ | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/browser": { | ||||
|       "version": "0.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-0.5.4.tgz", | ||||
|       "integrity": "sha1-Yh/5chgrc7YoVlhLkvxl/elpBMw=", | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-4.3.4.tgz", | ||||
|       "integrity": "sha512-odRXxnhcSYzyR4YvTolNEyrz3fdDVw308l+9RBRJA9yOFVlezaz1mXH6Gv00F7cIj9yE/JtezDyhP339WsWy3w==", | ||||
|       "requires": { | ||||
|         "@sentry/core": "0.5.4", | ||||
|         "@sentry/shim": "0.5.4" | ||||
|         "@sentry/core": "4.3.4", | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "@sentry/utils": "4.3.4", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/core": { | ||||
|       "version": "0.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/core/-/core-0.5.4.tgz", | ||||
|       "integrity": "sha1-mwqEK0QhMbOAG65wviyk6cUQV04=", | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/core/-/core-4.3.4.tgz", | ||||
|       "integrity": "sha512-KwolQmAnXiFMeXBuxPUM8fW+2bOICdHfpjdf83qD7WSeuKqGvXhxXyApWNSLE+l2DPO6/8UKnIGmR8bEn0G7QA==", | ||||
|       "requires": { | ||||
|         "@sentry/shim": "0.5.4" | ||||
|         "@sentry/hub": "4.3.4", | ||||
|         "@sentry/minimal": "4.3.4", | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "@sentry/utils": "4.3.4", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/electron": { | ||||
|       "version": "0.5.5", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-0.5.5.tgz", | ||||
|       "integrity": "sha1-kInWNC22xr1sCSpTdHcIAx1ft8M=", | ||||
|       "version": "0.14.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/electron/-/electron-0.14.0.tgz", | ||||
|       "integrity": "sha512-f0Aj2gKcX9LampOEFER0m6cC4etT1B3bsuY7E1R3vzZXcL7y5TaH2Omgk5atzAX5ycBk8LHxhVqUtE6fZBWlFA==", | ||||
|       "requires": { | ||||
|         "@sentry/browser": "0.5.4", | ||||
|         "@sentry/core": "0.5.4", | ||||
|         "@sentry/node": "0.5.4", | ||||
|         "@sentry/shim": "0.5.4", | ||||
|         "@sentry/utils": "0.5.4", | ||||
|         "electron-fetch": "^1.1.0", | ||||
|         "form-data": "^2.3.2", | ||||
|         "util.promisify": "^1.0.0" | ||||
|         "@sentry/browser": "~4.3.4", | ||||
|         "@sentry/core": "~4.3.4", | ||||
|         "@sentry/minimal": "~4.3.4", | ||||
|         "@sentry/node": "~4.3.4", | ||||
|         "@sentry/types": "~4.3.4", | ||||
|         "@sentry/utils": "~4.3.4", | ||||
|         "electron-fetch": "1.1.0", | ||||
|         "form-data": "2.3.2", | ||||
|         "util.promisify": "1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/hub": { | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-4.3.4.tgz", | ||||
|       "integrity": "sha512-vaBGCnhinLB8N4aQLMiPPhnlTkIUwU/dxWzw/xsuKY3MYWrmfMUyWgMZF60Mz3B4F0lW1lsg5jnJz9xPnjZowg==", | ||||
|       "requires": { | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "@sentry/utils": "4.3.4", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/minimal": { | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-4.3.4.tgz", | ||||
|       "integrity": "sha512-EBmQgdAQgxkhWFsBO4TmsP3cg5yTzg48HmPe3Dyt7PtF5Umw3DFW6qboAqnN1+KF+pHNuxkqevvgBTFp7b4Saw==", | ||||
|       "requires": { | ||||
|         "@sentry/hub": "4.3.4", | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/node": { | ||||
|       "version": "0.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/node/-/node-0.5.4.tgz", | ||||
|       "integrity": "sha1-s+c0nRs2EjmVkDbqrhnOvzVkw9M=", | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/node/-/node-4.3.4.tgz", | ||||
|       "integrity": "sha512-4ro8eFnIuuYrF/TLw788xU10AYJl9HvOYC5G3jLrHXTjRNLp7jJo9lZrUexjCvwM2WF/srF3+Z0Pwr+HwdKVVw==", | ||||
|       "requires": { | ||||
|         "@sentry/core": "0.5.4", | ||||
|         "@sentry/shim": "0.5.4", | ||||
|         "raven": "^2.6.0" | ||||
|         "@sentry/core": "4.3.4", | ||||
|         "@sentry/hub": "4.3.4", | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "@sentry/utils": "4.3.4", | ||||
|         "@types/stack-trace": "0.0.29", | ||||
|         "cookie": "0.3.1", | ||||
|         "lsmod": "1.0.0", | ||||
|         "md5": "2.2.1", | ||||
|         "stack-trace": "0.0.10", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sentry/shim": { | ||||
|       "version": "0.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/shim/-/shim-0.5.4.tgz", | ||||
|       "integrity": "sha1-y4JrGjR2WuXhsh5h3y3vL42pHcE=" | ||||
|     "@sentry/types": { | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/types/-/types-4.3.4.tgz", | ||||
|       "integrity": "sha512-qsqrcyNilpbzYjqef+km0Grh5BckSFD4MUdJDNkUE5XU/ImniYddj18bMDlQxluJlTPDjUFQ37FXtEmxLeOwkQ==" | ||||
|     }, | ||||
|     "@sentry/utils": { | ||||
|       "version": "0.5.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-0.5.4.tgz", | ||||
|       "integrity": "sha1-jt54rOlgIW3WMv1WHIP1AGLlruE=" | ||||
|       "version": "4.3.4", | ||||
|       "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-4.3.4.tgz", | ||||
|       "integrity": "sha512-CMGMdIv5RHUCKRF4aWPZ/gFRTfBQpLVVJEGCeFGZLXHBdpgQac0lf3jlu8sND0KZ0S3C5x3tGS/eEqmOZRQ/pw==", | ||||
|       "requires": { | ||||
|         "@sentry/types": "4.3.4", | ||||
|         "tslib": "^1.9.3" | ||||
|       } | ||||
|     }, | ||||
|     "@sindresorhus/is": { | ||||
|       "version": "0.7.0", | ||||
|       "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", | ||||
|       "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" | ||||
|     }, | ||||
|     "@types/stack-trace": { | ||||
|       "version": "0.0.29", | ||||
|       "resolved": "https://registry.npmjs.org/@types/stack-trace/-/stack-trace-0.0.29.tgz", | ||||
|       "integrity": "sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g==" | ||||
|     }, | ||||
|     "adm-zip": { | ||||
|       "version": "0.4.11", | ||||
|       "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", | ||||
| @@ -126,7 +169,7 @@ | ||||
|     "argparse": { | ||||
|       "version": "1.0.10", | ||||
|       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", | ||||
|       "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", | ||||
|       "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", | ||||
|       "requires": { | ||||
|         "sprintf-js": "~1.0.2" | ||||
|       } | ||||
| @@ -194,16 +237,16 @@ | ||||
|       "optional": true | ||||
|     }, | ||||
|     "bluebird": { | ||||
|       "version": "3.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", | ||||
|       "integrity": "sha1-2VUfnemPH82h5oPRfukaBgLuLrk=" | ||||
|       "version": "3.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz", | ||||
|       "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==" | ||||
|     }, | ||||
|     "bluebird-lst": { | ||||
|       "version": "1.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.5.tgz", | ||||
|       "integrity": "sha1-vryDAmt+kqcocaPcWZ4hnL+wAqk=", | ||||
|       "version": "1.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.6.tgz", | ||||
|       "integrity": "sha512-CBWFoPuUPpcvMUxfyr8DKdI5d4kjxFl1h39+VbKxP3KJWJHEsLtuT4pPLkjpxCGU6Ask21tvbnftWXdqIxYldQ==", | ||||
|       "requires": { | ||||
|         "bluebird": "^3.5.1" | ||||
|         "bluebird": "^3.5.2" | ||||
|       } | ||||
|     }, | ||||
|     "boom": { | ||||
| @@ -224,28 +267,33 @@ | ||||
|       } | ||||
|     }, | ||||
|     "buffer-from": { | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", | ||||
|       "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==" | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", | ||||
|       "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" | ||||
|     }, | ||||
|     "builder-util-runtime": { | ||||
|       "version": "4.4.0", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-4.4.0.tgz", | ||||
|       "integrity": "sha512-tkTF1o7XAX79ZkMo8822ZdQMpEBGSgfJ9kEYgyTAja90BPu7HO8C02pb8iSlFXfmK0Q0UA6D8MmnSNNPi0JLeg==", | ||||
|       "version": "8.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-8.1.0.tgz", | ||||
|       "integrity": "sha512-s1mlJ28mv+56Iebh6c9aXjVe11O3Z0cDTwAGeB0PCcUzHA37fDxGgS8ZGoYNMZP+rBHj21d/od1wuYofTVLaQg==", | ||||
|       "requires": { | ||||
|         "bluebird-lst": "^1.0.5", | ||||
|         "debug": "^3.1.0", | ||||
|         "fs-extra-p": "^4.6.1", | ||||
|         "bluebird-lst": "^1.0.6", | ||||
|         "debug": "^4.1.0", | ||||
|         "fs-extra-p": "^7.0.0", | ||||
|         "sax": "^1.2.4" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "debug": { | ||||
|           "version": "3.1.0", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", | ||||
|           "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", | ||||
|           "version": "4.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", | ||||
|           "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", | ||||
|           "requires": { | ||||
|             "ms": "2.0.0" | ||||
|             "ms": "^2.1.1" | ||||
|           } | ||||
|         }, | ||||
|         "ms": { | ||||
|           "version": "2.1.1", | ||||
|           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", | ||||
|           "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -386,18 +434,12 @@ | ||||
|         "mimic-response": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "deep-equal": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", | ||||
|       "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" | ||||
|     }, | ||||
|     "define-properties": { | ||||
|       "version": "1.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", | ||||
|       "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", | ||||
|       "version": "1.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", | ||||
|       "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", | ||||
|       "requires": { | ||||
|         "foreach": "^2.0.5", | ||||
|         "object-keys": "^1.0.8" | ||||
|         "object-keys": "^1.0.12" | ||||
|       } | ||||
|     }, | ||||
|     "delayed-stream": { | ||||
| @@ -487,46 +529,35 @@ | ||||
|       } | ||||
|     }, | ||||
|     "electron-updater": { | ||||
|       "version": "2.23.3", | ||||
|       "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-2.23.3.tgz", | ||||
|       "integrity": "sha512-ZoQZpiEbhT3xA5Oxn7a5o4Z9adRaYs901pnTKBVBxPWmc0Qw5sZXAHkRjftlRmEn3RiEVkJtBPQSfx8kIkRcUA==", | ||||
|       "version": "4.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-4.0.6.tgz", | ||||
|       "integrity": "sha512-JPGLME6fxJcHG8hX7HWFl6Aew6iVm0DkcrENreKa5SUJCHG+uUaAhxDGDt+YGcNkyx1uJ6eBGMvFxDTLUv67pg==", | ||||
|       "requires": { | ||||
|         "bluebird-lst": "^1.0.5", | ||||
|         "builder-util-runtime": "~4.4.0", | ||||
|         "electron-is-dev": "^0.3.0", | ||||
|         "fs-extra-p": "^4.6.1", | ||||
|         "bluebird-lst": "^1.0.6", | ||||
|         "builder-util-runtime": "~8.1.0", | ||||
|         "fs-extra-p": "^7.0.0", | ||||
|         "js-yaml": "^3.12.0", | ||||
|         "lazy-val": "^1.0.3", | ||||
|         "lodash.isequal": "^4.5.0", | ||||
|         "semver": "^5.5.0", | ||||
|         "source-map-support": "^0.5.6" | ||||
|         "pako": "^1.0.7", | ||||
|         "semver": "^5.6.0", | ||||
|         "source-map-support": "^0.5.9" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "semver": { | ||||
|           "version": "5.5.0", | ||||
|           "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz", | ||||
|           "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==" | ||||
|           "version": "5.6.0", | ||||
|           "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", | ||||
|           "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "electron-window-state": { | ||||
|       "version": "4.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-4.1.1.tgz", | ||||
|       "integrity": "sha1-azT9wxs4UU3+yLfI97XUrdtnYy0=", | ||||
|       "version": "5.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz", | ||||
|       "integrity": "sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg==", | ||||
|       "requires": { | ||||
|         "deep-equal": "^1.0.1", | ||||
|         "jsonfile": "^2.2.3", | ||||
|         "jsonfile": "^4.0.0", | ||||
|         "mkdirp": "^0.5.1" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "jsonfile": { | ||||
|           "version": "2.4.0", | ||||
|           "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", | ||||
|           "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", | ||||
|           "requires": { | ||||
|             "graceful-fs": "^4.1.6" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "encoding": { | ||||
| @@ -550,13 +581,13 @@ | ||||
|       } | ||||
|     }, | ||||
|     "es-to-primitive": { | ||||
|       "version": "1.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", | ||||
|       "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", | ||||
|       "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", | ||||
|       "requires": { | ||||
|         "is-callable": "^1.1.1", | ||||
|         "is-callable": "^1.1.4", | ||||
|         "is-date-object": "^1.0.1", | ||||
|         "is-symbol": "^1.0.1" | ||||
|         "is-symbol": "^1.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "escape-html": { | ||||
| @@ -565,9 +596,9 @@ | ||||
|       "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" | ||||
|     }, | ||||
|     "esprima": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz", | ||||
|       "integrity": "sha1-RJnt3NERDgshi6zy+n9/WfVcqAQ=" | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", | ||||
|       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" | ||||
|     }, | ||||
|     "event-kit": { | ||||
|       "version": "2.4.0", | ||||
| @@ -600,11 +631,6 @@ | ||||
|       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", | ||||
|       "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" | ||||
|     }, | ||||
|     "foreach": { | ||||
|       "version": "2.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", | ||||
|       "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" | ||||
|     }, | ||||
|     "forever-agent": { | ||||
|       "version": "0.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", | ||||
| @@ -630,9 +656,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "fs-extra": { | ||||
|       "version": "6.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-6.0.1.tgz", | ||||
|       "integrity": "sha512-GnyIkKhhzXZUWFCaJzvyDLEEgDkPfb4/TPvJCJVuS8MWZgoSsErf++QpiAlDnKFcqhRlm+tIOcencCjyJE6ZCA==", | ||||
|       "version": "7.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", | ||||
|       "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", | ||||
|       "requires": { | ||||
|         "graceful-fs": "^4.1.2", | ||||
|         "jsonfile": "^4.0.0", | ||||
| @@ -640,12 +666,12 @@ | ||||
|       } | ||||
|     }, | ||||
|     "fs-extra-p": { | ||||
|       "version": "4.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-4.6.1.tgz", | ||||
|       "integrity": "sha512-IsTMbUS0svZKZTvqF4vDS9c/L7Mw9n8nZQWWeSzAGacOSe+8CzowhUN0tdZEZFIJNP5HC7L9j3MMikz/G4hDeQ==", | ||||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/fs-extra-p/-/fs-extra-p-7.0.0.tgz", | ||||
|       "integrity": "sha512-5tg5jBOd0xIXjwj4PDnafOXL5TyPVzjxLby4DPKev53wurEXp7IsojBaD4Lj5M5w7jxw0pbkEU0fFEPmcKoMnA==", | ||||
|       "requires": { | ||||
|         "bluebird-lst": "^1.0.5", | ||||
|         "fs-extra": "^6.0.1" | ||||
|         "bluebird-lst": "^1.0.6", | ||||
|         "fs-extra": "^7.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "fs.realpath": { | ||||
| @@ -733,6 +759,11 @@ | ||||
|       "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", | ||||
|       "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" | ||||
|     }, | ||||
|     "has-symbols": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", | ||||
|       "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" | ||||
|     }, | ||||
|     "has-to-string-tag-x": { | ||||
|       "version": "1.4.1", | ||||
|       "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", | ||||
| @@ -778,9 +809,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "iconv-lite": { | ||||
|       "version": "0.4.23", | ||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", | ||||
|       "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==", | ||||
|       "version": "0.4.24", | ||||
|       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", | ||||
|       "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", | ||||
|       "requires": { | ||||
|         "safer-buffer": ">= 2.1.2 < 3" | ||||
|       } | ||||
| @@ -891,9 +922,12 @@ | ||||
|       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" | ||||
|     }, | ||||
|     "is-symbol": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", | ||||
|       "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=" | ||||
|       "version": "1.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", | ||||
|       "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", | ||||
|       "requires": { | ||||
|         "has-symbols": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "is-typedarray": { | ||||
|       "version": "1.0.0", | ||||
| @@ -993,7 +1027,7 @@ | ||||
|     "lazy-val": { | ||||
|       "version": "1.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.3.tgz", | ||||
|       "integrity": "sha1-u5eyAO8AgB2UwxfincbtOeMcXtw=" | ||||
|       "integrity": "sha512-pjCf3BYk+uv3ZcPzEVM0BFvO9Uw58TmlrU0oG5tTrr9Kcid3+kdKxapH8CjdYmVa2nO5wOoZn2rdvZx2PKj/xg==" | ||||
|     }, | ||||
|     "lodash.assign": { | ||||
|       "version": "4.2.0", | ||||
| @@ -1024,6 +1058,11 @@ | ||||
|         "yallist": "^2.1.2" | ||||
|       } | ||||
|     }, | ||||
|     "lsmod": { | ||||
|       "version": "1.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/lsmod/-/lsmod-1.0.0.tgz", | ||||
|       "integrity": "sha1-mgD3bco26yP6BTUK/htYXUKZ5ks=" | ||||
|     }, | ||||
|     "md5": { | ||||
|       "version": "2.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", | ||||
| @@ -1084,30 +1123,24 @@ | ||||
|       "integrity": "sha1-ltDNYQ69WNS03pzAxoKM2pnHVI8=" | ||||
|     }, | ||||
|     "node-json-db": { | ||||
|       "version": "0.7.3", | ||||
|       "resolved": "https://registry.npmjs.org/node-json-db/-/node-json-db-0.7.3.tgz", | ||||
|       "integrity": "sha1-v2Mf9NTPQhHL3/5srmqq/m7lTN8=", | ||||
|       "version": "0.9.1", | ||||
|       "resolved": "https://registry.npmjs.org/node-json-db/-/node-json-db-0.9.1.tgz", | ||||
|       "integrity": "sha512-4BydUI7a10W8QBdHq/J3UBswU1i8WhCgTS4BZU0MjlUKrSU7cuUti71eojistgqe5hIrb4adj/wvAT5dw63NPg==", | ||||
|       "requires": { | ||||
|         "mkdirp": "0.5.x" | ||||
|       } | ||||
|     }, | ||||
|     "node-mac-notifier": { | ||||
|       "version": "0.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-0.1.0.tgz", | ||||
|       "integrity": "sha512-K07p0jW6BhE4Jxpem4pcOdOCDgF/vy3xIqZoZpqme5JAfeW0cEkQ5Z3AmsM7fhJB59kDeOiouyykwPnvLV+hAg==", | ||||
|       "version": "1.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-1.1.0.tgz", | ||||
|       "integrity": "sha512-Fwv09eKMGkM4xn+Eby5g7lvmYF+1KYWX4V5QNn27l4cVFVS7MNYOxhbvGGHT5VuTWbWDQIi/Lr7l6so5vvmqVw==", | ||||
|       "optional": true, | ||||
|       "requires": { | ||||
|         "bindings": "^1.2.1", | ||||
|         "event-target-shim": "^1.1.1", | ||||
|         "node-uuid": "^1.4.7" | ||||
|         "uuid": "^3.3.2" | ||||
|       } | ||||
|     }, | ||||
|     "node-uuid": { | ||||
|       "version": "1.4.8", | ||||
|       "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", | ||||
|       "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "normalize-url": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", | ||||
| @@ -1196,6 +1229,11 @@ | ||||
|         "p-finally": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "pako": { | ||||
|       "version": "1.0.7", | ||||
|       "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.7.tgz", | ||||
|       "integrity": "sha512-3HNK5tW4x8o5mO8RuHZp3Ydw9icZXx0RANAOMzlMzx7LVXhMJ4mo3MOBpzyd7r/+RUu8BmndP47LXT+vzjtWcQ==" | ||||
|     }, | ||||
|     "path-is-absolute": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", | ||||
| @@ -1309,18 +1347,6 @@ | ||||
|         "strict-uri-encode": "^1.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "raven": { | ||||
|       "version": "2.6.3", | ||||
|       "resolved": "https://registry.npmjs.org/raven/-/raven-2.6.3.tgz", | ||||
|       "integrity": "sha512-bKre7qlDW+y1+G2bUtCuntdDYc8o5v1T233t0vmJfbj8ttGOgLrGRlYB8saelVMW9KUAJNLrhFkAKOwFWFJonw==", | ||||
|       "requires": { | ||||
|         "cookie": "0.3.1", | ||||
|         "md5": "^2.2.1", | ||||
|         "stack-trace": "0.0.10", | ||||
|         "timed-out": "4.0.1", | ||||
|         "uuid": "3.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "readable-stream": { | ||||
|       "version": "2.3.6", | ||||
|       "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", | ||||
| @@ -1431,7 +1457,7 @@ | ||||
|     "sax": { | ||||
|       "version": "1.2.4", | ||||
|       "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", | ||||
|       "integrity": "sha1-KBYjTiN4vdxOU1T6tcqold9xANk=" | ||||
|       "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" | ||||
|     }, | ||||
|     "semver": { | ||||
|       "version": "5.4.1", | ||||
| @@ -1457,12 +1483,12 @@ | ||||
|     "source-map": { | ||||
|       "version": "0.6.1", | ||||
|       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | ||||
|       "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=" | ||||
|       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" | ||||
|     }, | ||||
|     "source-map-support": { | ||||
|       "version": "0.5.6", | ||||
|       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", | ||||
|       "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", | ||||
|       "version": "0.5.9", | ||||
|       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.9.tgz", | ||||
|       "integrity": "sha512-gR6Rw4MvUlYy83vP0vxoVNzM6t8MUXqNuRsuBmBHQDu1Fh6X015FrLdgoDKcNdkwGubozq0P4N0Q37UyFVr1EA==", | ||||
|       "requires": { | ||||
|         "buffer-from": "^1.0.0", | ||||
|         "source-map": "^0.6.0" | ||||
| @@ -1539,6 +1565,11 @@ | ||||
|         "punycode": "^1.4.1" | ||||
|       } | ||||
|     }, | ||||
|     "tslib": { | ||||
|       "version": "1.9.3", | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", | ||||
|       "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" | ||||
|     }, | ||||
|     "tunnel-agent": { | ||||
|       "version": "0.6.0", | ||||
|       "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", | ||||
| @@ -1601,9 +1632,10 @@ | ||||
|       } | ||||
|     }, | ||||
|     "uuid": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz", | ||||
|       "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg=" | ||||
|       "version": "3.3.2", | ||||
|       "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", | ||||
|       "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", | ||||
|       "optional": true | ||||
|     }, | ||||
|     "verror": { | ||||
|       "version": "1.10.0", | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "zulip", | ||||
|   "productName": "Zulip", | ||||
|   "version": "2.3.4-beta", | ||||
|   "version": "2.5.0", | ||||
|   "description": "Zulip Desktop App", | ||||
|   "license": "Apache-2.0", | ||||
|   "copyright": "Kandra Labs, Inc.", | ||||
| @@ -26,23 +26,23 @@ | ||||
|     "InstantMessaging" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "@electron-elements/send-feedback": "1.0.7", | ||||
|     "@sentry/electron": "0.5.5", | ||||
|     "@electron-elements/send-feedback": "1.0.8", | ||||
|     "@sentry/electron": "0.14.0", | ||||
|     "adm-zip": "0.4.11", | ||||
|     "auto-launch": "5.0.5", | ||||
|     "electron-is-dev": "0.3.0", | ||||
|     "electron-log": "2.2.14", | ||||
|     "electron-spellchecker": "1.1.2", | ||||
|     "electron-updater": "2.23.3", | ||||
|     "electron-window-state": "4.1.1", | ||||
|     "electron-updater": "4.0.6", | ||||
|     "electron-window-state": "5.0.3", | ||||
|     "escape-html": "1.0.3", | ||||
|     "is-online": "7.0.0", | ||||
|     "node-json-db": "0.7.3", | ||||
|     "node-json-db": "0.9.1", | ||||
|     "request": "2.85.0", | ||||
|     "semver": "5.4.1", | ||||
|     "wurl": "2.5.0" | ||||
|   }, | ||||
|   "optionalDependencies": { | ||||
|     "node-mac-notifier": "0.1.0" | ||||
|     "node-mac-notifier": "1.1.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,6 @@ | ||||
| 				Available under the | ||||
| 				<a onclick="linkInBrowser('license')">Apache 2.0 License</a> | ||||
| 			</p> | ||||
| 			<a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<script> | ||||
| @@ -38,9 +37,6 @@ | ||||
| 				case 'license': | ||||
| 					url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE"; | ||||
| 					break; | ||||
| 				default: | ||||
| 					url = 'https://github.com/zulip/zulip-electron/issues/new?body=' + | ||||
| 						'%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E'; | ||||
| 			} | ||||
| 			shell.openExternal(url); | ||||
| 		} | ||||
|   | ||||
| @@ -57,22 +57,6 @@ body { | ||||
| 	width: 100%; | ||||
| } | ||||
|  | ||||
| .maintenance-info .bug { | ||||
| 	display: inline-block; | ||||
| 	padding: 8px 15px; | ||||
| 	margin-top: 30px; | ||||
| 	text-decoration: none; | ||||
| 	background-color: #52c2af; | ||||
| 	color: #fff; | ||||
| 	border-radius: 4px; | ||||
|  | ||||
| 	transition: background-color 0.2s ease; | ||||
| } | ||||
|  | ||||
| .maintenance-info .bug:hover { | ||||
| 	background-color: #32a692; | ||||
| } | ||||
|  | ||||
| p.detail a { | ||||
| 	color: #355f4c; | ||||
| } | ||||
|   | ||||
| @@ -151,7 +151,7 @@ body { | ||||
| } | ||||
|  | ||||
| .tab:first-child { | ||||
|     margin-top: 8px; | ||||
|     margin-top: 9px; | ||||
| } | ||||
|  | ||||
| .tab { | ||||
| @@ -165,25 +165,33 @@ body { | ||||
| } | ||||
|  | ||||
| .tab .server-icons { | ||||
|     border-radius: 50%; | ||||
|     width: 30px; | ||||
|     padding: 3px; | ||||
|     height: 30px; | ||||
|     width: 35px; | ||||
|     vertical-align: top; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .tab .server-tab { | ||||
|     width: 100%; | ||||
|     height: 35px; | ||||
|     position: relative; | ||||
|     margin: 5px 0 2px 0; | ||||
|     margin-top: 5px; | ||||
|     z-index: 11; | ||||
|     line-height: 31px; | ||||
|     color: #eee; | ||||
|     text-align: center; | ||||
|     overflow: hidden; | ||||
|     opacity: 0.6; | ||||
|     padding: 2px 0; | ||||
|     padding: 6px 0; | ||||
| } | ||||
|  | ||||
| .server-tab .alt-icon { | ||||
|     font-family: Verdana; | ||||
|     font-weight: 600; | ||||
|     font-size: 22px; | ||||
|     border: 2px solid #222c31; | ||||
|     margin-left: 17%; | ||||
|     width: 35px; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .tab .server-tab:hover { | ||||
| @@ -358,7 +366,7 @@ webview.focus { | ||||
|     left: 56px; | ||||
|     padding: 10px 20px; | ||||
|     position: fixed; | ||||
|     margin-top: 8px; | ||||
|     margin-top: 11px; | ||||
|     z-index: 5000 !important; | ||||
|     color: #fff; | ||||
|     border-radius: 4px; | ||||
|   | ||||
| @@ -7,14 +7,16 @@ body { | ||||
|     font-family: menu, "Helvetica Neue", sans-serif; | ||||
|     -webkit-font-smoothing: antialiased; | ||||
|     font-size: 14px; | ||||
|     color: #333; | ||||
|     background: #efefef; | ||||
|     letter-spacing: -.08px; | ||||
|     line-height: 18px; | ||||
|     color: #8b8e8f; | ||||
| } | ||||
|  | ||||
| kbd { | ||||
|     display: inline-block; | ||||
|     border: 1px solid #ccc; | ||||
|     border-radius: 3px; | ||||
|     border-radius: 4px; | ||||
|     font-size: 15px; | ||||
|     font-family: Courier New, Courier, monospace; | ||||
|     font-weight: bold; | ||||
| @@ -109,7 +111,7 @@ td:nth-child(odd) { | ||||
| } | ||||
|  | ||||
| .nav { | ||||
|     padding: 5px 0; | ||||
|     padding: 7px 0; | ||||
|     color: #999; | ||||
|     cursor: pointer; | ||||
| } | ||||
| @@ -129,7 +131,6 @@ td:nth-child(odd) { | ||||
|     content: ''; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* We don't want to show this in nav item since we have the + button for adding an Organization */ | ||||
|  | ||||
| #nav-AddServer { | ||||
| @@ -187,7 +188,7 @@ td:nth-child(odd) { | ||||
| img.server-info-icon { | ||||
|     width: 36px; | ||||
|     height: 36px; | ||||
|     padding: 4px; | ||||
|     padding: 4px 4px 4px 10px; | ||||
|     cursor: pointer; | ||||
|     vertical-align: middle; | ||||
| } | ||||
| @@ -246,7 +247,7 @@ img.server-info-icon { | ||||
| .setting-input-value { | ||||
|     flex-grow: 1; | ||||
|     font-size: 14px; | ||||
|     border-radius: 3px; | ||||
|     border-radius: 4px; | ||||
|     padding: 13px; | ||||
|     border: #ededed 2px solid; | ||||
|     outline-width: 0; | ||||
| @@ -306,7 +307,7 @@ img.server-info-icon { | ||||
|     padding: 12px 30px; | ||||
|     margin: 10px 0 20px 0; | ||||
|     background: #fff; | ||||
|     width: 70%; | ||||
|     width: 80%; | ||||
|     transition: all 0.2s; | ||||
| } | ||||
|  | ||||
| @@ -323,20 +324,42 @@ img.server-info-icon { | ||||
|     color: #ef5350; | ||||
|     padding: 8px; | ||||
|     border: rgba(239, 83, 80, 0.5) solid 1px; | ||||
|     border-radius: 4px; | ||||
|     border-color: #fff; | ||||
| } | ||||
|  | ||||
| .red:hover { | ||||
|     color: #e63431; | ||||
|     border: rgba(239, 83, 80, 0.7) solid 1px; | ||||
|     ; | ||||
|     background-color: rgba(230, 52, 49, 0.5); | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| .blue { | ||||
|     color: #ffffff; | ||||
| .green { | ||||
|     color: #fff; | ||||
|     background: #4EBFAC; | ||||
|     padding: 3px; | ||||
|     padding-right: 10px; | ||||
|     padding-left: 10px; | ||||
|     border-radius: 4px; | ||||
|     display: inline-block; | ||||
|     border: none; | ||||
|     padding: 10px; | ||||
|     width: 100px; | ||||
|     cursor: pointer; | ||||
|     font-size: 1rem; | ||||
|     font-weight: bold; | ||||
|     transition: background-color 0.2s ease; | ||||
|     text-decoration: none; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .green:hover { | ||||
|     background-color: #3c9f8d; | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| .w-150 { | ||||
|     width: 150px; | ||||
| } | ||||
|  | ||||
| .w-250 { | ||||
|     width: 250px; | ||||
| } | ||||
|  | ||||
| .grey { | ||||
| @@ -358,38 +381,19 @@ img.server-info-icon { | ||||
| } | ||||
|  | ||||
| i.open-tab-button { | ||||
|     padding-left: 2px; | ||||
|     padding-left: 5px; | ||||
|     font-size: 19px; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .reset-data-button, | ||||
| .custom-css-button, | ||||
| .download-folder-button { | ||||
|     display: inline-block; | ||||
|     border: none; | ||||
|     padding: 10px; | ||||
|     width: 120px; | ||||
|     cursor: pointer; | ||||
|     font-size: 13px; | ||||
|     transition: background-color 0.2s ease; | ||||
|     text-decoration: none; | ||||
| } | ||||
|  | ||||
| .css-delete-action { | ||||
|     margin-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .reset-data-button:hover { | ||||
|     background-color: #3c9f8d; | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| .selected-css-path, | ||||
| .download-folder-path { | ||||
|     background: #eeeeee; | ||||
|     padding: 10px; | ||||
|     margin-top: 10px; | ||||
|     padding: 5px 10px; | ||||
|     margin-right: 10px; | ||||
|     display: flex; | ||||
|     width: 90%; | ||||
| @@ -401,7 +405,11 @@ i.open-tab-button { | ||||
| } | ||||
|  | ||||
| #server-info-container { | ||||
|     min-height: calc(100% - 260px); | ||||
|     /* min-height: calc(100% - 260px); */ | ||||
| } | ||||
|  | ||||
| #new-org-button { | ||||
|     margin: 30px 0px 30px 0px; | ||||
| } | ||||
|  | ||||
| #create-organization-container { | ||||
| @@ -451,9 +459,12 @@ input.toggle-round+label:after { | ||||
| } | ||||
|  | ||||
| input.toggle-round+label:before { | ||||
|     right: 2px; | ||||
|     background-color: #f1f1f1; | ||||
|     border-radius: 25px; | ||||
|     top: 0; | ||||
|     right: 0px; | ||||
|     left: 0px; | ||||
|     bottom: 0px; | ||||
| } | ||||
|  | ||||
| input.toggle-round+label:after { | ||||
| @@ -465,13 +476,16 @@ input.toggle-round+label:after { | ||||
|  | ||||
| input.toggle-round:checked+label:before { | ||||
|     background-color: #4EBFAC; | ||||
|     top: 0; | ||||
|     right: 0px; | ||||
|     left: 0px; | ||||
|     bottom: 0px; | ||||
| } | ||||
|  | ||||
| input.toggle-round:checked+label:after { | ||||
|     margin-left: 25px; | ||||
| } | ||||
|  | ||||
|  | ||||
| /*  Add new server modal */ | ||||
|  | ||||
| .add-server-modal { | ||||
| @@ -489,7 +503,6 @@ input.toggle-round:checked+label:after { | ||||
|     overflow: auto; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Modal Content */ | ||||
|  | ||||
| .modal-container { | ||||
| @@ -547,7 +560,7 @@ input.toggle-round:checked+label:after { | ||||
|     border: none; | ||||
|     width: 98%; | ||||
|     height: 46px; | ||||
|     border-radius: 3px; | ||||
|     border-radius: 4px; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| @@ -560,7 +573,7 @@ input.toggle-round:checked+label:after { | ||||
| } | ||||
|  | ||||
| .certificates-card { | ||||
|      width:70% | ||||
|     width: 80% | ||||
| } | ||||
|  | ||||
| .certificate-input { | ||||
| @@ -579,15 +592,16 @@ input.toggle-round:checked+label:after { | ||||
| } | ||||
|  | ||||
| #add-certificate-button { | ||||
|     width:20%; | ||||
|     margin-right:0px; | ||||
|     height: 35px; | ||||
|     margin: 10px 10px 10px 37px; | ||||
| } | ||||
|  | ||||
| .tip { | ||||
|     background-color: hsl(46,63%,95%); | ||||
|     border: 1px solid hsl(46,63%,84%); | ||||
|     border-radius: 4px; | ||||
|     background: none; | ||||
|     padding: 0; | ||||
| } | ||||
|  | ||||
| .tip:hover{ | ||||
|    box-shadow: none; | ||||
| } | ||||
|  | ||||
| .md-14 { | ||||
| @@ -655,12 +669,10 @@ input.toggle-round:checked+label:after { | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|     } | ||||
|  | ||||
|     .server-info-right { | ||||
|         flex-direction: column; | ||||
|         align-items: center; | ||||
|     } | ||||
|  | ||||
|     .action { | ||||
|         margin-top: 10px; | ||||
|     } | ||||
|   | ||||
| @@ -10,6 +10,8 @@ function handleExternalLink(event) { | ||||
| 	const { url } = event; | ||||
| 	const domainPrefix = DomainUtil.getDomain(this.props.index).url; | ||||
| 	const downloadPath = ConfigUtil.getConfigItem('downloadsPath', `${app.getPath('downloads')}`); | ||||
| 	const shouldShowInFolder = ConfigUtil.getConfigItem('showDownloadFolder', false); | ||||
|  | ||||
| 	// Whitelist URLs which are allowed to be opened in the app | ||||
| 	const { | ||||
| 		isInternalUrl: isWhiteListURL, | ||||
| @@ -19,15 +21,25 @@ function handleExternalLink(event) { | ||||
| 	if (isWhiteListURL) { | ||||
| 		event.preventDefault(); | ||||
|  | ||||
|     // download txt, pdf, mp3, mp4 etc.. by using downloadURL in the | ||||
| 		// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream) | ||||
| 		// Show pdf attachments in a new window | ||||
| 		// if (LinkUtil.isPDF(url) && isUploadsURL) { | ||||
| 		// 	ipcRenderer.send('pdf-view', url); | ||||
| 		// 	return; | ||||
| 		// } | ||||
|  | ||||
| 		// download txt, mp3, mp4 etc.. by using downloadURL in the | ||||
| 		// main process which allows the user to save the files to their desktop | ||||
| 		// and not trigger webview reload while image in webview will | ||||
| 		// do nothing and will not save it | ||||
|  | ||||
| 			// Code to show pdf in a new BrowserWindow (currently commented out due to bug-upstream) | ||||
| 		// if (!LinkUtil.isImage(url) && !LinkUtil.isPDF(url) && isUploadsURL) { | ||||
| 		if (!LinkUtil.isImage(url) && isUploadsURL) { | ||||
| 			ipcRenderer.send('downloadFile', url, downloadPath); | ||||
| 			ipcRenderer.once('downloadFileCompleted', (event, filePath, fileName) => { | ||||
| 				const downloadNotification = new Notification('Download Complete', { | ||||
| 					body: `Click to open ${fileName}`, | ||||
| 					body: shouldShowInFolder ? `Click to show ${fileName} in folder` : `Click to open ${fileName}`, | ||||
| 					silent: true // We'll play our own sound - ding.ogg | ||||
| 				}); | ||||
|  | ||||
| @@ -37,7 +49,13 @@ function handleExternalLink(event) { | ||||
| 				} | ||||
|  | ||||
| 				downloadNotification.onclick = () => { | ||||
| 					if (shouldShowInFolder) { | ||||
| 						// Reveal file in download folder | ||||
| 						shell.showItemInFolder(filePath); | ||||
| 					} else { | ||||
| 						// Open file in the default native app | ||||
| 						shell.openItem(filePath); | ||||
| 					} | ||||
| 				}; | ||||
| 				ipcRenderer.removeAllListeners('downloadFileFailed'); | ||||
| 			}); | ||||
|   | ||||
| @@ -8,7 +8,7 @@ const {ipcRenderer} = require('electron'); | ||||
| class ServerTab extends Tab { | ||||
| 	template() { | ||||
| 		return `<div class="tab" data-tab-id="${this.props.tabIndex}"> | ||||
| 					<div class="server-tooltip" style="display:none"></div> | ||||
| 					<div class="server-tooltip" style="display:none">${this.props.name}</div> | ||||
| 					<div class="server-tab-badge"></div> | ||||
| 					<div class="server-tab"> | ||||
| 					<img class="server-icons" src='${this.props.icon}'/> | ||||
|   | ||||
| @@ -33,6 +33,7 @@ class WebView extends BaseComponent { | ||||
| 					disablewebsecurity | ||||
| 					${this.props.preload ? 'preload="js/preload.js"' : ''} | ||||
| 					partition="persist:webviewsession" | ||||
| 					name="${this.props.name}" | ||||
| 					webpreferences="allowRunningInsecureContent, javascript=yes"> | ||||
| 				</webview>`; | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										40
									
								
								app/renderer/js/electron-bridge.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								app/renderer/js/electron-bridge.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| const events = require('events'); | ||||
| const { ipcRenderer } = require('electron'); | ||||
|  | ||||
| // we have and will have some non camelcase stuff | ||||
| // while working with zulip so just turning the rule off | ||||
| // for the whole file. | ||||
| /* eslint-disable camelcase */ | ||||
| class ElectronBridge extends events { | ||||
| 	send_event(...args) { | ||||
| 		this.emit(...args); | ||||
| 	} | ||||
|  | ||||
| 	on_event(...args) { | ||||
| 		this.on(...args); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| const electron_bridge = new ElectronBridge(); | ||||
|  | ||||
| electron_bridge.on('total_unread_count', (...args) => { | ||||
| 	ipcRenderer.send('unread-count', ...args); | ||||
| }); | ||||
|  | ||||
| electron_bridge.on('realm_name', realmName => { | ||||
| 	const serverURL = location.origin; | ||||
| 	ipcRenderer.send('realm-name-changed', serverURL, realmName); | ||||
| }); | ||||
|  | ||||
| electron_bridge.on('realm_icon_url', iconURL => { | ||||
| 	const serverURL = location.origin; | ||||
| 	iconURL = iconURL.includes('http') ? iconURL : `${serverURL}${iconURL}`; | ||||
| 	ipcRenderer.send('realm-icon-changed', serverURL, iconURL); | ||||
| }); | ||||
|  | ||||
| // this follows node's idiomatic implementation of event | ||||
| // emitters to make event handling more simpler instead of using | ||||
| // functions zulip side will emit event using ElectronBrigde.send_event | ||||
| // which is alias of .emit and on this side we can handle the data by adding | ||||
| // a listener for the event. | ||||
| module.exports = electron_bridge; | ||||
| @@ -3,7 +3,8 @@ | ||||
| const { ipcRenderer, remote } = require('electron'); | ||||
| const isDev = require('electron-is-dev'); | ||||
|  | ||||
| const { session, app } = remote; | ||||
| const { session, app, Menu, dialog } = remote; | ||||
| const escape = require('escape-html'); | ||||
|  | ||||
| require(__dirname + '/js/tray.js'); | ||||
| const DomainUtil = require(__dirname + '/js/utils/domain-util.js'); | ||||
| @@ -14,9 +15,9 @@ const ConfigUtil = require(__dirname + '/js/utils/config-util.js'); | ||||
| const DNDUtil = require(__dirname + '/js/utils/dnd-util.js'); | ||||
| const ReconnectUtil = require(__dirname + '/js/utils/reconnect-util.js'); | ||||
| const Logger = require(__dirname + '/js/utils/logger-util.js'); | ||||
| const { feedbackHolder } = require(__dirname + '/js/feedback.js'); | ||||
| const CommonUtil = require(__dirname + '/js/utils/common-util.js'); | ||||
|  | ||||
| const escape = require('escape-html'); | ||||
| const { feedbackHolder } = require(__dirname + '/js/feedback.js'); | ||||
|  | ||||
| const logger = new Logger({ | ||||
| 	file: 'errors.log', | ||||
| @@ -110,6 +111,7 @@ class ServerManagerView { | ||||
| 			showNotification: true, | ||||
| 			autoUpdate: true, | ||||
| 			betaUpdate: false, | ||||
| 			customCSS: false, | ||||
| 			silent: false, | ||||
| 			lastActiveTab: 0, | ||||
| 			dnd: false, | ||||
| @@ -117,7 +119,8 @@ class ServerManagerView { | ||||
| 				showNotification: true, | ||||
| 				silent: false | ||||
| 			}, | ||||
| 			downloadsPath: `${app.getPath('downloads')}` | ||||
| 			downloadsPath: `${app.getPath('downloads')}`, | ||||
| 			showDownloadFolder: false | ||||
| 		}; | ||||
|  | ||||
| 		// Platform specific settings | ||||
| @@ -133,6 +136,10 @@ class ServerManagerView { | ||||
| 			settingOptions.dockBouncing = true; | ||||
| 		} | ||||
|  | ||||
| 		if (process.platform !== 'darwin') { | ||||
| 			settingOptions.autoHideMenubar = false; | ||||
| 		} | ||||
|  | ||||
| 		for (const i in settingOptions) { | ||||
| 			if (ConfigUtil.getConfigItem(i) === null) { | ||||
| 				ConfigUtil.setConfigItem(i, settingOptions[i]); | ||||
| @@ -167,18 +174,19 @@ class ServerManagerView { | ||||
| 		this.tabs.push(new ServerTab({ | ||||
| 			role: 'server', | ||||
| 			icon: server.icon, | ||||
| 			name: server.alias, | ||||
| 			$root: this.$tabsContainer, | ||||
| 			onClick: this.activateLastTab.bind(this, index), | ||||
| 			index, | ||||
| 			tabIndex, | ||||
| 			onHover: this.onHover.bind(this, index, server.alias), | ||||
| 			onHover: this.onHover.bind(this, index), | ||||
| 			onHoverOut: this.onHoverOut.bind(this, index), | ||||
| 			webview: new WebView({ | ||||
| 				$root: this.$webviewsContainer, | ||||
| 				index, | ||||
| 				tabIndex, | ||||
| 				url: server.url, | ||||
| 				name: server.alias, | ||||
| 				name: CommonUtil.decodeString(server.alias), | ||||
| 				isActive: () => { | ||||
| 					return index === this.activeTabIndex; | ||||
| 				}, | ||||
| @@ -192,6 +200,24 @@ class ServerManagerView { | ||||
|  | ||||
| 	initActions() { | ||||
| 		this.initDNDButton(); | ||||
| 		this.initServerActions(); | ||||
| 		this.initLeftSidebarEvents(); | ||||
| 	} | ||||
|  | ||||
| 	initServerActions() { | ||||
| 		const $serverImgs = document.querySelectorAll('.server-icons'); | ||||
| 		$serverImgs.forEach(($serverImg, index) => { | ||||
| 			this.addContextMenu($serverImg, index); | ||||
| 			if ($serverImg.src.includes('img/icon.png')) { | ||||
| 				this.displayInitialCharLogo($serverImg, index); | ||||
| 			} | ||||
| 			$serverImg.addEventListener('error', () => { | ||||
| 				this.displayInitialCharLogo($serverImg, index); | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	initLeftSidebarEvents() { | ||||
| 		this.$dndButton.addEventListener('click', () => { | ||||
| 			const dndUtil = DNDUtil.toggle(); | ||||
| 			ipcRenderer.send('forward-message', 'toggle-dnd', dndUtil.dnd, dndUtil.newSettings); | ||||
| @@ -209,13 +235,6 @@ class ServerManagerView { | ||||
| 			this.tabs[this.activeTabIndex].webview.back(); | ||||
| 		}); | ||||
|  | ||||
| 		const $serverImgs = document.querySelectorAll('.server-icons'); | ||||
| 		$serverImgs.forEach($serverImg => { | ||||
| 			$serverImg.addEventListener('error', () => { | ||||
| 				$serverImg.src = 'img/icon.png'; | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip, true); | ||||
| 		this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip); | ||||
| 		this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip); | ||||
| @@ -234,6 +253,34 @@ class ServerManagerView { | ||||
| 		return currentIndex; | ||||
| 	} | ||||
|  | ||||
| 	displayInitialCharLogo($img, index) { | ||||
| 		/* | ||||
| 			index parameter needed because webview[data-tab-id] can increment | ||||
| 			beyond size of sidebar org array and throw error | ||||
| 		*/ | ||||
|  | ||||
| 		const $altIcon = document.createElement('div'); | ||||
| 		const $parent = $img.parentElement; | ||||
| 		const $container = $parent.parentElement; | ||||
| 		const webviewId = $container.dataset.tabId; | ||||
| 		const $webview = document.querySelector(`webview[data-tab-id="${webviewId}"]`); | ||||
| 		const realmName = $webview.getAttribute('name'); | ||||
|  | ||||
| 		if (realmName === null) { | ||||
| 			$img.src = '/img/icon.png'; | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		$altIcon.textContent = realmName.charAt(0) || 'Z'; | ||||
| 		$altIcon.classList.add('server-icon'); | ||||
| 		$altIcon.classList.add('alt-icon'); | ||||
|  | ||||
| 		$parent.removeChild($img); | ||||
| 		$parent.appendChild($altIcon); | ||||
|  | ||||
| 		this.addContextMenu($altIcon, index); | ||||
| 	} | ||||
|  | ||||
| 	sidebarHoverEvent(SidebarButton, SidebarTooltip, addServer = false) { | ||||
| 		SidebarButton.addEventListener('mouseover', () => { | ||||
| 			SidebarTooltip.removeAttribute('style'); | ||||
| @@ -251,8 +298,9 @@ class ServerManagerView { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	onHover(index, serverName) { | ||||
| 		this.$serverIconTooltip[index].innerHTML = escape(serverName); | ||||
| 	onHover(index) { | ||||
| 		// this.$serverIconTooltip[index].innerHTML already has realm name, so we are just | ||||
| 		// removing the style. | ||||
| 		this.$serverIconTooltip[index].removeAttribute('style'); | ||||
| 		// To handle position of servers' tooltip due to scrolling of list of organizations | ||||
| 		// This could not be handled using CSS, hence the top of the tooltip is made same | ||||
| @@ -274,6 +322,7 @@ class ServerManagerView { | ||||
| 		this.functionalTabs[tabProps.name] = this.tabs.length; | ||||
|  | ||||
| 		const tabIndex = this.getTabIndex(); | ||||
|  | ||||
| 		this.tabs.push(new FunctionalTab({ | ||||
| 			role: 'function', | ||||
| 			materialIcon: tabProps.materialIcon, | ||||
| @@ -298,9 +347,11 @@ class ServerManagerView { | ||||
| 				preload: false | ||||
| 			}) | ||||
| 		})); | ||||
|  | ||||
| 		// To show loading indicator the first time a functional tab is opened, indicator is | ||||
| 		// closed when the functional tab DOM is ready, handled in webview.js | ||||
| 		this.$webviewsContainer.classList.remove('loaded'); | ||||
|  | ||||
| 		this.activateTab(this.functionalTabs[tabProps.name]); | ||||
| 	} | ||||
|  | ||||
| @@ -337,6 +388,25 @@ class ServerManagerView { | ||||
| 		ConfigUtil.setConfigItem('lastActiveTab', index); | ||||
| 	} | ||||
|  | ||||
| 	// returns this.tabs in an way that does | ||||
| 	// not crash app when this.tabs is passed into | ||||
| 	// ipcRenderer. Something about webview, and props.webview | ||||
| 	// properties in ServerTab causes the app to crash. | ||||
| 	get tabsForIpc() { | ||||
| 		const tabs = []; | ||||
| 		this.tabs.forEach(tab => { | ||||
| 			const proto = Object.create(Object.getPrototypeOf(tab)); | ||||
| 			const tabClone = Object.assign(proto, tab); | ||||
|  | ||||
| 			tabClone.webview = { props: {} }; | ||||
| 			tabClone.webview.props.name = tab.webview.props.name; | ||||
| 			delete tabClone.props.webview; | ||||
| 			tabs.push(tabClone); | ||||
| 		}); | ||||
|  | ||||
| 		return tabs; | ||||
| 	} | ||||
|  | ||||
| 	activateTab(index, hideOldTab = true) { | ||||
| 		if (!this.tabs[index]) { | ||||
| 			return; | ||||
| @@ -363,38 +433,12 @@ class ServerManagerView { | ||||
| 		this.tabs[index].activate(); | ||||
|  | ||||
| 		ipcRenderer.send('update-menu', { | ||||
| 			tabs: this.tabs, | ||||
| 			activeTabIndex: this.activeTabIndex | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-sidebar', (event, state) => { | ||||
| 			const selector = 'webview:not([class*=disabled])'; | ||||
| 			const webview = document.querySelector(selector); | ||||
| 			const webContents = webview.getWebContents(); | ||||
| 			webContents.send('toggle-sidebar', state); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-silent', (event, state) => { | ||||
| 			const webviews = document.querySelectorAll('webview'); | ||||
| 			webviews.forEach(webview => { | ||||
| 				try { | ||||
| 					webview.setAudioMuted(state); | ||||
| 				} catch (err) { | ||||
| 					// webview is not ready yet | ||||
| 					webview.addEventListener('dom-ready', () => { | ||||
| 						webview.isAudioMuted(); | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-dnd', (event, state, newSettings) => { | ||||
| 			this.toggleDNDButton(state); | ||||
| 			ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent); | ||||
| 			const selector = 'webview:not([class*=disabled])'; | ||||
| 			const webview = document.querySelector(selector); | ||||
| 			const webContents = webview.getWebContents(); | ||||
| 			webContents.send('toggle-dnd', state, newSettings); | ||||
| 			// JSON stringify this.tabs to avoid a crash | ||||
| 			// util.inspect is being used to handle circular references | ||||
| 			tabs: this.tabsForIpc, | ||||
| 			activeTabIndex: this.activeTabIndex, | ||||
| 			// Following flag controls whether a menu item should be enabled or not | ||||
| 			enableMenu: this.tabs[index].props.role === 'server' | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| @@ -436,6 +480,7 @@ class ServerManagerView { | ||||
| 		// Destroy the current view and re-initiate it | ||||
| 		this.destroyView(); | ||||
| 		this.initTabs(); | ||||
| 		this.initServerActions(); | ||||
| 	} | ||||
|  | ||||
| 	// This will trigger when pressed CTRL/CMD + R [WIP] | ||||
| @@ -471,6 +516,32 @@ class ServerManagerView { | ||||
| 		this.$dndButton.querySelector('i').textContent = alert ? 'notifications_off' : 'notifications'; | ||||
| 	} | ||||
|  | ||||
| 	addContextMenu($serverImg, index) { | ||||
| 		$serverImg.addEventListener('contextmenu', e => { | ||||
| 			e.preventDefault(); | ||||
| 			const template = [ | ||||
| 				{ | ||||
| 					label: 'Disconnect organization', | ||||
| 					click: () => { | ||||
| 						dialog.showMessageBox({ | ||||
| 							type: 'warning', | ||||
| 							buttons: ['YES', 'NO'], | ||||
| 							defaultId: 0, | ||||
| 							message: 'Are you sure you want to disconnect this organization?' | ||||
| 						}, response => { | ||||
| 							if (response === 0) { | ||||
| 								DomainUtil.removeDomain(index); | ||||
| 								ipcRenderer.send('reload-full-app'); | ||||
| 							} | ||||
| 						}); | ||||
| 					} | ||||
| 				} | ||||
| 			]; | ||||
| 			const contextMenu = Menu.buildFromTemplate(template); | ||||
| 			contextMenu.popup({ window: remote.getCurrentWindow() }); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	registerIpcs() { | ||||
| 		const webviewListeners = { | ||||
| 			'webview-reload': 'reload', | ||||
| @@ -516,6 +587,10 @@ class ServerManagerView { | ||||
| 			this.activateLastTab(index); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('open-org-tab', () => { | ||||
| 			this.openSettings('AddServer'); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('reload-proxy', (event, showAlert) => { | ||||
| 			this.loadProxy().then(() => { | ||||
| 				if (showAlert) { | ||||
| @@ -526,7 +601,74 @@ class ServerManagerView { | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-sidebar', (event, show) => { | ||||
| 			// Toggle the left sidebar | ||||
| 			this.toggleSidebar(show); | ||||
|  | ||||
| 			// Toggle sidebar switch in the general settings | ||||
| 			const selector = 'webview:not([class*=disabled])'; | ||||
| 			const webview = document.querySelector(selector); | ||||
| 			const webContents = webview.getWebContents(); | ||||
| 			webContents.send('toggle-sidebar-setting', show); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-silent', (event, state) => { | ||||
| 			const webviews = document.querySelectorAll('webview'); | ||||
| 			webviews.forEach(webview => { | ||||
| 				try { | ||||
| 					webview.setAudioMuted(state); | ||||
| 				} catch (err) { | ||||
| 					// webview is not ready yet | ||||
| 					webview.addEventListener('dom-ready', () => { | ||||
| 						webview.setAudioMuted(state); | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-dnd', (event, state, newSettings) => { | ||||
| 			this.toggleDNDButton(state); | ||||
| 			ipcRenderer.send('forward-message', 'toggle-silent', newSettings.silent); | ||||
| 			const selector = 'webview:not([class*=disabled])'; | ||||
| 			const webview = document.querySelector(selector); | ||||
| 			const webContents = webview.getWebContents(); | ||||
| 			webContents.send('toggle-dnd', state, newSettings); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('update-realm-name', (event, serverURL, realmName) => { | ||||
| 			DomainUtil.getDomains().forEach((domain, index) => { | ||||
| 				if (domain.url.includes(serverURL)) { | ||||
| 					const serverTooltipSelector = `.tab .server-tooltip`; | ||||
| 					const serverTooltips = document.querySelectorAll(serverTooltipSelector); | ||||
| 					serverTooltips[index].innerHTML = escape(realmName); | ||||
| 					this.tabs[index].props.name = escape(realmName); | ||||
| 					this.tabs[index].webview.props.name = realmName; | ||||
|  | ||||
| 					domain.alias = escape(realmName); | ||||
| 					DomainUtil.db.push(`/domains[${index}]`, domain, true); | ||||
| 					DomainUtil.reloadDB(); | ||||
| 					// Update the realm name also on the Window menu | ||||
| 					ipcRenderer.send('update-menu', { | ||||
| 						tabs: this.tabsForIpc, | ||||
| 						activeTabIndex: this.activeTabIndex | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('update-realm-icon', (event, serverURL, iconURL) => { | ||||
| 			DomainUtil.getDomains().forEach((domain, index) => { | ||||
| 				if (domain.url.includes(serverURL)) { | ||||
| 					DomainUtil.saveServerIcon(iconURL).then(localIconUrl => { | ||||
| 						const serverImgsSelector = `.tab .server-icons`; | ||||
| 						const serverImgs = document.querySelectorAll(serverImgsSelector); | ||||
| 						serverImgs[index].src = localIconUrl; | ||||
|  | ||||
| 						domain.icon = localIconUrl; | ||||
| 						DomainUtil.db.push(`/domains[${index}]`, domain, true); | ||||
| 						DomainUtil.reloadDB(); | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('enter-fullscreen', () => { | ||||
|   | ||||
| @@ -141,7 +141,8 @@ function parseReply(reply) { | ||||
|  | ||||
| function setupReply(id) { | ||||
| 	const { narrow } = window; | ||||
| 	narrow.by_subject(id, { trigger: 'notification' }); | ||||
| 	const narrowByTopic = narrow.by_topic || narrow.by_subject; | ||||
| 	narrowByTopic(id, { trigger: 'notification' }); | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const { | ||||
|   remote: { app } | ||||
| } = require('electron'); | ||||
|  | ||||
| const params = require('../utils/params-util.js'); | ||||
| const DefaultNotification = require('./default-notification'); | ||||
| const { appId, loadBots } = require('./helpers'); | ||||
|  | ||||
| @@ -19,9 +20,8 @@ if (process.platform === 'darwin') { | ||||
| } | ||||
|  | ||||
| window.addEventListener('load', () => { | ||||
| 	// Call this function only when user is logged in | ||||
| 	// eslint-disable-next-line no-undef, camelcase | ||||
| 	if (page_params.realm_uri) { | ||||
| 	if (params.isPageParams() && page_params.realm_uri) { | ||||
| 		loadBots(); | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| 'use-strict'; | ||||
|  | ||||
| const { dialog } = require('electron').remote; | ||||
| const path = require('path'); | ||||
|  | ||||
| const BaseComponent = require(__dirname + '/../../components/base.js'); | ||||
| const CertificateUtil = require(__dirname + '/../../utils/certificate-util.js'); | ||||
| @@ -15,14 +16,14 @@ class AddCertificate extends BaseComponent { | ||||
|  | ||||
| 	template() { | ||||
| 		return ` | ||||
| 			<div class="settings-card server-center certificates-card"> | ||||
| 			<div class="settings-card certificates-card"> | ||||
| 				<div class="certificate-input"> | ||||
| 					<div>Organization URL :</div>  | ||||
| 					<div>Organization URL</div> | ||||
| 					<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or zulip.your-organization.com"/> | ||||
| 				</div> | ||||
| 				<div class="certificate-input"> | ||||
| 					<div>Custom CA's certificate file :</div>  | ||||
| 					<button id="add-certificate-button">Add</button> | ||||
| 					<div>Certificate file</div> | ||||
| 					<button class="green" id="add-certificate-button">Upload</button> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		`; | ||||
| @@ -41,7 +42,7 @@ class AddCertificate extends BaseComponent { | ||||
| 		const serverUrl = this.serverUrl.value; | ||||
| 		if (certificate !== '' && serverUrl !== '') { | ||||
| 			const server = encodeURIComponent(DomainUtil.formatUrl(serverUrl)); | ||||
| 			const fileName = certificate.substring(certificate.lastIndexOf('/') + 1); | ||||
| 			const fileName = path.basename(certificate); | ||||
| 			const copy = CertificateUtil.copyCertificate(server, certificate, fileName); | ||||
| 			if (!copy) { | ||||
| 				return; | ||||
|   | ||||
| @@ -17,7 +17,7 @@ class ConnectedOrgSection extends BaseSection { | ||||
| 				<div class="page-title">Connected organizations</div> | ||||
| 				<div class="title" id="existing-servers">All the connected orgnizations will appear here.</div> | ||||
| 				<div id="server-info-container"></div> | ||||
|  | ||||
| 				<div id="new-org-button"><button class="green sea w-250">Connect to another organization</button></div> | ||||
| 				<div class="page-title">Add Custom Certificates</div> | ||||
| 				<div id="add-certificate-container"></div> | ||||
| 			</div> | ||||
| @@ -33,8 +33,11 @@ class ConnectedOrgSection extends BaseSection { | ||||
|  | ||||
| 		const servers = DomainUtil.getDomains(); | ||||
| 		this.props.$root.innerHTML = this.template(); | ||||
|  | ||||
| 		this.$serverInfoContainer = document.getElementById('server-info-container'); | ||||
| 		this.$existingServers = document.getElementById('existing-servers'); | ||||
| 		this.$newOrgButton = document.getElementById('new-org-button'); | ||||
| 		this.$addCertificateContainer = document.getElementById('add-certificate-container'); | ||||
|  | ||||
| 		const noServerText = 'All the connected orgnizations will appear here'; | ||||
| 		// Show noServerText if no servers are there otherwise hide it | ||||
| @@ -49,7 +52,12 @@ class ConnectedOrgSection extends BaseSection { | ||||
| 			}).init(); | ||||
| 		} | ||||
|  | ||||
| 		this.$addCertificateContainer = document.getElementById('add-certificate-container'); | ||||
| 		this.$newOrgButton.addEventListener('click', () => { | ||||
| 			// We don't need to import this since it's already imported in other files | ||||
| 			// eslint-disable-next-line no-undef | ||||
| 			ipcRenderer.send('forward-message', 'open-org-tab'); | ||||
| 		}); | ||||
|  | ||||
| 		this.initAddCertificate(); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -23,8 +23,12 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-description">Show app icon in system tray</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="menubar-option" style= "display:${process.platform === 'darwin' ? 'none' : ''}"> | ||||
| 						<div class="setting-description">Auto hide Menubar (Press Alt key to display)</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="sidebar-option"> | ||||
| 						<div class="setting-description">Show sidebar (<span class="code">Cmd Or Ctrl+S</span>)</div> | ||||
| 						<div class="setting-description">Show sidebar (<span class="code">${process.platform === 'darwin' ? 'Cmd+Shift+S' : 'Ctrl+Shift+S'}</span>)</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="badge-option"> | ||||
| @@ -40,7 +44,7 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="title">Desktop Notification</div> | ||||
| 				<div class="title">Desktop Notifications</div> | ||||
| 				<div class="settings-card"> | ||||
| 					<div class="setting-row" id="show-notification-option"> | ||||
| 						<div class="setting-description">Show Desktop Notifications</div> | ||||
| @@ -73,17 +77,21 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="enable-spellchecker-option"> | ||||
| 						<div class="setting-description">Enable Spellchecker (requires restart)</div> | ||||
| 						<div class="setting-description">Enable spellchecker (requires restart)</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="title">Add custom CSS</div> | ||||
| 				<div class="title">Advanced</div> | ||||
| 				<div class="settings-card"> | ||||
| 				<div class="setting-row" id="show-download-folder"> | ||||
| 					<div class="setting-description">Show downloaded files in file manager</div> | ||||
| 					<div class="setting-control"></div> | ||||
| 				</div> | ||||
| 				<div class="setting-row" id="add-custom-css"> | ||||
| 				<div class="setting-description"> | ||||
| 							This will inject the selected css stylesheet in all the added accounts | ||||
| 					Add custom CSS | ||||
| 				</div> | ||||
| 						<button class="custom-css-button blue">Add</button> | ||||
| 				<button class="custom-css-button green">Upload</button> | ||||
| 			</div> | ||||
| 			<div class="setting-row" id="remove-custom-css"> | ||||
| 				<div class="setting-description"> | ||||
| @@ -94,27 +102,25 @@ class GeneralSection extends BaseSection { | ||||
| 					<span>Delete</span> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 				</div> | ||||
| 				<div class="title">Advanced</div> | ||||
| 				<div class="settings-card"> | ||||
| 					<div class="setting-row" id="download-folder"> | ||||
| 						<div class="setting-description"> | ||||
| 							Default download location | ||||
| 						</div> | ||||
| 						<button class="download-folder-button blue">Choose</button> | ||||
| 						<button class="download-folder-button green">Change</button> | ||||
| 					</div> | ||||
| 					<div class="setting-row"> | ||||
| 						<div class="setting-description"> | ||||
| 							<div class="download-folder-path">${ConfigUtil.getConfigItem('downloadsPath', `${app.getPath('downloads')}`)}</div> | ||||
| 						</div> | ||||
| 					</div> | ||||
|  | ||||
| 				</div> | ||||
| 				<div class="title">Reset Application Data</div> | ||||
|                 <div class="settings-card"> | ||||
| 					<div class="setting-row" id="resetdata-option"> | ||||
| 						<div class="setting-description">This will delete all application data including all added accounts and preferences | ||||
| 						</div> | ||||
| 						<button class="reset-data-button blue">Reset App Data</button> | ||||
| 						<button class="reset-data-button green w-150">Reset App Data</button> | ||||
| 					</div> | ||||
| 				</div> | ||||
|             </div> | ||||
| @@ -138,6 +144,7 @@ class GeneralSection extends BaseSection { | ||||
| 		this.showCustomCSSPath(); | ||||
| 		this.removeCustomCSS(); | ||||
| 		this.downloadFolder(); | ||||
| 		this.showDownloadFolder(); | ||||
|  | ||||
| 		// Platform specific settings | ||||
|  | ||||
| @@ -149,6 +156,11 @@ class GeneralSection extends BaseSection { | ||||
| 		if (process.platform === 'darwin') { | ||||
| 			this.updateDockBouncing(); | ||||
| 		} | ||||
|  | ||||
| 		// Auto hide menubar on Windows and Linux | ||||
| 		if (process.platform !== 'darwin') { | ||||
| 			this.updateMenubarOption(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	updateTrayOption() { | ||||
| @@ -164,6 +176,19 @@ class GeneralSection extends BaseSection { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateMenubarOption() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#menubar-option .setting-control'), | ||||
| 			value: ConfigUtil.getConfigItem('autoHideMenubar', false), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('autoHideMenubar'); | ||||
| 				ConfigUtil.setConfigItem('autoHideMenubar', newValue); | ||||
| 				ipcRenderer.send('toggle-menubar', newValue); | ||||
| 				this.updateMenubarOption(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateBadgeOption() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#badge-option .setting-control'), | ||||
| @@ -208,6 +233,10 @@ class GeneralSection extends BaseSection { | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('autoUpdate'); | ||||
| 				ConfigUtil.setConfigItem('autoUpdate', newValue); | ||||
| 				if (!newValue) { | ||||
| 					ConfigUtil.setConfigItem('betaUpdate', false); | ||||
| 					this.betaUpdateOption(); | ||||
| 				} | ||||
| 				this.autoUpdateOption(); | ||||
| 			} | ||||
| 		}); | ||||
| @@ -219,9 +248,11 @@ class GeneralSection extends BaseSection { | ||||
| 			value: ConfigUtil.getConfigItem('betaUpdate', false), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('betaUpdate'); | ||||
| 				if (ConfigUtil.getConfigItem('autoUpdate')) { | ||||
| 					ConfigUtil.setConfigItem('betaUpdate', newValue); | ||||
| 					this.betaUpdateOption(); | ||||
| 				} | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| @@ -385,6 +416,18 @@ class GeneralSection extends BaseSection { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	showDownloadFolder() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#show-download-folder .setting-control'), | ||||
| 			value: ConfigUtil.getConfigItem('showDownloadFolder', false), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('showDownloadFolder'); | ||||
| 				ConfigUtil.setConfigItem('showDownloadFolder', newValue); | ||||
| 				this.showDownloadFolder(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| module.exports = GeneralSection; | ||||
|   | ||||
| @@ -38,8 +38,7 @@ class NetworkSection extends BaseSection { | ||||
| 							<input class="setting-input-value" placeholder="e.g. foobar.com"/> | ||||
| 						</div> | ||||
| 						<div class="setting-row"> | ||||
| 							<div class="action blue" id="proxy-save-action"> | ||||
| 								<i class="material-icons">check_box</i> | ||||
| 							<div class="action green" id="proxy-save-action"> | ||||
| 								<span>Save</span> | ||||
| 							</div> | ||||
| 						</div> | ||||
| @@ -100,7 +99,7 @@ class NetworkSection extends BaseSection { | ||||
| 				if (newValue === false) { | ||||
| 					// Remove proxy system proxy settings | ||||
| 					ConfigUtil.setConfigItem('proxyRules', ''); | ||||
| 					ipcRenderer.send('forward-message', 'reload-proxy', true); | ||||
| 					ipcRenderer.send('forward-message', 'reload-proxy', false); | ||||
| 				} | ||||
| 				ConfigUtil.setConfigItem('useSystemProxy', newValue); | ||||
| 				this.updateProxyOption(); | ||||
|   | ||||
| @@ -90,7 +90,7 @@ class PreferenceView extends BaseComponent { | ||||
| 			this.handleNavigation(navItem); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toggle-sidebar', (event, state) => { | ||||
| 		ipcRenderer.on('toggle-sidebar-setting', (event, state) => { | ||||
| 			this.handleToggle('sidebar-option', state); | ||||
| 		}); | ||||
|  | ||||
|   | ||||
| @@ -14,6 +14,7 @@ class ShortcutsSection extends BaseSection { | ||||
|  | ||||
| 		return ` | ||||
|             <div class="settings-pane"> | ||||
|             <div class="settings-card tip"><p><b><i class="material-icons md-14">settings</i>Tip:  </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link"> keyboard shortcuts</span>.</p></div> | ||||
|               <div class="title">Application Shortcuts</div> | ||||
|               <div class="settings-card"> | ||||
|                 <table> | ||||
| @@ -160,7 +161,6 @@ class ShortcutsSection extends BaseSection { | ||||
|                 </table> | ||||
|                 <div class="setting-control"></div> | ||||
|               </div> | ||||
|               <div class="settings-card tip"><b><i class="material-icons md-14">settings</i>Tip: </b>These desktop app shortcuts extend the Zulip webapp's <span id="open-hotkeys-link">keyboard shortcuts</span>.</div> | ||||
|             </div> | ||||
| 		`; | ||||
| 	} | ||||
|   | ||||
| @@ -5,6 +5,7 @@ const SetupSpellChecker = require('./spellchecker'); | ||||
|  | ||||
| const ConfigUtil = require(__dirname + '/utils/config-util.js'); | ||||
| const LinkUtil = require(__dirname + '/utils/link-util.js'); | ||||
| const params = require(__dirname + '/utils/params-util.js'); | ||||
|  | ||||
| // eslint-disable-next-line import/no-unassigned-import | ||||
| require('./notification'); | ||||
| @@ -12,6 +13,9 @@ require('./notification'); | ||||
| // Prevent drag and drop event in main process which prevents remote code executaion | ||||
| require(__dirname + '/shared/preventdrag.js'); | ||||
|  | ||||
| // eslint-disable-next-line camelcase | ||||
| window.electron_bridge = require('./electron-bridge'); | ||||
|  | ||||
| const logout = () => { | ||||
| 	// Create the menu for the below | ||||
| 	document.querySelector('.dropdown-toggle').click(); | ||||
| @@ -39,16 +43,15 @@ process.once('loaded', () => { | ||||
|  | ||||
| // To prevent failing this script on linux we need to load it after the document loaded | ||||
| document.addEventListener('DOMContentLoaded', () => { | ||||
| 	if (params.isPageParams()) { | ||||
| 	// Get the default language of the server | ||||
| 		const serverLanguage = page_params.default_language; // eslint-disable-line no-undef, camelcase | ||||
|  | ||||
| 		if (serverLanguage) { | ||||
| 			// Set spellcheker language | ||||
| 			ConfigUtil.setConfigItem('spellcheckerLanguage', serverLanguage); | ||||
| 			// Init spellchecker | ||||
| 			SetupSpellChecker.init(); | ||||
| 		} | ||||
|  | ||||
| 		// redirect users to network troubleshooting page | ||||
| 		const getRestartButton = document.querySelector('.restart_get_events_button'); | ||||
| 		if (getRestartButton) { | ||||
| @@ -56,10 +59,8 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
| 				ipcRenderer.send('forward-message', 'reload-viewer'); | ||||
| 			}); | ||||
| 		} | ||||
|  | ||||
| 		// Open image attachment link in the lightbox instead of opening in the default browser | ||||
| 		const { $, lightbox } = window; | ||||
|  | ||||
| 		$('#main_div').on('click', '.message_content p a', function (e) { | ||||
| 			const url = $(this).attr('href'); | ||||
|  | ||||
| @@ -79,6 +80,7 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
| 				lightbox.open($img); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| // Clean up spellchecker events after you navigate away from this page; | ||||
|   | ||||
| @@ -99,7 +99,9 @@ const renderNativeImage = function (arg) { | ||||
| 		.then(() => renderCanvas(arg)) | ||||
| 		.then(canvas => { | ||||
| 			const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPNG(); | ||||
| 			return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio)); | ||||
| 			return Promise.resolve(nativeImage.createFromBuffer(pngData, { | ||||
| 				scaleFactor: config.pixelRatio | ||||
| 			})); | ||||
| 		}); | ||||
| }; | ||||
|  | ||||
| @@ -115,26 +117,13 @@ function sendAction(action) { | ||||
|  | ||||
| const createTray = function () { | ||||
| 	window.tray = new Tray(iconPath()); | ||||
| 	const contextMenu = Menu.buildFromTemplate([{ | ||||
| 		label: 'About', | ||||
| 		click() { | ||||
| 			// We need to focus the main window first | ||||
| 			ipcRenderer.send('focus-app'); | ||||
| 			sendAction('open-about'); | ||||
| 		} | ||||
| 	}, | ||||
| 	const contextMenu = Menu.buildFromTemplate([ | ||||
| 		{ | ||||
| 		type: 'separator' | ||||
| 	}, | ||||
| 	{ | ||||
| 		label: 'Focus', | ||||
| 			label: 'Zulip', | ||||
| 			click() { | ||||
| 				ipcRenderer.send('focus-app'); | ||||
| 			} | ||||
| 		}, | ||||
| 	{ | ||||
| 		type: 'separator' | ||||
| 	}, | ||||
| 		{ | ||||
| 			label: 'Settings', | ||||
| 			click() { | ||||
| @@ -153,12 +142,11 @@ const createTray = function () { | ||||
| 		} | ||||
| 	]); | ||||
| 	window.tray.setContextMenu(contextMenu); | ||||
| 	if (process.platform === 'linux' || process.platform === 'win32') { | ||||
| 		window.tray.on('click', () => { | ||||
| 		// Click event only works on Windows | ||||
| 		if (process.platform === 'win32') { | ||||
| 			ipcRenderer.send('toggle-app'); | ||||
| 		} | ||||
| 		}); | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| ipcRenderer.on('destroytray', event => { | ||||
|   | ||||
							
								
								
									
										25
									
								
								app/renderer/js/utils/common-util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/renderer/js/utils/common-util.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| let instance = null; | ||||
|  | ||||
| class CommonUtil { | ||||
| 	constructor() { | ||||
| 		if (instance) { | ||||
| 			return instance; | ||||
| 		} else { | ||||
| 			instance = this; | ||||
| 		} | ||||
| 		return instance; | ||||
| 	} | ||||
|  | ||||
| 	// unescape already encoded/escaped strings | ||||
| 	decodeString(string) { | ||||
| 		const parser = new DOMParser(); | ||||
| 		const dom = parser.parseFromString( | ||||
| 			'<!doctype html><body>' + string, | ||||
| 			'text/html'); | ||||
| 		return dom.body.textContent; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| module.exports = new CommonUtil(); | ||||
| @@ -39,7 +39,12 @@ class ConfigUtil { | ||||
| 	} | ||||
|  | ||||
| 	getConfigItem(key, defaultValue = null) { | ||||
| 		this.reloadDB(); | ||||
| 		try { | ||||
| 			this.db.reload(); | ||||
| 		} catch (err) { | ||||
| 			logger.error('Error while reloading settings.json: '); | ||||
| 			logger.error(err); | ||||
| 		} | ||||
| 		const value = this.db.getData('/')[key]; | ||||
| 		if (value === undefined) { | ||||
| 			this.setConfigItem(key, defaultValue); | ||||
| @@ -50,19 +55,24 @@ class ConfigUtil { | ||||
| 	} | ||||
| 	// This function returns whether a key exists in the configuration file (settings.json) | ||||
| 	isConfigItemExists(key) { | ||||
| 		this.reloadDB(); | ||||
| 		try { | ||||
| 			this.db.reload(); | ||||
| 		} catch (err) { | ||||
| 			logger.error('Error while reloading settings.json: '); | ||||
| 			logger.error(err); | ||||
| 		} | ||||
| 		const value = this.db.getData('/')[key]; | ||||
| 		return (value !== undefined); | ||||
| 	} | ||||
|  | ||||
| 	setConfigItem(key, value) { | ||||
| 		this.db.push(`/${key}`, value, true); | ||||
| 		this.reloadDB(); | ||||
| 		this.db.save(); | ||||
| 	} | ||||
|  | ||||
| 	removeConfigItem(key) { | ||||
| 		this.db.delete(`/${key}`); | ||||
| 		this.reloadDB(); | ||||
| 		this.db.save(); | ||||
| 	} | ||||
|  | ||||
| 	reloadDB() { | ||||
|   | ||||
| @@ -10,6 +10,9 @@ const escape = require('escape-html'); | ||||
| const Logger = require('./logger-util'); | ||||
|  | ||||
| const CertificateUtil = require(__dirname + '/certificate-util.js'); | ||||
| const ProxyUtil = require(__dirname + '/proxy-util.js'); | ||||
| const ConfigUtil = require(__dirname + '/config-util.js'); | ||||
| const SystemUtil = require(__dirname + '/../utils/system-util.js'); | ||||
|  | ||||
| const logger = new Logger({ | ||||
| 	file: `domain-util.log`, | ||||
| @@ -63,7 +66,7 @@ class DomainUtil { | ||||
| 	addDomain(server) { | ||||
| 		return new Promise(resolve => { | ||||
| 			if (server.icon) { | ||||
| 				this.saveServerIcon(server.icon).then(localIconUrl => { | ||||
| 				this.saveServerIcon(server).then(localIconUrl => { | ||||
| 					server.icon = localIconUrl; | ||||
| 					this.db.push('/domains[]', server, true); | ||||
| 					this.reloadDB(); | ||||
| @@ -119,8 +122,18 @@ class DomainUtil { | ||||
| 				logger.warn('Error while trying to get certificate: ' + err); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy'); | ||||
|  | ||||
| 		// If certificate for the domain exists add it as a ca key in the request's parameter else consider only domain as the parameter for request | ||||
| 		const checkDomain = (certificateLocation) ? ({url: domain + '/static/audio/zulip.ogg', ca: certificateLocation}) : domain + '/static/audio/zulip.ogg'; | ||||
| 		// Add proxy as a parameter if it sbeing used. | ||||
| 		const checkDomain = { | ||||
| 			url: domain + '/static/audio/zulip.ogg', | ||||
| 			ca: (certificateLocation) ? certificateLocation : '', | ||||
| 			proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : '', | ||||
| 			ecdhCurve: 'auto', | ||||
| 			headers: { 'User-Agent': SystemUtil.getUserAgent() } | ||||
| 		}; | ||||
|  | ||||
| 		const serverConf = { | ||||
| 			icon: defaultIconUrl, | ||||
| @@ -193,9 +206,28 @@ class DomainUtil { | ||||
| 	} | ||||
|  | ||||
| 	getServerSettings(domain) { | ||||
| 		const serverSettingsUrl = domain + '/api/v1/server_settings'; | ||||
| 		const certificate = CertificateUtil.getCertificate(encodeURIComponent(domain)); | ||||
| 		let certificateLocation = ''; | ||||
|  | ||||
| 		if (certificate) { | ||||
| 			// To handle case where certificate has been moved from the location in certificates.json | ||||
| 			try { | ||||
| 				certificateLocation = fs.readFileSync(certificate); | ||||
| 			} catch (err) { | ||||
| 				logger.warn('Error while trying to get certificate: ' + err); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy'); | ||||
| 		const serverSettingsOptions = { | ||||
| 			url: domain + '/api/v1/server_settings', | ||||
| 			ca: (certificateLocation) ? certificateLocation : '', | ||||
| 			proxy: proxyEnabled ? ProxyUtil.getProxy(domain) : '', | ||||
| 			ecdhCurve: 'auto', | ||||
| 			headers: { 'User-Agent': SystemUtil.getUserAgent() } | ||||
| 		}; | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			request(serverSettingsUrl, (error, response) => { | ||||
| 			request(serverSettingsOptions, (error, response) => { | ||||
| 				if (!error && response.statusCode === 200) { | ||||
| 					const data = JSON.parse(response.body); | ||||
| 					if (data.hasOwnProperty('realm_icon') && data.realm_icon) { | ||||
| @@ -214,13 +246,39 @@ class DomainUtil { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	saveServerIcon(url) { | ||||
| 	saveServerIcon(server) { | ||||
| 		const url = server.icon; | ||||
| 		const domain = server.url; | ||||
|  | ||||
| 		const certificate = CertificateUtil.getCertificate(encodeURIComponent(domain)); | ||||
| 		let certificateLocation = ''; | ||||
|  | ||||
| 		if (certificate) { | ||||
| 			// To handle case where certificate has been moved from the location in certificates.json | ||||
| 			try { | ||||
| 				certificateLocation = fs.readFileSync(certificate); | ||||
| 			} catch (err) { | ||||
| 				logger.warn('Error while trying to get certificate: ' + err); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		const proxyEnabled = ConfigUtil.getConfigItem('useManualProxy') || ConfigUtil.getConfigItem('useSystemProxy'); | ||||
|  | ||||
| 		// Add proxy and certificate as a parameter if its being used. | ||||
| 		const serverIconOptions = { | ||||
| 			url, | ||||
| 			ca: (certificateLocation) ? certificateLocation : '', | ||||
| 			proxy: proxyEnabled ? ProxyUtil.getProxy(url) : '', | ||||
| 			ecdhCurve: 'auto', | ||||
| 			headers: { 'User-Agent': SystemUtil.getUserAgent() } | ||||
| 		}; | ||||
|  | ||||
| 		// The save will always succeed. If url is invalid, downgrade to default icon. | ||||
| 		return new Promise(resolve => { | ||||
| 			const filePath = this.generateFilePath(url); | ||||
| 			const file = fs.createWriteStream(filePath); | ||||
| 			try { | ||||
| 				request(url).on('response', response => { | ||||
| 				request(serverIconOptions).on('response', response => { | ||||
| 					response.on('error', err => { | ||||
| 						logger.log('Could not get server icon.'); | ||||
| 						logger.log(err); | ||||
| @@ -248,7 +306,7 @@ class DomainUtil { | ||||
| 	updateSavedServer(url, index) { | ||||
| 		// Does not promise successful update | ||||
| 		this.checkDomain(url, true).then(newServerConf => { | ||||
| 			this.saveServerIcon(newServerConf.icon).then(localIconUrl => { | ||||
| 			this.saveServerIcon(newServerConf).then(localIconUrl => { | ||||
| 				newServerConf.icon = localIconUrl; | ||||
| 				this.updateDomain(index, newServerConf); | ||||
| 				this.reloadDB(); | ||||
|   | ||||
| @@ -34,6 +34,12 @@ class LinkUtil { | ||||
| 		const isImageUrl = /\.(bmp|gif|jpg|jpeg|png|webp)\?*.*$/i; | ||||
| 		return isImageUrl.test(url); | ||||
| 	} | ||||
|  | ||||
| 	isPDF(url) { | ||||
| 		// test for pdf extension | ||||
| 		const isPDFUrl = /\.(pdf)\?*.*$/i; | ||||
| 		return isPDFUrl.test(url); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| module.exports = new LinkUtil(); | ||||
|   | ||||
							
								
								
									
										15
									
								
								app/renderer/js/utils/params-util.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								app/renderer/js/utils/params-util.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // This util function returns the page params if they're present else returns null | ||||
| function isPageParams() { | ||||
| 	let webpageParams = null; | ||||
| 	try { | ||||
| 		// eslint-disable-next-line no-undef, camelcase | ||||
| 		webpageParams = page_params; | ||||
| 	} catch (err) { | ||||
| 		webpageParams = null; | ||||
| 	} | ||||
| 	return webpageParams; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
| 	isPageParams | ||||
| }; | ||||
| @@ -1,5 +1,6 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const url = require('url'); | ||||
| const ConfigUtil = require('./config-util.js'); | ||||
|  | ||||
| let instance = null; | ||||
| @@ -15,6 +16,39 @@ class ProxyUtil { | ||||
| 		return instance; | ||||
| 	} | ||||
|  | ||||
| 	// Return proxy to be used for a particular uri, to be used for request | ||||
| 	getProxy(uri) { | ||||
| 		uri = url.parse(uri); | ||||
| 		const proxyRules = ConfigUtil.getConfigItem('proxyRules', '').split(';'); | ||||
| 		// If SPS is on and system uses no proxy then request should not try to use proxy from | ||||
| 		// environment. NO_PROXY = '*' makes request ignore all environment proxy variables. | ||||
| 		if (proxyRules[0] === '') { | ||||
| 			process.env.NO_PROXY = '*'; | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		const proxyRule = {}; | ||||
| 		if (uri.protocol === 'http:') { | ||||
| 			proxyRules.forEach(proxy => { | ||||
| 				if (proxy.includes('http=')) { | ||||
| 					proxyRule.hostname = proxy.split('http=')[1].trim().split(':')[0]; | ||||
| 					proxyRule.port = proxy.split('http=')[1].trim().split(':')[1]; | ||||
| 				} | ||||
| 			}); | ||||
| 			return proxyRule; | ||||
| 		} | ||||
|  | ||||
| 		if (uri.protocol === 'https:') { | ||||
| 			proxyRules.forEach(proxy => { | ||||
| 				if (proxy.includes('https=')) { | ||||
| 					proxyRule.hostname = proxy.split('https=')[1].trim().split(':')[0]; | ||||
| 					proxyRule.port = proxy.split('https=')[1].trim().split(':')[1]; | ||||
| 				} | ||||
| 			}); | ||||
| 			return proxyRule; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	resolveSystemProxy(mainWindow) { | ||||
| 		const page = mainWindow.webContents; | ||||
| 		const ses = page.session; | ||||
|   | ||||
| @@ -45,8 +45,8 @@ class ReconnectUtil { | ||||
| 						const errMsgHolder = document.querySelector('#description'); | ||||
| 						if (errMsgHolder) { | ||||
| 							errMsgHolder.innerHTML = ` | ||||
| 										<div>You internet connection does't seem to work properly!</div> | ||||
| 										</div>Verify that it works and then click try again.</div>`; | ||||
| 										<div>Your internet connection doesn't seem to work properly!</div> | ||||
| 										<div>Verify that it works and then click try again.</div>`; | ||||
| 						} | ||||
| 						return resolve(false); | ||||
| 					}); | ||||
|   | ||||
| @@ -5,6 +5,10 @@ const sentryInit = () => { | ||||
| 	if (!isDev) { | ||||
| 		init({ | ||||
| 			dsn: 'SENTRY_DSN', | ||||
| 			// We should ignore this error since it's harmless and we know the reason behind this | ||||
| 			// This error mainly comes from the console logs. | ||||
| 			// This is a temp solution until Sentry supports disabling the console logs | ||||
| 			ignoreErrors: ['does not appear to be a valid Zulip server'], | ||||
| 			sendTimeout: 30 // wait 30 seconds before considering the sending capture to have failed, default is 1 second | ||||
| 		}); | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										81
									
								
								changelog.md
									
									
									
									
									
								
							
							
						
						
									
										81
									
								
								changelog.md
									
									
									
									
									
								
							| @@ -1,9 +1,85 @@ | ||||
| # Version History | ||||
|  | ||||
|  | ||||
|  | ||||
| All notable changes to the Zulip desktop app are documented in this file. | ||||
|  | ||||
| ### v2.3.82 --2018-09-25 | ||||
|  | ||||
| **New features**: | ||||
| * UI enhancements. | ||||
| * Updated some menu items. | ||||
|  | ||||
| **Fixes**: | ||||
| * Fix about page not opening up. | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.8 --2018-09-25 | ||||
|  | ||||
| **New features**: | ||||
| * Auto hide menubar on Windows/Linux. Add a setting option for the same. | ||||
| * Improve design of setting page. | ||||
| * Toggle app on clicking the tray icon (Linux). | ||||
| * Update sidebar realm name when it's changed in webapp. | ||||
| * left-sidebar: Add initial character of realm name instead of default icon. | ||||
|  | ||||
| **Fixes**: | ||||
| * linux: Fix ALT+SHIFT opening menu items on Linux. | ||||
| * sentry: Add ignore errors to sentry configuration. | ||||
| * Linux: Add label for help menu item. | ||||
| * file-attachments: Allow multiple downloads of same file name. | ||||
|  | ||||
| **Module Updates** | ||||
| * electron: Update electron to v2.0.9. | ||||
|  | ||||
|  | ||||
| <hr> | ||||
|  | ||||
|  | ||||
| ### v2.3.7-beta --2018-09-03 | ||||
|  | ||||
| **New features**: | ||||
| * Add a feature to show and view pdf file in the app. | ||||
|  | ||||
| **Fixes**: | ||||
| * Use package reload instead of custom reload. This is an experimental fix for setting files getting corrupted issue. | ||||
| * Unescape server name in window menu item. | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.6 --2018-08-27 | ||||
|  | ||||
| **New features**: | ||||
| * Add proxy details while validating a server. This fixes the server validating issue for users who are using the proxy settings.  | ||||
|  | ||||
|  | ||||
| **Fixes**: | ||||
|  | ||||
| * Fix youtube video not playing in lightbox.  | ||||
| * Fix realm name not escaped properly. | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.5 --2018-08-03 | ||||
|  | ||||
| **New features**: | ||||
| * Add a setting option to show downloaded file in file manager. | ||||
| * Added electron bridge to communicate with webapp in real time. | ||||
|  | ||||
| **Fixes**: | ||||
|  | ||||
| * Fix failing attached file downloads. | ||||
| * Fix page_params error. | ||||
| * gulpfile: Update syntax and methods for gulp v4.x. | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.4-beta --2018-07-24 | ||||
|  | ||||
| **Fixes**: | ||||
| * Fix downloading functionality of file attachments. | ||||
| * Fix null of downloadPath when settings.json fails. | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.3 --2018-07-14 | ||||
|  | ||||
| @@ -27,6 +103,7 @@ electron-builder: v20.20.4 | ||||
|  | ||||
| electron-updater: v2.23.3 | ||||
|  | ||||
| <hr> | ||||
|  | ||||
| ### v2.3.2 --2018-05-28 | ||||
|  | ||||
|   | ||||
							
								
								
									
										10
									
								
								gulpfile.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								gulpfile.js
									
									
									
									
									
								
							| @@ -10,11 +10,11 @@ gulp.task('dev', () => { | ||||
|   // Start browser process | ||||
| 	electron.start(); | ||||
|   // Restart browser process | ||||
| 	gulp.watch('app/main/*.js', ['restart:browser']); | ||||
| 	gulp.watch('app/main/*.js', gulp.series('restart:browser')); | ||||
|   // Reload renderer process | ||||
| 	gulp.watch('app/renderer/css/*.css', ['reload:renderer']); | ||||
| 	gulp.watch('app/renderer/*.html', ['reload:renderer']); | ||||
| 	gulp.watch('app/renderer/js/**/*.js', ['reload:renderer']); | ||||
| 	gulp.watch('app/renderer/css/*.css', gulp.series('reload:renderer')); | ||||
| 	gulp.watch('app/renderer/*.html', gulp.series('reload:renderer')); | ||||
| 	gulp.watch('app/renderer/js/**/*.js', gulp.series('reload:renderer')); | ||||
| }); | ||||
|  | ||||
| gulp.task('restart:browser', done => { | ||||
| @@ -36,4 +36,4 @@ gulp.task('test-e2e', () => { | ||||
| 	})); | ||||
| }); | ||||
|  | ||||
| gulp.task('default', ['dev', 'test-e2e']); | ||||
| gulp.task('default', gulp.parallel('dev', 'test-e2e')); | ||||
|   | ||||
							
								
								
									
										105
									
								
								help.md
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								help.md
									
									
									
									
									
								
							| @@ -1,105 +0,0 @@ | ||||
| # User Guide | ||||
|  | ||||
| > Welcome! This guide will walk you through the basics of using Zulip Desktop. | ||||
|  | ||||
|  | ||||
| ## Get Zulip Desktop | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Connect to a Server | ||||
|  | ||||
| ### Connect through a proxy | ||||
|  | ||||
| It's possible to connect to your server through a proxy.  | ||||
| You can enter the proxy settings in the `Network` section of App Settings.  | ||||
| There are three fields provided: | ||||
| * `PAC script` - The URL associated with the PAC file. | ||||
| * `Proxy rules` - Rules indicating which proxies to use. | ||||
| * `Proxy bypass rules` - Rules indicating which URLs should | ||||
|     bypass the proxy settings. | ||||
| 	 | ||||
| For a typical setup where internet access is required to use an HTTP proxy, | ||||
| but URLs on the local network should be accessed directly, configure as follows: | ||||
|  | ||||
| `Proxy rules = proxy.example.com` | ||||
|  | ||||
| Your HTTP proxy server | ||||
| `Proxy bypass rules = *.example.com;10.0.0.0/8` | ||||
|  | ||||
| Directly connect to your own domain and private IP subnet | ||||
| for more complex setups, read below to configure complex proxy rules and proxy bypass rules. | ||||
|  | ||||
| ### Sets the proxy settings. | ||||
|  | ||||
| When `PAC script` and `Proxy rules` are provided together, the `Proxy rules` | ||||
| option is ignored and `PAC script` configuration is applied. | ||||
|  | ||||
| The `Proxy rules` has to follow the rules below: | ||||
|  | ||||
| ``` | ||||
| proxyRules = schemeProxies[";"<schemeProxies>] | ||||
| schemeProxies = [<urlScheme>"="]<proxyURIList> | ||||
| urlScheme = "http" | "https" | "ftp" | "socks" | ||||
| proxyURIList = <proxyURL>[","<proxyURIList>] | ||||
| proxyURL = [<proxyScheme>"://"]<proxyHost>[":"<proxyPort>] | ||||
| ``` | ||||
|  | ||||
| For example: | ||||
|  | ||||
| * `http=foopy:80;ftp=foopy2` - Use HTTP proxy `foopy:80` for `http://` URLs, and | ||||
|   HTTP proxy `foopy2:80` for `ftp://` URLs. | ||||
| * `foopy:80` - Use HTTP proxy `foopy:80` for all URLs. | ||||
| * `foopy:80,bar,direct://` - Use HTTP proxy `foopy:80` for all URLs, failing | ||||
|   over to `bar` if `foopy:80` is unavailable, and after that using no proxy. | ||||
| * `socks4://foopy` - Use SOCKS v4 proxy `foopy:1080` for all URLs. | ||||
| * `http=foopy,socks5://bar.com` - Use HTTP proxy `foopy` for http URLs, and fail | ||||
|   over to the SOCKS5 proxy `bar.com` if `foopy` is unavailable. | ||||
| * `http=foopy,direct://` - Use HTTP proxy `foopy` for http URLs, and use no | ||||
|   proxy if `foopy` is unavailable. | ||||
| * `http=foopy;socks=foopy2` -  Use HTTP proxy `foopy` for http URLs, and use | ||||
|   `socks4://foopy2` for all other URLs. | ||||
|  | ||||
| The `Proxy bypass rules` is a comma separated list of rules described below: | ||||
|  | ||||
| * `[ URL_SCHEME "://" ] HOSTNAME_PATTERN [ ":" <port> ]` | ||||
|  | ||||
|    Match all hostnames that match the pattern HOSTNAME_PATTERN. | ||||
|  | ||||
|    Examples: | ||||
|      "foobar.com", "*foobar.com", "*.foobar.com", "*foobar.com:99", | ||||
|      "https://x.*.y.com:99" | ||||
|  | ||||
|  * `"." HOSTNAME_SUFFIX_PATTERN [ ":" PORT ]` | ||||
|  | ||||
|    Match a particular domain suffix. | ||||
|  | ||||
|    Examples: | ||||
|      ".google.com", ".com", "http://.google.com" | ||||
|  | ||||
| * `[ SCHEME "://" ] IP_LITERAL [ ":" PORT ]` | ||||
|  | ||||
|    Match URLs which are IP address literals. | ||||
|  | ||||
|    Examples: | ||||
|      "127.0.1", "[0:0::1]", "[::1]", "http://[::1]:99" | ||||
|  | ||||
| *  `IP_LITERAL "/" PREFIX_LENGHT_IN_BITS` | ||||
|  | ||||
|    Match any URL that is to an IP literal that falls between the | ||||
|    given range. IP range is specified using CIDR notation. | ||||
|  | ||||
|    Examples: | ||||
|      "192.168.1.1/16", "fefe:13::abc/33". | ||||
|  | ||||
| *  `<local>` | ||||
|  | ||||
|    Match local addresses. The meaning of `<local>` is whether the | ||||
|    host matches one of: "127.0.0.1", "::1", "localhost". | ||||
|  | ||||
|  | ||||
| ## Change App Preferences | ||||
|  | ||||
|  | ||||
|  | ||||
| ## Reporting an Issue | ||||
							
								
								
									
										1292
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1292
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "zulip", | ||||
|   "productName": "Zulip", | ||||
|   "version": "2.3.4-beta", | ||||
|   "version": "2.5.0", | ||||
|   "main": "./app/main", | ||||
|   "description": "Zulip Desktop App", | ||||
|   "license": "Apache-2.0", | ||||
| @@ -123,8 +123,8 @@ | ||||
|     "assert": "1.4.1", | ||||
|     "cp-file": "^5.0.0", | ||||
|     "devtron": "1.4.0", | ||||
|     "electron": "2.0.1", | ||||
|     "electron-builder": "20.20.4", | ||||
|     "electron": "3.0.10", | ||||
|     "electron-builder": "20.28.3", | ||||
|     "electron-connect": "0.6.2", | ||||
|     "electron-debug": "1.4.0", | ||||
|     "google-translate-api": "2.3.0", | ||||
| @@ -133,7 +133,7 @@ | ||||
|     "is-ci": "^1.0.10", | ||||
|     "nodemon": "^1.14.11", | ||||
|     "pre-commit": "1.2.2", | ||||
|     "spectron": "3.8.0", | ||||
|     "spectron": "^5.0.0", | ||||
|     "tap-colorize": "^1.2.0", | ||||
|     "tape": "^4.8.0", | ||||
|     "xo": "0.18.2" | ||||
| @@ -153,7 +153,7 @@ | ||||
|           "max-lines": [ | ||||
|             "warn", | ||||
|             { | ||||
|               "max": 500, | ||||
|               "max": 600, | ||||
|               "skipBlankLines": true, | ||||
|               "skipComments": true | ||||
|             } | ||||
|   | ||||
							
								
								
									
										183
									
								
								tests/e2e/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								tests/e2e/package.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,183 @@ | ||||
| { | ||||
|     "name": "zulip", | ||||
|     "productName": "Zulip", | ||||
|     "version": "2.4.0", | ||||
|     "main": "../../app/main", | ||||
|     "description": "Zulip Desktop App", | ||||
|     "license": "Apache-2.0", | ||||
|     "copyright": "Kandra Labs, Inc.", | ||||
|     "author": { | ||||
|       "name": "Kandra Labs, Inc.", | ||||
|       "email": "support@zulipchat.com" | ||||
|     }, | ||||
|     "repository": { | ||||
|       "type": "git", | ||||
|       "url": "https://github.com/zulip/zulip-electron.git" | ||||
|     }, | ||||
|     "bugs": { | ||||
|       "url": "https://github.com/zulip/zulip-electron/issues" | ||||
|     }, | ||||
|     "engines": { | ||||
|       "node": ">=6.0.0" | ||||
|     }, | ||||
|     "scripts": { | ||||
|       "start": "electron app --disable-http-cache --no-electron-connect", | ||||
|       "reinstall": "node ./tools/reinstall-node-modules.js", | ||||
|       "postinstall": "electron-builder install-app-deps", | ||||
|       "test": "xo", | ||||
|       "test-e2e": "gulp test-e2e", | ||||
|       "dev": "gulp dev & nodemon --watch app/main --watch app/renderer --exec 'npm test' -e html,css,js", | ||||
|       "pack": "electron-builder --dir", | ||||
|       "dist": "electron-builder", | ||||
|       "mas": "electron-builder --mac mas", | ||||
|       "travis": "cd ./scripts && ./travis-build-test.sh", | ||||
|       "build-locales": "node tools/locale-helper" | ||||
|     }, | ||||
|     "pre-commit": [ | ||||
|       "test" | ||||
|     ], | ||||
|     "build": { | ||||
|       "appId": "org.zulip.zulip-electron", | ||||
|       "asar": true, | ||||
|       "files": [ | ||||
|         "**/*", | ||||
|         "!docs${/*}", | ||||
|         "!node_modules/@paulcbetts/cld/deps/cld${/*}" | ||||
|       ], | ||||
|       "copyright": "©2017 Kandra Labs, Inc.", | ||||
|       "mac": { | ||||
|         "category": "public.app-category.productivity", | ||||
|         "artifactName": "${productName}-${version}-${arch}.${ext}" | ||||
|       }, | ||||
|       "linux": { | ||||
|         "category": "Chat;GNOME;GTK;Network;InstantMessaging", | ||||
|         "packageCategory": "GNOME;GTK;Network;InstantMessaging", | ||||
|         "description": "Zulip Desktop Client for Linux", | ||||
|         "target": [ | ||||
|           "deb", | ||||
|           "zip", | ||||
|           "AppImage", | ||||
|           "snap" | ||||
|         ], | ||||
|         "maintainer": "Akash Nimare <svnitakash@gmail.com>", | ||||
|         "artifactName": "${productName}-${version}-${arch}.${ext}" | ||||
|       }, | ||||
|       "deb": { | ||||
|         "synopsis": "Zulip Desktop App", | ||||
|         "afterInstall": "./scripts/debian-add-repo.sh", | ||||
|         "afterRemove": "./scripts/debian-uninstaller.sh" | ||||
|       }, | ||||
|       "snap": { | ||||
|         "synopsis": "Zulip Desktop App" | ||||
|       }, | ||||
|       "dmg": { | ||||
|         "background": "build/appdmg.png", | ||||
|         "icon": "build/icon.icns", | ||||
|         "iconSize": 100, | ||||
|         "contents": [ | ||||
|           { | ||||
|             "x": 380, | ||||
|             "y": 280, | ||||
|             "type": "link", | ||||
|             "path": "/Applications" | ||||
|           }, | ||||
|           { | ||||
|             "x": 110, | ||||
|             "y": 280, | ||||
|             "type": "file" | ||||
|           } | ||||
|         ], | ||||
|         "window": { | ||||
|           "width": 500, | ||||
|           "height": 500 | ||||
|         } | ||||
|       }, | ||||
|       "win": { | ||||
|         "target": [ | ||||
|           { | ||||
|             "target": "nsis-web", | ||||
|             "arch": [ | ||||
|               "x64", | ||||
|               "ia32" | ||||
|             ] | ||||
|           } | ||||
|         ], | ||||
|         "icon": "build/icon.ico", | ||||
|         "publisherName": "Kandra Labs, Inc." | ||||
|       }, | ||||
|       "nsis": { | ||||
|         "allowToChangeInstallationDirectory": true | ||||
|       } | ||||
|     }, | ||||
|     "keywords": [ | ||||
|       "Zulip", | ||||
|       "Group Chat app", | ||||
|       "electron-app", | ||||
|       "electron", | ||||
|       "Desktop app", | ||||
|       "InstantMessaging" | ||||
|     ], | ||||
|     "devDependencies": { | ||||
|       "assert": "1.4.1", | ||||
|       "cp-file": "^5.0.0", | ||||
|       "devtron": "1.4.0", | ||||
|       "electron": "3.0.10", | ||||
|       "electron-builder": "20.38.4", | ||||
|       "electron-connect": "0.6.2", | ||||
|       "electron-debug": "1.4.0", | ||||
|       "google-translate-api": "2.3.0", | ||||
|       "gulp": "^4.0.0", | ||||
|       "gulp-tape": "0.0.9", | ||||
|       "is-ci": "^1.0.10", | ||||
|       "nodemon": "^1.14.11", | ||||
|       "pre-commit": "1.2.2", | ||||
|       "spectron": "3.8.0", | ||||
|       "tap-colorize": "^1.2.0", | ||||
|       "tape": "^4.8.0", | ||||
|       "xo": "0.18.2" | ||||
|     }, | ||||
|     "xo": { | ||||
|       "parserOptions": { | ||||
|         "sourceType": "script", | ||||
|         "ecmaFeatures": { | ||||
|           "globalReturn": true | ||||
|         } | ||||
|       }, | ||||
|       "esnext": true, | ||||
|       "overrides": [ | ||||
|         { | ||||
|           "files": "app*/**/*.js", | ||||
|           "rules": { | ||||
|             "max-lines": [ | ||||
|               "warn", | ||||
|               { | ||||
|                 "max": 600, | ||||
|                 "skipBlankLines": true, | ||||
|                 "skipComments": true | ||||
|               } | ||||
|             ], | ||||
|             "no-warning-comments": 0, | ||||
|             "object-curly-spacing": 0, | ||||
|             "capitalized-comments": 0, | ||||
|             "no-else-return": 0, | ||||
|             "no-path-concat": 0, | ||||
|             "no-alert": 0, | ||||
|             "guard-for-in": 0, | ||||
|             "prefer-promise-reject-errors": 0, | ||||
|             "import/no-unresolved": 0, | ||||
|             "import/no-extraneous-dependencies": 0, | ||||
|             "no-prototype-builtins": 0 | ||||
|           } | ||||
|         } | ||||
|       ], | ||||
|       "ignore": [ | ||||
|         "tests/*.js", | ||||
|         "tools/locale-helper/*.js" | ||||
|       ], | ||||
|       "envs": [ | ||||
|         "node", | ||||
|         "browser", | ||||
|         "mocha" | ||||
|       ] | ||||
|     } | ||||
|   } | ||||
		Reference in New Issue
	
	Block a user