mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-10-31 20:13:43 +00:00 
			
		
		
		
	Compare commits
	
		
			67 Commits
		
	
	
		
			disable-au
			...
			v1.6.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | dcd2abca6e | ||
|  | 2fb9efb981 | ||
|  | 7245b6a110 | ||
|  | bcb8ffb55f | ||
|  | 77094596a5 | ||
|  | 06ad44bdd7 | ||
|  | e719ba139c | ||
|  | 9853e9226c | ||
|  | f2c76b5ca3 | ||
|  | e6dbff995b | ||
|  | 4578d4a5f7 | ||
|  | 4b895a2312 | ||
|  | 53c0428a3a | ||
|  | 0a1866abb5 | ||
|  | ce862a4890 | ||
|  | 1b1ad2cd61 | ||
|  | ead7a06308 | ||
|  | 6659dd5097 | ||
|  | ed1f0f6d5b | ||
|  | 79acf8a6e1 | ||
|  | 8e0033f03e | ||
|  | 9144c2630d | ||
|  | fae05fc3b1 | ||
|  | 73603a4fd2 | ||
|  | a498ffc7d6 | ||
|  | 7afcf13401 | ||
|  | 89a292559d | ||
|  | be14517caf | ||
|  | 3b6c5ae532 | ||
|  | 40e3ed0f2f | ||
|  | 5d988858b0 | ||
|  | 3a974136a3 | ||
|  | 6ed5a5309c | ||
|  | 80c37fabb8 | ||
|  | 79366e19df | ||
|  | f409bb0449 | ||
|  | 45bdde951f | ||
|  | 6b627780f0 | ||
|  | 6f67553da5 | ||
|  | 2e710a9322 | ||
|  | 91f3afa8fe | ||
|  | f784345495 | ||
|  | 67da435154 | ||
|  | c89733610d | ||
|  | 8f272a67b5 | ||
|  | f6c4a76138 | ||
|  | b90a4c5254 | ||
|  | a06e09e565 | ||
|  | ad5bef821e | ||
|  | 58bbd7bf30 | ||
|  | 90d080dc96 | ||
|  | ad3fcf585e | ||
|  | 4b8f216bab | ||
|  | e620e0c428 | ||
|  | 50b3151b5d | ||
|  | 0c32756485 | ||
|  | 0c0835e364 | ||
|  | 9e962a5c44 | ||
|  | a218f7ea64 | ||
|  | 13a7f7475a | ||
|  | 48b17a1549 | ||
|  | 653598fd9e | ||
|  | ddbc282f49 | ||
|  | 992d92b06d | ||
|  | 45867ef15e | ||
|  | 6572c90d49 | ||
|  | 1ed0011c88 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -24,3 +24,5 @@ yarn-error.log* | ||||
| # miscellaneous | ||||
| .idea | ||||
| config.gypi | ||||
| .python-version | ||||
|  | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| 2.7.9 | ||||
| @@ -8,7 +8,7 @@ Desktop client for Zulip. Available for Mac, Linux and Windows. | ||||
| <img src="http://i.imgur.com/ChzTq4F.png"/> | ||||
|  | ||||
| # Download | ||||
| Please see [installation guide](./how-to-install.md). | ||||
| Please see [installation guide](https://zulipchat.com/help/desktop-app-install-guide). | ||||
|  | ||||
| # Features | ||||
| * Sign in to multiple teams | ||||
|   | ||||
| @@ -7,9 +7,8 @@ const isDev = require('electron-is-dev'); | ||||
| const ConfigUtil = require('./../renderer/js/utils/config-util.js'); | ||||
|  | ||||
| function appUpdater() { | ||||
| 	// Don't initiate auto-updates in development and on Linux system | ||||
| 	// since autoUpdater doesn't work on Linux | ||||
| 	if (isDev || process.platform === 'linux') { | ||||
| 	// Don't initiate auto-updates in development | ||||
| 	if (isDev) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
|   | ||||
							
								
								
									
										16
									
								
								app/main/crash-reporter.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/main/crash-reporter.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const { crashReporter } = require('electron'); | ||||
|  | ||||
| const crashHandler = () => { | ||||
| 	crashReporter.start({ | ||||
| 		productName: 'zulip-electron', | ||||
| 		companyName: 'Kandra Labs, Inc.', | ||||
| 		submitURL: 'https://zulip-sentry.herokuapp.com/crashreport', | ||||
| 		autoSubmit: true | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| module.exports = { | ||||
| 	crashHandler | ||||
| }; | ||||
| @@ -1,10 +1,12 @@ | ||||
| 'use strict'; | ||||
| const path = require('path'); | ||||
| const electron = require('electron'); | ||||
| const electronLocalshortcut = require('electron-localshortcut'); | ||||
| const windowStateKeeper = require('electron-window-state'); | ||||
| const isDev = require('electron-is-dev'); | ||||
| const appMenu = require('./menu'); | ||||
| const { appUpdater } = require('./autoupdater'); | ||||
| const { crashHandler } = require('./crash-reporter'); | ||||
|  | ||||
| const { setAutoLaunch } = require('./startup'); | ||||
|  | ||||
| const { app, ipcMain } = electron; | ||||
| @@ -12,7 +14,10 @@ const { app, ipcMain } = electron; | ||||
| const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js'); | ||||
|  | ||||
| // Adds debug features like hotkeys for triggering dev tools and reload | ||||
| // in development mode | ||||
| if (isDev) { | ||||
| 	require('electron-debug')(); | ||||
| } | ||||
|  | ||||
| // Prevent window being garbage collected | ||||
| let mainWindow; | ||||
| @@ -92,9 +97,6 @@ function createMainWindow() { | ||||
| 				win.hide(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Unregister all the shortcuts so that they don't interfare with other apps | ||||
| 		electronLocalshortcut.unregisterAll(mainWindow); | ||||
| 	}); | ||||
|  | ||||
| 	win.setTitle('Zulip'); | ||||
| @@ -122,33 +124,12 @@ function createMainWindow() { | ||||
| 	return win; | ||||
| } | ||||
|  | ||||
| function registerLocalShortcuts(page) { | ||||
| 	// Somehow, reload action cannot be overwritten by the menu item | ||||
| 	electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => { | ||||
| 		page.send('reload-viewer'); | ||||
| 	}); | ||||
|  | ||||
| 	// Also adding these shortcuts because some users might want to use it instead of CMD/Left-Right | ||||
| 	electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => { | ||||
| 		page.send('back'); | ||||
| 	}); | ||||
|  | ||||
| 	electronLocalshortcut.register(mainWindow, 'CommandOrControl+]', () => { | ||||
| 		page.send('forward'); | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| // eslint-disable-next-line max-params | ||||
| app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { | ||||
| 	event.preventDefault(); | ||||
| 	callback(true); | ||||
| }); | ||||
|  | ||||
| app.on('window-all-closed', () => { | ||||
| 	// Unregister all the shortcuts so that they don't interfare with other apps | ||||
| 	electronLocalshortcut.unregisterAll(mainWindow); | ||||
| }); | ||||
|  | ||||
| app.on('activate', () => { | ||||
| 	if (!mainWindow) { | ||||
| 		mainWindow = createMainWindow(); | ||||
| @@ -163,8 +144,6 @@ app.on('ready', () => { | ||||
|  | ||||
| 	const page = mainWindow.webContents; | ||||
|  | ||||
| 	registerLocalShortcuts(page); | ||||
|  | ||||
| 	page.on('dom-ready', () => { | ||||
| 		mainWindow.show(); | ||||
| 	}); | ||||
| @@ -172,6 +151,7 @@ app.on('ready', () => { | ||||
| 	page.once('did-frame-finish-load', () => { | ||||
| 		// Initate auto-updates on MacOS and Windows | ||||
| 		appUpdater(); | ||||
| 		crashHandler(); | ||||
| 	}); | ||||
|  | ||||
| 	electron.powerMonitor.on('resume', () => { | ||||
| @@ -229,30 +209,15 @@ app.on('ready', () => { | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('register-server-tab-shortcut', (event, index) => { | ||||
| 		electronLocalshortcut.register(mainWindow, `CommandOrControl+${index}`, () => { | ||||
| 		// Array index == Shown index - 1 | ||||
| 		page.send('switch-server-tab', index - 1); | ||||
| 	}); | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('local-shortcuts', (event, enable) => { | ||||
| 		if (enable) { | ||||
| 			registerLocalShortcuts(page); | ||||
| 		} else { | ||||
| 			electronLocalshortcut.unregisterAll(mainWindow); | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	ipcMain.on('toggleAutoLauncher', (event, AutoLaunchValue) => { | ||||
| 		setAutoLaunch(AutoLaunchValue); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| app.on('will-quit', () => { | ||||
| 	// Unregister all the shortcuts so that they don't interfare with other apps | ||||
| 	electronLocalshortcut.unregisterAll(mainWindow); | ||||
| }); | ||||
|  | ||||
| app.on('before-quit', () => { | ||||
| 	isQuitting = true; | ||||
| }); | ||||
|   | ||||
| @@ -37,7 +37,7 @@ class AppMenu { | ||||
| 			accelerator: 'CommandOrControl+R', | ||||
| 			click(item, focusedWindow) { | ||||
| 				if (focusedWindow) { | ||||
| 					AppMenu.sendAction('reload-viewer'); | ||||
| 					AppMenu.sendAction('reload-current-viewer'); | ||||
| 				} | ||||
| 			} | ||||
| 		}, { | ||||
| @@ -173,7 +173,7 @@ class AppMenu { | ||||
| 		return [{ | ||||
| 			label: `${app.getName()}`, | ||||
| 			submenu: [{ | ||||
| 				label: 'Zulip Desktop', | ||||
| 				label: 'About Zulip', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('open-about'); | ||||
| @@ -273,7 +273,7 @@ class AppMenu { | ||||
| 		return [{ | ||||
| 			label: 'File', | ||||
| 			submenu: [{ | ||||
| 				label: 'Zulip Desktop', | ||||
| 				label: 'About Zulip', | ||||
| 				click(item, focusedWindow) { | ||||
| 					if (focusedWindow) { | ||||
| 						AppMenu.sendAction('open-about'); | ||||
| @@ -370,10 +370,20 @@ class AppMenu { | ||||
| 	} | ||||
|  | ||||
| 	static resetAppSettings() { | ||||
| 		const getAppPath = path.join(app.getPath('appData'), appName, 'window-state.json'); | ||||
| 		// We save App's settings/configurations in following files | ||||
| 		const settingFiles = ['window-state.json', 'domain.json', 'settings.json']; | ||||
|  | ||||
| 		fs.unlink(getAppPath, () => { | ||||
| 			setTimeout(() => AppMenu.sendAction('clear-app-data'), 1000); | ||||
| 		settingFiles.forEach(settingFileName => { | ||||
| 			const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName); | ||||
| 			fs.access(getSettingFilesPath, error => { | ||||
| 				if (error) { | ||||
| 					console.log(error); | ||||
| 				} else { | ||||
| 					fs.unlink(getSettingFilesPath, () => { | ||||
| 						AppMenu.sendAction('clear-app-data'); | ||||
| 					}); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "zulip", | ||||
|   "productName": "Zulip", | ||||
|   "version": "1.4.0", | ||||
|   "version": "1.6.0-beta", | ||||
|   "description": "Zulip Desktop App", | ||||
|   "license": "Apache-2.0", | ||||
|   "email": "<svnitakash@gmail.com>", | ||||
| @@ -27,12 +27,10 @@ | ||||
|     "InstantMessaging" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "electron-debug": "1.4.0", | ||||
|     "electron-is-dev": "0.3.0", | ||||
|     "electron-localshortcut": "2.0.2", | ||||
|     "electron-log": "2.2.7", | ||||
|     "electron-spellchecker": "1.2.0", | ||||
|     "electron-updater": "2.8.9", | ||||
|     "electron-spellchecker": "1.1.2", | ||||
|     "electron-updater": "2.16.2", | ||||
|     "node-json-db": "0.7.3", | ||||
|     "request": "2.81.0", | ||||
|     "wurl": "2.5.0", | ||||
|   | ||||
| @@ -18,7 +18,7 @@ body { | ||||
|     background-position: center; | ||||
| } | ||||
|  | ||||
| #sidebar { | ||||
| .toggle-sidebar { | ||||
|     background: #222c31; | ||||
|     width: 54px; | ||||
|     padding: 27px 0 20px 0; | ||||
| @@ -27,6 +27,21 @@ body { | ||||
|     flex-direction: column; | ||||
|     -webkit-app-region: drag; | ||||
|     overflow: hidden; | ||||
|     transition: all 0.5s ease; | ||||
| } | ||||
|  | ||||
| .toggle-sidebar div { | ||||
|     transition: all 0.5s ease-out; | ||||
| } | ||||
|  | ||||
| .sidebar-hide { | ||||
|     width: 0; | ||||
|     transition: all 0.8s ease; | ||||
| } | ||||
|  | ||||
| .sidebar-hide div { | ||||
|     transform: translateX(-100%); | ||||
|     transition: all 0.6s ease-out; | ||||
| } | ||||
|  | ||||
| @font-face { | ||||
| @@ -258,6 +273,33 @@ webview:focus { | ||||
|     right: 68px; | ||||
| } | ||||
|  | ||||
| #add-server-tooltip, | ||||
| .server-tooltip { | ||||
|     font-family: 'arial'; | ||||
|     background: #222c31; | ||||
|     left: 56px; | ||||
|     padding: 10px 20px; | ||||
|     position: fixed; | ||||
|     margin-top: 8px; | ||||
|     z-index: 5000 !important; | ||||
|     color: #fff; | ||||
|     border-radius: 4px; | ||||
|     text-align: center; | ||||
|     width: max-content; | ||||
|     font-size: 14px; | ||||
| } | ||||
|  | ||||
| #add-server-tooltip:after, | ||||
| .server-tooltip:after { | ||||
|     content: " "; | ||||
|     border-top: 8px solid transparent; | ||||
|     border-bottom: 8px solid transparent; | ||||
|     border-right: 8px solid #222c31; | ||||
|     position: absolute; | ||||
|     top: 10px; | ||||
|     left: -5px; | ||||
| } | ||||
|  | ||||
| #collapse-button { | ||||
|     bottom: 30px; | ||||
|     left: 20px; | ||||
| @@ -285,7 +327,9 @@ webview:focus { | ||||
|     display: none !important; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Full screen Popup container  */ | ||||
|  | ||||
| .popup .popuptext { | ||||
|     visibility: hidden; | ||||
|     background-color: #555; | ||||
|   | ||||
| @@ -12,33 +12,33 @@ body { | ||||
| } | ||||
|  | ||||
| kbd { | ||||
|     padding: 0.1em 0.6em; | ||||
|     padding: 0.3em 0.8em; | ||||
|     border: 1px solid #ccc; | ||||
|     font-size: 12px; | ||||
|     font-family: Arial,Helvetica,sans-serif; | ||||
|     background-color: #f7f7f7; | ||||
|     color: #333; | ||||
|     -moz-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset; | ||||
|     -webkit-box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset; | ||||
|     box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2),0 0 0 2px #ffffff inset; | ||||
|     -moz-border-radius: 3px; | ||||
|     -webkit-border-radius: 3px; | ||||
|     border-radius: 3px; | ||||
|     font-size: 15px; | ||||
|     font-family: Courier New, Courier, monospace; | ||||
|     background-color: #383430; | ||||
|     color: #ededed; | ||||
|     display: inline-block; | ||||
|     margin: 0 0.1em; | ||||
|     text-shadow: 0 1px 0 #fff; | ||||
|     line-height: 1.4; | ||||
|     font-weight: bold; | ||||
|     white-space: nowrap; | ||||
| } | ||||
|  | ||||
| table, th, td { | ||||
|     border: 1px solid #ddd; | ||||
|     border-collapse: collapse; | ||||
|     color: #383430; | ||||
| } | ||||
|  | ||||
| table { width: 85%; } | ||||
| table {  | ||||
|     width: 100%; | ||||
|     margin-top: 18px; | ||||
|     margin-bottom: 18px; | ||||
|  } | ||||
|  | ||||
| table tr:nth-child(even) { background-color: #f7eee6; } | ||||
|  | ||||
| table tr:nth-child(odd) { background-color: #fff8ef; } | ||||
|  | ||||
| table tr:nth-child(even) { background-color: #f2f2f2; } | ||||
|  | ||||
| td { padding: 5px; } | ||||
|  | ||||
| @@ -56,6 +56,11 @@ td:nth-child(odd) { | ||||
|        url(../fonts/MaterialIcons-Regular.ttf) format('truetype'); | ||||
| } | ||||
|  | ||||
| @font-face { | ||||
|   font-family: 'Montserrat'; | ||||
|   src: url(../fonts/Montserrat-Regular.ttf) format('truetype'); | ||||
| } | ||||
|  | ||||
| .material-icons { | ||||
|   font-family: 'Material Icons'; | ||||
|   font-weight: normal; | ||||
| @@ -78,7 +83,7 @@ td:nth-child(odd) { | ||||
| #content { | ||||
|     display: flex; | ||||
|     height: 100%; | ||||
|     font-family: sans-serif; | ||||
|     font-family: 'Montserrat'; | ||||
| } | ||||
|  | ||||
| #sidebar { | ||||
| @@ -116,7 +121,9 @@ td:nth-child(odd) { | ||||
|  | ||||
| #settings-header { | ||||
|     font-size: 22px; | ||||
|     color: #5c6166; | ||||
|     color: #222c31; | ||||
|     font-weight: bold; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| #settings-container { | ||||
| @@ -134,7 +141,8 @@ td:nth-child(odd) { | ||||
| .title { | ||||
|     padding: 4px 0 6px 0; | ||||
|     font-weight: bold; | ||||
|     color: #1e1e1e; | ||||
|     color: #222c31; | ||||
|     text-transform: uppercase; | ||||
| } | ||||
|  | ||||
| .sub-title { | ||||
| @@ -210,7 +218,6 @@ img.server-info-icon { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     padding: 0 10px; | ||||
|     border-radius: 2px; | ||||
|     margin-right: 10px; | ||||
| } | ||||
|  | ||||
| @@ -242,9 +249,8 @@ img.server-info-icon { | ||||
|     padding: 12px 30px; | ||||
|     margin: 10px 0 20px 0; | ||||
|     background: #fff; | ||||
|     border-radius: 2px; | ||||
|     width: 540px; | ||||
|     box-shadow: 1px 2px 4px #bcbcbc; | ||||
|     width: 70%; | ||||
|     border-left: 8px solid #bcbcbc; | ||||
| } | ||||
|  | ||||
| .hidden { | ||||
| @@ -253,15 +259,19 @@ img.server-info-icon { | ||||
| } | ||||
|  | ||||
| .red { | ||||
|     color: #ef5350; | ||||
|     background: #ffebee; | ||||
|     border: 1px solid #ef5350; | ||||
|     color: #ffffff; | ||||
|     background: #ef5350; | ||||
|     padding: 3px; | ||||
|     padding-right: 10px; | ||||
|     padding-left: 10px; | ||||
| } | ||||
|  | ||||
| .green { | ||||
|     color: #388E3C; | ||||
|     background: #E8F5E9; | ||||
|     border: 1px solid #388E3C; | ||||
| .blue { | ||||
|     color: #ffffff; | ||||
|     background: #4EBFAC; | ||||
|     padding: 3px; | ||||
|     padding-right: 10px; | ||||
|     padding-left: 10px; | ||||
| } | ||||
|  | ||||
| .grey { | ||||
| @@ -272,9 +282,10 @@ img.server-info-icon { | ||||
|  | ||||
| .setting-row { | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: space-between; | ||||
|     width: 100%; | ||||
|     margin: 6px 0; | ||||
|     margin: 6px; | ||||
| } | ||||
|  | ||||
| .code { | ||||
| @@ -289,26 +300,90 @@ i.open-tab-button { | ||||
|  | ||||
| .reset-data-button { | ||||
|     display: inline-block; | ||||
|     border: none; | ||||
|     padding: 10px; | ||||
|     width: 120px; | ||||
|     cursor: pointer; | ||||
|     font-size: 11px; | ||||
|     font-size: 13px; | ||||
|     transition: background-color 0.2s ease; | ||||
|     text-decoration: none; | ||||
|  | ||||
| } | ||||
|  | ||||
| .reset-data-button:hover { | ||||
|     background-color: #32a692; | ||||
|     background-color: #3c9f8d; | ||||
|     color: #fff; | ||||
| } | ||||
|  | ||||
| #open-shortcuts-url { | ||||
| 		color: #08c; | ||||
| #server-info-container { | ||||
| 		min-height: calc(100% - 235px); | ||||
| } | ||||
|  | ||||
| #create-organization-container { | ||||
| 		font-size: 1.15em; | ||||
| 		margin-bottom: 15px; | ||||
| } | ||||
|  | ||||
| #create-organization-container i { | ||||
| 	position: relative; | ||||
| 	top: 3px; | ||||
| } | ||||
|  | ||||
| #open-create-org-link { | ||||
| 		color: #666; | ||||
| 		cursor: pointer; | ||||
| 		text-decoration: none; | ||||
| } | ||||
|  | ||||
| #open-shortcuts-url:hover { | ||||
| #open-create-org-link:hover { | ||||
| 		color: #005580;; | ||||
| 		text-decoration: underline; | ||||
| } | ||||
|  | ||||
| .toggle { | ||||
|   position: absolute; | ||||
|   margin-left: -9999px; | ||||
|   visibility: hidden; | ||||
| } | ||||
| .toggle + label { | ||||
|   display: block; | ||||
|   position: relative; | ||||
|   cursor: pointer; | ||||
|   outline: none; | ||||
|   user-select: none; | ||||
| } | ||||
|  | ||||
| input.toggle-round + label { | ||||
|   padding: 2px; | ||||
|   width: 50px; | ||||
|   height: 25px; | ||||
|   background-color: #dddddd; | ||||
|   border-radius: 25px; | ||||
| } | ||||
| input.toggle-round + label:before, | ||||
| input.toggle-round + label:after { | ||||
|   display: block; | ||||
|   position: absolute; | ||||
|   top: 2px; | ||||
|   left: 2px; | ||||
|   bottom: 2px; | ||||
|   content: ""; | ||||
| } | ||||
| input.toggle-round + label:before { | ||||
|   right: 2px; | ||||
|   background-color: #f1f1f1; | ||||
|   border-radius: 25px; | ||||
|   transition: background 0.4s; | ||||
| } | ||||
| input.toggle-round + label:after { | ||||
|   width: 25px; | ||||
|   height: 25px; | ||||
|   background-color: #fff; | ||||
|   border-radius: 100%; | ||||
|   transition: margin 0.4s; | ||||
| } | ||||
| input.toggle-round:checked + label:before { | ||||
|   background-color: #4EBFAC; | ||||
| } | ||||
| input.toggle-round:checked + label:after { | ||||
|   margin-left: 25px; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								app/renderer/fonts/Montserrat-Regular.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/renderer/fonts/Montserrat-Regular.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -8,6 +8,7 @@ const {ipcRenderer} = require('electron'); | ||||
| class ServerTab extends Tab { | ||||
| 	template() { | ||||
| 		return `<div class="tab"> | ||||
| 					<div class="server-tooltip" style="display:none"></div> | ||||
| 					<div class="server-tab-badge"></div> | ||||
| 					<div class="server-tab"> | ||||
| 					<img class="server-icons" src='${this.props.icon}'/> | ||||
|   | ||||
| @@ -21,6 +21,8 @@ class Tab extends BaseComponent { | ||||
|  | ||||
| 	registerListeners() { | ||||
| 		this.$el.addEventListener('click', this.props.onClick); | ||||
| 		this.$el.addEventListener('mouseover', this.props.onHover); | ||||
| 		this.$el.addEventListener('mouseout', this.props.onHoverOut); | ||||
| 	} | ||||
|  | ||||
| 	isLoading() { | ||||
|   | ||||
| @@ -6,7 +6,7 @@ const fs = require('fs'); | ||||
| const DomainUtil = require(__dirname + '/../utils/domain-util.js'); | ||||
| const SystemUtil = require(__dirname + '/../utils/system-util.js'); | ||||
| const LinkUtil = require(__dirname + '/../utils/link-util.js'); | ||||
| const {shell} = require('electron').remote; | ||||
| const { shell, app } = require('electron').remote; | ||||
|  | ||||
| const BaseComponent = require(__dirname + '/../components/base.js'); | ||||
|  | ||||
| @@ -60,6 +60,16 @@ class WebView extends BaseComponent { | ||||
| 			this.props.onTitleChange(); | ||||
| 		}); | ||||
|  | ||||
| 		this.$el.addEventListener('page-favicon-updated', event => { | ||||
| 			const { favicons } = event; | ||||
| 			// This returns a string of favicons URL. If there is a PM counts in unread messages then the URL would be like | ||||
| 			// https://chat.zulip.org/static/images/favicon/favicon-pms.png | ||||
| 			if (favicons[0].indexOf('favicon-pms') > 0 && process.platform === 'darwin') { | ||||
| 				// This api is only supported on macOS | ||||
| 				app.dock.setBadge('●'); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		this.$el.addEventListener('dom-ready', () => { | ||||
| 			if (this.props.role === 'server') { | ||||
| 				this.$el.classList.add('onload'); | ||||
|   | ||||
| @@ -21,8 +21,11 @@ class ServerManagerView { | ||||
| 		this.$settingsButton = $actionsContainer.querySelector('#settings-action'); | ||||
| 		this.$webviewsContainer = document.getElementById('webviews-container'); | ||||
|  | ||||
| 		this.$addServerTooltip = document.getElementById('add-server-tooltip'); | ||||
| 		this.$reloadTooltip = $actionsContainer.querySelector('#reload-tooltip'); | ||||
| 		this.$settingsTooltip = $actionsContainer.querySelector('#setting-tooltip'); | ||||
| 		this.$serverIconTooltip = document.getElementsByClassName('server-tooltip'); | ||||
|  | ||||
| 		this.$sidebar = document.getElementById('sidebar'); | ||||
|  | ||||
| 		this.$fullscreenPopup = document.getElementById('fullscreen-popup'); | ||||
| @@ -75,12 +78,11 @@ class ServerManagerView { | ||||
| 				DomainUtil.updateSavedServer(servers[i].url, i); | ||||
| 				this.activateTab(i); | ||||
| 			} | ||||
| 			this.activateTab(0); | ||||
| 			// Open last active tab | ||||
| 			this.activateTab(ConfigUtil.getConfigItem('lastActiveTab')); | ||||
| 		} else { | ||||
| 			this.openSettings('Servers'); | ||||
| 		} | ||||
|  | ||||
| 		ipcRenderer.send('local-shortcuts', true); | ||||
| 	} | ||||
|  | ||||
| 	initServer(server, index) { | ||||
| @@ -88,8 +90,10 @@ class ServerManagerView { | ||||
| 			role: 'server', | ||||
| 			icon: server.icon, | ||||
| 			$root: this.$tabsContainer, | ||||
| 			onClick: this.activateTab.bind(this, index), | ||||
| 			onClick: this.activateLastTab.bind(this, index), | ||||
| 			index, | ||||
| 			onHover: this.onHover.bind(this, index, server.alias), | ||||
| 			onHoverOut: this.onHoverOut.bind(this, index), | ||||
| 			webview: new WebView({ | ||||
| 				$root: this.$webviewsContainer, | ||||
| 				index, | ||||
| @@ -117,6 +121,7 @@ class ServerManagerView { | ||||
| 			this.openSettings('General'); | ||||
| 		}); | ||||
|  | ||||
| 		this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip); | ||||
| 		this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip); | ||||
| 		this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip); | ||||
| 	} | ||||
| @@ -130,6 +135,15 @@ class ServerManagerView { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	onHover(index, serverName) { | ||||
| 		this.$serverIconTooltip[index].innerHTML = serverName; | ||||
| 		this.$serverIconTooltip[index].removeAttribute('style'); | ||||
| 	} | ||||
|  | ||||
| 	onHoverOut(index) { | ||||
| 		this.$serverIconTooltip[index].style.display = 'none'; | ||||
| 	} | ||||
|  | ||||
| 	openFunctionalTab(tabProps) { | ||||
| 		if (this.functionalTabs[tabProps.name] !== undefined) { | ||||
| 			this.activateTab(this.functionalTabs[tabProps.name]); | ||||
| @@ -188,8 +202,15 @@ class ServerManagerView { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	activateLastTab(index) { | ||||
| 		// Open last active tab | ||||
| 		ConfigUtil.setConfigItem('lastActiveTab', index); | ||||
| 		// Open all the tabs in background | ||||
| 		this.activateTab(index); | ||||
| 	} | ||||
|  | ||||
| 	activateTab(index, hideOldTab = true) { | ||||
| 		if (this.tabs[index].webview.loading) { | ||||
| 		if (!this.tabs[index]) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| @@ -235,16 +256,24 @@ class ServerManagerView { | ||||
| 		// Clear DOM elements | ||||
| 		this.$tabsContainer.innerHTML = ''; | ||||
| 		this.$webviewsContainer.innerHTML = ''; | ||||
|  | ||||
| 		// Destroy shortcuts | ||||
| 		ipcRenderer.send('local-shortcuts', false); | ||||
| 	} | ||||
|  | ||||
| 	reloadView() { | ||||
| 		// Save and remember the index of last active tab so that we can use it later | ||||
| 		const lastActiveTab = this.tabs[this.activeTabIndex].props.index; | ||||
| 		ConfigUtil.setConfigItem('lastActiveTab', lastActiveTab); | ||||
|  | ||||
| 		// Destroy the current view and re-initiate it | ||||
| 		this.destroyView(); | ||||
| 		this.initTabs(); | ||||
| 	} | ||||
|  | ||||
| 	// This will trigger when pressed CTRL/CMD + R [WIP] | ||||
| 	// It won't reload the current view properly when you add/delete a server. | ||||
| 	reloadCurrentView() { | ||||
| 		this.$reloadButton.click(); | ||||
| 	} | ||||
|  | ||||
| 	updateBadge() { | ||||
| 		let messageCountAll = 0; | ||||
| 		for (let i = 0; i < this.tabs.length; i++) { | ||||
| @@ -260,9 +289,9 @@ class ServerManagerView { | ||||
|  | ||||
| 	toggleSidebar(show) { | ||||
| 		if (show) { | ||||
| 			this.$sidebar.classList.remove('hidden'); | ||||
| 			this.$sidebar.classList.remove('sidebar-hide'); | ||||
| 		} else { | ||||
| 			this.$sidebar.classList.add('hidden'); | ||||
| 			this.$sidebar.classList.add('sidebar-hide'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -295,7 +324,9 @@ class ServerManagerView { | ||||
|  | ||||
| 		ipcRenderer.on('open-about', this.openAbout.bind(this)); | ||||
|  | ||||
| 		ipcRenderer.on('reload-viewer', this.reloadView.bind(this)); | ||||
| 		ipcRenderer.on('reload-viewer', this.reloadView.bind(this, this.tabs[this.activeTabIndex].props.index)); | ||||
|  | ||||
| 		ipcRenderer.on('reload-current-viewer', this.reloadCurrentView.bind(this)); | ||||
|  | ||||
| 		ipcRenderer.on('hard-reload', () => { | ||||
| 			ipcRenderer.send('reload-full-app'); | ||||
|   | ||||
| @@ -15,10 +15,16 @@ const NativeNotification = window.Notification; | ||||
| class baseNotification extends NativeNotification { | ||||
| 	constructor(title, opts) { | ||||
| 		opts.silent = ConfigUtil.getConfigItem('silent') || false; | ||||
| 		// calling super without passing arguments will fail to constuct Notification | ||||
| 		// and will result in no notification. It's a hack (not an ideal way) for deleting the window notification | ||||
| 		ConfigUtil.getConfigItem('showNotification') ? super(title, opts) : super(); // eslint-disable-line no-unused-expressions | ||||
| 		super(title, opts); | ||||
| 	} | ||||
| 	static requestPermission() { | ||||
| 		return; // eslint-disable-line no-useless-return | ||||
| 	} | ||||
| 	// Override default Notification permission | ||||
| 	static get permission() { | ||||
| 		return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied'; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| window.Notification = baseNotification; | ||||
|  | ||||
|   | ||||
| @@ -45,7 +45,7 @@ class BadgeSettings { | ||||
|  | ||||
| 	updateOverlayIcon(messageCount, mainWindow) { | ||||
| 		if (!mainWindow.isFocused()) { | ||||
| 			mainWindow.flashFrame(true); | ||||
| 			mainWindow.flashFrame(ConfigUtil.getConfigItem('flashTaskbarOnMessage')); | ||||
| 		} | ||||
| 		if (messageCount === 0) { | ||||
| 			mainWindow.setOverlayIcon(null, ''); | ||||
|   | ||||
| @@ -19,14 +19,20 @@ class BaseSection extends BaseComponent { | ||||
| 	generateOptionTemplate(settingOption) { | ||||
| 		if (settingOption) { | ||||
| 			return ` | ||||
| 				<div class="action green"> | ||||
| 					<span>On</span> | ||||
| 				<div class="action"> | ||||
| 					<div class="switch"> | ||||
| 					  <input class="toggle toggle-round" type="checkbox" checked> | ||||
| 					  <label></label> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			`; | ||||
| 		} else { | ||||
| 			return ` | ||||
| 				<div class="action red"> | ||||
| 					<span>Off</span> | ||||
| 				<div class="action"> | ||||
| 					<div class="switch"> | ||||
| 					  <input class="toggle toggle-round" type="checkbox"> | ||||
| 					  <label></label> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 			`; | ||||
| 		} | ||||
|   | ||||
							
								
								
									
										37
									
								
								app/renderer/js/pages/preference/create-new-org.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								app/renderer/js/pages/preference/create-new-org.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const BaseComponent = require(__dirname + '/../../components/base.js'); | ||||
| const shell = require('electron').shell; | ||||
|  | ||||
| class CreateOrganziation extends BaseComponent { | ||||
| 	constructor(props) { | ||||
| 		super(); | ||||
| 		this.props = props; | ||||
| 	} | ||||
|  | ||||
| 	template() { | ||||
| 		return ` | ||||
| 			<div class="setting-row"> | ||||
| 				<div class="setting-description"> | ||||
| 					<span id="open-create-org-link">Create a new organization on zulipchat.com<i class="material-icons open-tab-button">open_in_new</i></span> | ||||
| 				</div> | ||||
| 				<div class="setting-control"></div> | ||||
| 			</div> | ||||
| 		`; | ||||
| 	} | ||||
|  | ||||
| 	init() { | ||||
| 		this.props.$root.innerHTML = this.template(); | ||||
| 		this.openCreateNewOrgExternalLink(); | ||||
| 	} | ||||
|  | ||||
| 	openCreateNewOrgExternalLink() { | ||||
| 		const link = 'https://zulipchat.com/beta/'; | ||||
| 		const externalCreateNewOrgEl = document.getElementById('open-create-org-link'); | ||||
| 		externalCreateNewOrgEl.addEventListener('click', () => { | ||||
| 			shell.openExternal(link); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| module.exports = CreateOrganziation; | ||||
| @@ -32,6 +32,10 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-description">Show app unread badge</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="flash-taskbar-option" style= "display:${process.platform === 'win32' ? '' : 'none'}"> | ||||
| 						<div class="setting-description">Flash taskbar on new message</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="title">Desktop Notification</div> | ||||
| 				<div class="settings-card"> | ||||
| @@ -50,10 +54,6 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-description">Get beta updates</div> | ||||
| 						<div class="setting-control"></div> | ||||
| 					</div> | ||||
| 					<div class="setting-row" id="autoupdate-option"> | ||||
| 					<div class="setting-description">Automatically install new updates</div> | ||||
| 					<div class="setting-control"></div> | ||||
| 				</div> | ||||
| 				</div> | ||||
| 				<div class="title">Functionality</div> | ||||
|                 <div class="settings-card"> | ||||
| @@ -61,13 +61,17 @@ class GeneralSection extends BaseSection { | ||||
| 						<div class="setting-description">Start app at login</div> | ||||
| 						<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-control"></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 green">Reset App Data</button> | ||||
| 						<button class="reset-data-button blue">Reset App Data</button> | ||||
| 					</div> | ||||
| 				</div> | ||||
|             </div> | ||||
| @@ -78,13 +82,19 @@ class GeneralSection extends BaseSection { | ||||
| 		this.props.$root.innerHTML = this.template(); | ||||
| 		this.updateTrayOption(); | ||||
| 		this.updateBadgeOption(); | ||||
| 		this.updateUpdateOption(); | ||||
| 		this.updateAutoUpdateOption(); | ||||
| 		this.updateSilentOption(); | ||||
| 		this.updateUpdateOption(); | ||||
| 		this.updateSidebarOption(); | ||||
| 		this.updateStartAtLoginOption(); | ||||
| 		this.updateResetDataOption(); | ||||
| 		this.showDesktopNotification(); | ||||
| 		this.enableSpellchecker(); | ||||
|  | ||||
| 		// Platform specific settings | ||||
| 		// Flashing taskbar on Windows | ||||
| 		if (process.platform === 'win32') { | ||||
| 			this.updateFlashTaskbar(); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	updateTrayOption() { | ||||
| @@ -113,6 +123,18 @@ class GeneralSection extends BaseSection { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateFlashTaskbar() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#flash-taskbar-option .setting-control'), | ||||
| 			value: ConfigUtil.getConfigItem('flashTaskbarOnMessage', true), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('flashTaskbarOnMessage'); | ||||
| 				ConfigUtil.setConfigItem('flashTaskbarOnMessage', newValue); | ||||
| 				this.updateFlashTaskbar(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateUpdateOption() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#betaupdate-option .setting-control'), | ||||
| @@ -125,18 +147,6 @@ class GeneralSection extends BaseSection { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateAutoUpdateOption() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#autoupdate-option .setting-control'), | ||||
| 			value: ConfigUtil.getConfigItem('autoUpdate', true), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('autoUpdate'); | ||||
| 				ConfigUtil.setConfigItem('autoUpdate', newValue); | ||||
| 				this.updateAutoUpdateOption(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	updateSilentOption() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#silent-option .setting-control'), | ||||
| @@ -187,6 +197,18 @@ class GeneralSection extends BaseSection { | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	enableSpellchecker() { | ||||
| 		this.generateSettingOption({ | ||||
| 			$element: document.querySelector('#enable-spellchecker-option .setting-control'), | ||||
| 			value: ConfigUtil.getConfigItem('enableSpellchecker', true), | ||||
| 			clickHandler: () => { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('enableSpellchecker'); | ||||
| 				ConfigUtil.setConfigItem('enableSpellchecker', newValue); | ||||
| 				this.enableSpellchecker(); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	clearAppDataDialog() { | ||||
| 		const clearAppDataMessage = 'By clicking proceed you will be removing all added accounts and preferences from Zulip. When the application restarts, it will be as if you are starting Zulip for the first time.'; | ||||
| 		const getAppPath = path.join(app.getPath('appData'), app.getName()); | ||||
|   | ||||
| @@ -34,7 +34,7 @@ class NetworkSection extends BaseSection { | ||||
| 							<input class="setting-input-value" placeholder="e.g. foobar.com"/> | ||||
| 						</div> | ||||
| 						<div class="setting-row"> | ||||
| 							<div class="action green" id="proxy-save-action"> | ||||
| 							<div class="action blue" id="proxy-save-action"> | ||||
| 								<i class="material-icons">check_box</i> | ||||
| 								<span>Save</span> | ||||
| 							</div> | ||||
|   | ||||
| @@ -14,12 +14,12 @@ class NewServerForm extends BaseComponent { | ||||
| 			<div class="settings-card"> | ||||
| 				<div class="server-info-right"> | ||||
| 					<div class="server-info-row"> | ||||
| 						<input class="setting-input-value" autofocus placeholder="Entert the URL of your Zulip organization..."/> | ||||
| 						<input class="setting-input-value" autofocus placeholder="Enter the URL of your Zulip organization..."/> | ||||
| 					</div> | ||||
| 					<div class="server-info-row"> | ||||
| 						<div class="action green server-save-action"> | ||||
| 						<div class="action blue server-save-action"> | ||||
| 							<i class="material-icons">check_box</i> | ||||
| 							<span>Save</span> | ||||
| 							<span>Add</span> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
|   | ||||
| @@ -3,6 +3,8 @@ | ||||
| const BaseComponent = require(__dirname + '/js/components/base.js'); | ||||
| const { ipcRenderer } = require('electron'); | ||||
|  | ||||
| const ConfigUtil = require(__dirname + '/js/utils/config-util.js'); | ||||
|  | ||||
| const Nav = require(__dirname + '/js/pages/preference/nav.js'); | ||||
| const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js'); | ||||
| const GeneralSection = require(__dirname + '/js/pages/preference/general-section.js'); | ||||
| @@ -25,6 +27,7 @@ class PreferenceView extends BaseComponent { | ||||
|  | ||||
| 		this.setDefaultView(); | ||||
| 		this.registerIpcs(); | ||||
| 		this.setDefaultSettings(); | ||||
| 	} | ||||
|  | ||||
| 	setDefaultView() { | ||||
| @@ -36,6 +39,30 @@ class PreferenceView extends BaseComponent { | ||||
| 		this.handleNavigation(nav); | ||||
| 	} | ||||
|  | ||||
| 	// Settings are initialized only when user clicks on General/Server/Network section settings | ||||
| 	// In case, user doesn't visit these section, those values set to be null automatically | ||||
| 	// This will make sure the default settings are correctly set to either true or false | ||||
| 	setDefaultSettings() { | ||||
| 		// Default settings which should be respected | ||||
| 		const settingOptions = { | ||||
| 			trayIcon: true, | ||||
| 			useProxy: false, | ||||
| 			showSidebar: true, | ||||
| 			badgeOption: true, | ||||
| 			startAtLogin: false, | ||||
| 			enableSpellchecker: true, | ||||
| 			showNotification: true, | ||||
| 			betaUpdate: false, | ||||
| 			silent: false | ||||
| 		}; | ||||
|  | ||||
| 		for (const i in settingOptions) { | ||||
| 			if (ConfigUtil.getConfigItem(i) === null) { | ||||
| 				ConfigUtil.setConfigItem(i, settingOptions[i]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	handleNavigation(navItem) { | ||||
| 		this.nav.select(navItem); | ||||
| 		switch (navItem) { | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const BaseSection = require(__dirname + '/base-section.js'); | ||||
| const DomainUtil = require(__dirname + '/../../utils/domain-util.js'); | ||||
| const ServerInfoForm = require(__dirname + '/server-info-form.js'); | ||||
| const NewServerForm = require(__dirname + '/new-server-form.js'); | ||||
| const CreateOrganziation = require(__dirname + '/create-new-org.js'); | ||||
|  | ||||
| class ServersSection extends BaseSection { | ||||
| 	constructor(props) { | ||||
| @@ -18,6 +19,7 @@ class ServersSection extends BaseSection { | ||||
| 				<div id="new-server-container"></div> | ||||
| 				<div class="title" id="existing-servers"></div> | ||||
| 				<div id="server-info-container"></div> | ||||
| 				<div id="create-organization-container"></div> | ||||
| 			</div> | ||||
| 		`; | ||||
| 	} | ||||
| @@ -41,6 +43,9 @@ class ServersSection extends BaseSection { | ||||
| 		this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Existing Servers'; | ||||
| 		this.initNewServerForm(); | ||||
|  | ||||
| 		this.$createOrganizationContainer = document.getElementById('create-organization-container'); | ||||
| 		this.initCreateNewOrganization(); | ||||
|  | ||||
| 		for (let i = 0; i < servers.length; i++) { | ||||
| 			new ServerInfoForm({ | ||||
| 				$root: this.$serverInfoContainer, | ||||
| @@ -51,6 +56,12 @@ class ServersSection extends BaseSection { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	initCreateNewOrganization() { | ||||
| 		new CreateOrganziation({ | ||||
| 			$root: this.$createOrganizationContainer | ||||
| 		}).init(); | ||||
| 	} | ||||
|  | ||||
| 	initNewServerForm() { | ||||
| 		new NewServerForm({ | ||||
| 			$root: this.$newServerContainer, | ||||
|   | ||||
| @@ -1,7 +1,10 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| const { ipcRenderer } = require('electron'); | ||||
| const { spellChecker } = require('./spellchecker'); | ||||
| const SetupSpellChecker = require('./spellchecker'); | ||||
|  | ||||
| const ConfigUtil = require(__dirname + '/utils/config-util.js'); | ||||
|  | ||||
| // eslint-disable-next-line import/no-unassigned-import | ||||
| require('./notification'); | ||||
|  | ||||
| @@ -32,8 +35,15 @@ process.once('loaded', () => { | ||||
|  | ||||
| // To prevent failing this script on linux we need to load it after the document loaded | ||||
| document.addEventListener('DOMContentLoaded', () => { | ||||
| 	// 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 | ||||
| 	spellChecker(); | ||||
| 		SetupSpellChecker.init(); | ||||
| 	} | ||||
|  | ||||
| 	// redirect users to network troubleshooting page | ||||
| 	const getRestartButton = document.querySelector('.restart_get_events_button'); | ||||
| @@ -43,3 +53,10 @@ document.addEventListener('DOMContentLoaded', () => { | ||||
| 		}); | ||||
| 	} | ||||
| }); | ||||
|  | ||||
| // Clean up spellchecker events after you navigate away from this page; | ||||
| // otherwise, you may experience errors | ||||
| window.addEventListener('beforeunload', () => { | ||||
| 	SetupSpellChecker.unsubscribeSpellChecker(); | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -2,28 +2,55 @@ | ||||
|  | ||||
| const { SpellCheckHandler, ContextMenuListener, ContextMenuBuilder } = require('electron-spellchecker'); | ||||
|  | ||||
| function spellChecker() { | ||||
| 	// Implement spellcheck using electron api | ||||
| 	window.spellCheckHandler = new SpellCheckHandler(); | ||||
| 	window.spellCheckHandler.attachToInput(); | ||||
| const ConfigUtil = require(__dirname + '/utils/config-util.js'); | ||||
|  | ||||
| 	// Start off as US English | ||||
| 	window.spellCheckHandler.switchLanguage('en-US'); | ||||
| class SetupSpellChecker { | ||||
| 	init() { | ||||
| 		if (ConfigUtil.getConfigItem('enableSpellchecker')) { | ||||
| 			this.enableSpellChecker(); | ||||
| 		} | ||||
| 		this.enableContextMenu(); | ||||
| 	} | ||||
|  | ||||
| 	const contextMenuBuilder = new ContextMenuBuilder(window.spellCheckHandler); | ||||
| 	const contextMenuListener = new ContextMenuListener(info => { | ||||
| 	enableSpellChecker() { | ||||
| 		try { | ||||
| 			this.SpellCheckHandler = new SpellCheckHandler(); | ||||
| 		} catch (err) { | ||||
| 			console.log(err); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	enableContextMenu() { | ||||
| 		if (this.SpellCheckHandler) { | ||||
| 			this.SpellCheckHandler.attachToInput(); | ||||
|  | ||||
| 			const userLanguage = ConfigUtil.getConfigItem('spellcheckerLanguage'); | ||||
|  | ||||
| 			// eslint-disable-next-line no-unused-expressions | ||||
| 			process.platform === 'darwin' ? | ||||
| 				// On macOS, spellchecker fails to auto-detect the lanugage user is typing in | ||||
| 				// that's why we need to mention it explicitly | ||||
| 				this.SpellCheckHandler.switchLanguage(userLanguage) : | ||||
| 				// On Linux and Windows, spellchecker can automatically detects the language the user is typing in | ||||
| 				// and silently switches on the fly; thus we can start off as US English | ||||
| 				this.SpellCheckHandler.switchLanguage('en-US'); | ||||
| 		} | ||||
|  | ||||
| 		const contextMenuBuilder = new ContextMenuBuilder(this.SpellCheckHandler); | ||||
| 		this.contextMenuListener = new ContextMenuListener(info => { | ||||
| 			contextMenuBuilder.showPopupMenu(info); | ||||
| 		}); | ||||
|  | ||||
| 	// Clean up events after you navigate away from this page; | ||||
| 	// otherwise, you may experience errors | ||||
| 	window.addEventListener('beforeunload', () => { | ||||
| 	// eslint-disable-next-line no-undef | ||||
| 		spellCheckHandler.unsubscribe(); | ||||
| 		contextMenuListener.unsubscribe(); | ||||
| 	}); | ||||
| 	} | ||||
|  | ||||
| module.exports = { | ||||
| 	spellChecker | ||||
| }; | ||||
| 	unsubscribeSpellChecker() { | ||||
| 		// eslint-disable-next-line no-undef | ||||
| 		if (this.SpellCheckHandler) { | ||||
| 			this.SpellCheckHandler.unsubscribe(); | ||||
| 		} | ||||
| 		if (this.contextMenuListener) { | ||||
| 			this.contextMenuListener.unsubscribe(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| module.exports = new SetupSpellChecker(); | ||||
|   | ||||
| @@ -110,8 +110,10 @@ class DomainUtil { | ||||
| 		return new Promise((resolve, reject) => { | ||||
| 			request(checkDomain, (error, response) => { | ||||
| 				const certsError = | ||||
| 					['Error: self signed certificate', | ||||
| 						'Error: unable to verify the first certificate' | ||||
| 					[ | ||||
| 						'Error: self signed certificate', | ||||
| 						'Error: unable to verify the first certificate', | ||||
| 						'Error: unable to get local issuer certificate' | ||||
| 					]; | ||||
| 				if (!error && response.statusCode !== 404) { | ||||
| 					// Correct | ||||
| @@ -152,7 +154,9 @@ class DomainUtil { | ||||
| 						}); | ||||
| 					} | ||||
| 				} else { | ||||
| 					reject('Not a valid Zulip server'); | ||||
| 					const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \ | ||||
| 					(1) you can connect to that URL in a web browser and \n (2) if you need a proxy to connect to the Internet, that you've configured your proxy in the Network settings`; | ||||
| 					reject(invalidZulipServerError); | ||||
| 				} | ||||
| 			}); | ||||
| 		}); | ||||
| @@ -166,7 +170,9 @@ class DomainUtil { | ||||
| 					const data = JSON.parse(response.body); | ||||
| 					if (data.hasOwnProperty('realm_icon') && data.realm_icon) { | ||||
| 						resolve({ | ||||
| 							icon: data.realm_uri + data.realm_icon, | ||||
| 							// Some Zulip Servers use absolute URL for server icon whereas others use relative URL | ||||
| 							// Following check handles both the cases | ||||
| 							icon: data.realm_icon.startsWith('/') ? data.realm_uri + data.realm_icon : data.realm_icon, | ||||
| 							url: data.realm_uri, | ||||
| 							alias: data.realm_name | ||||
| 						}); | ||||
|   | ||||
| @@ -13,13 +13,14 @@ | ||||
|     <div class="popup"> | ||||
|       <span class="popuptext hidden" id="fullscreen-popup"></span> | ||||
|   </div> | ||||
|   <div id="sidebar"> | ||||
|   <div id="sidebar" class="toggle-sidebar"> | ||||
|     <div id="view-controls-container"> | ||||
|       <div id="tabs-container"></div> | ||||
|       <div id="add-tab" class="tab functional-tab"> | ||||
|         <div class="server-tab" id="add-action"> | ||||
|           <i class="material-icons">add</i> | ||||
|         </div> | ||||
|         <span id="add-server-tooltip" style="display:none">Add organization</span> | ||||
|       </div> | ||||
|     </div> | ||||
|     <div id="actions-container"> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@2x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@2x.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@3x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@3x.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@4x.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/resources/tray/trayosx@4x.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										10
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
|   "name": "zulip", | ||||
|   "productName": "Zulip", | ||||
|   "version": "1.4.0", | ||||
|   "version": "1.6.0-beta", | ||||
|   "main": "./app/main", | ||||
|   "description": "Zulip Desktop App", | ||||
|   "license": "Apache-2.0", | ||||
| @@ -20,6 +20,7 @@ | ||||
|   }, | ||||
|   "scripts": { | ||||
|     "start": "electron app --disable-http-cache", | ||||
|     "reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install", | ||||
|     "postinstall": "electron-builder install-app-deps", | ||||
|     "test": "xo", | ||||
|     "dev": "gulp dev", | ||||
| @@ -105,8 +106,8 @@ | ||||
|   "devDependencies": { | ||||
|     "assert": "1.4.1", | ||||
|     "devtron": "1.4.0", | ||||
|     "electron-builder": "19.27.3", | ||||
|     "electron": "1.6.11", | ||||
|     "electron-builder": "19.45.5", | ||||
|     "electron": "1.6.14", | ||||
|     "electron-connect": "0.6.2", | ||||
|     "gulp": "3.9.1", | ||||
|     "gulp-mocha": "4.3.1", | ||||
| @@ -114,7 +115,8 @@ | ||||
|     "chai": "4.1.1", | ||||
|     "spectron": "3.7.2", | ||||
|     "xo": "0.18.2", | ||||
|     "pre-commit": "1.2.2" | ||||
|     "pre-commit": "1.2.2", | ||||
|     "electron-debug": "1.4.0" | ||||
|   }, | ||||
|   "xo": { | ||||
|     "parserOptions": { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user