mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-10-31 12:03:39 +00:00 
			
		
		
		
	Compare commits
	
		
			19 Commits
		
	
	
		
			git-linter
			...
			setup-karm
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 292e1b4d15 | ||
|  | 64a12dca09 | ||
|  | d1d19327ba | ||
|  | dc6a1c3c0b | ||
|  | 63005d20ca | ||
|  | 600e8acdfa | ||
|  | 3f01774d0a | ||
|  | 808dffd2ed | ||
|  | 6b51f6d9a6 | ||
|  | 36342145da | ||
|  | 7b639129b3 | ||
|  | 89ae4585e6 | ||
|  | 469b827425 | ||
|  | 7feb0e4280 | ||
|  | 8cd8bac6a7 | ||
|  | ce1e902e89 | ||
|  | 3baa2c6ca5 | ||
|  | 2114ccfb40 | ||
|  | c6f1f99fe9 | 
							
								
								
									
										8
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/ISSUE_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| --- |  | ||||||
| <!-- Please Include: --> |  | ||||||
| - **Operating System**: |  | ||||||
|   - [ ] Windows |  | ||||||
|   - [ ] Linux/Ubutnu |  | ||||||
|   - [ ] macOS |  | ||||||
| - **Clear steps to reproduce the issue**: |  | ||||||
| - **Relevant error messages and/or screenshots**: |  | ||||||
							
								
								
									
										16
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,16 +0,0 @@ | |||||||
| --- |  | ||||||
| <!-- |  | ||||||
| Remove the fields that are not appropriate |  | ||||||
| Please include: |  | ||||||
| --> |  | ||||||
|  |  | ||||||
| **What's this PR do?** |  | ||||||
|  |  | ||||||
| **Any background context you want to provide?** |  | ||||||
|  |  | ||||||
| **Screenshots?** |  | ||||||
|  |  | ||||||
| **You have tested this PR on:** |  | ||||||
|   - [ ] Windows |  | ||||||
|   - [ ] Linux/Ubuntu |  | ||||||
|   - [ ] macOS |  | ||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -26,6 +26,7 @@ yarn-error.log* | |||||||
| config.gypi | config.gypi | ||||||
|  |  | ||||||
| # Test generated files | # Test generated files | ||||||
| tests/package.json | tests/e2e/package.json | ||||||
|  | coverage | ||||||
|  |  | ||||||
| .python-version | .python-version | ||||||
|   | |||||||
| @@ -28,13 +28,7 @@ cache: | |||||||
|   - app/node_modules |   - app/node_modules | ||||||
|  |  | ||||||
| script: | script: | ||||||
|   - echo $TRAVIS_COMMIT_RANGE | - npm run travis | ||||||
|   - echo ${TRAVIS_COMMIT_RANGE/.../..} |  | ||||||
|   - echo "test" |  | ||||||
|   - git log -4 |  | ||||||
|   - node ./tools/gitlint --ci-mode |  | ||||||
|   - npm run travis |  | ||||||
|  |  | ||||||
| notifications: | notifications: | ||||||
|   webhooks: |   webhooks: | ||||||
|     urls: |     urls: | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								ISSUE_TEMPLATE.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								ISSUE_TEMPLATE.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | --- | ||||||
|  | Please include:  | ||||||
|  | - `Operating System` | ||||||
|  | - `Clear steps to reproduce the issue` | ||||||
|  | - `Relevant error messages and/or screenshots` | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								README.md
									
									
									
									
									
								
							| @@ -12,12 +12,19 @@ Please see [installation guide](https://zulipchat.com/help/desktop-app-install-g | |||||||
|  |  | ||||||
| # Features | # Features | ||||||
| * Sign in to multiple teams | * Sign in to multiple teams | ||||||
| * Desktop Notifications with inline reply support | * Native desktop Notifications | ||||||
| * Multilanguage SpellChecker | * SpellChecker | ||||||
| * OSX/Win/Linux installers | * OSX/Win/Linux installers | ||||||
| * Automatic Updates (macOS/Windows/Linux) | * Automatic Updates (macOS/Windows) | ||||||
| * Keyboard shortcuts | * Keyboard shortcuts | ||||||
|  |  | ||||||
|  | Description            | Keys | ||||||
|  | -----------------------| ----------------------- | ||||||
|  | Default shortcuts      | <kbd>Cmd/Ctrl</kbd> <kbd>k</kbd> | ||||||
|  | Manage Zulip Servers    | <kbd>Cmd/Ctrl</kbd> <kbd>,</kbd> | ||||||
|  | Back                   | <kbd>Cmd/Ctrl</kbd> <kbd>[</kbd> | ||||||
|  | Forward                | <kbd>Cmd/Ctrl</kbd> <kbd>]</kbd> | ||||||
|  |  | ||||||
| # Development | # Development | ||||||
| Please see our [development guide](./development.md) to get started and run app locally. | Please see our [development guide](./development.md) to get started and run app locally. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  | const fs = require('fs'); | ||||||
| const { app, dialog } = require('electron'); | const { app, dialog } = require('electron'); | ||||||
| const { autoUpdater } = require('electron-updater'); | const { autoUpdater } = require('electron-updater'); | ||||||
| const isDev = require('electron-is-dev'); | const isDev = require('electron-is-dev'); | ||||||
| @@ -14,6 +15,10 @@ function appUpdater() { | |||||||
| 	// Create Logs directory | 	// Create Logs directory | ||||||
| 	const LogsDir = `${app.getPath('userData')}/Logs`; | 	const LogsDir = `${app.getPath('userData')}/Logs`; | ||||||
|  |  | ||||||
|  | 	if (!fs.existsSync(LogsDir)) { | ||||||
|  | 		fs.mkdirSync(LogsDir); | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Log whats happening | 	// Log whats happening | ||||||
| 	const log = require('electron-log'); | 	const log = require('electron-log'); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -12,7 +12,6 @@ const { setAutoLaunch } = require('./startup'); | |||||||
| const { app, ipcMain } = electron; | const { app, ipcMain } = electron; | ||||||
|  |  | ||||||
| const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js'); | const BadgeSettings = require('./../renderer/js/pages/preference/badge-settings.js'); | ||||||
| const ConfigUtil = require('./../renderer/js/utils/config-util.js'); |  | ||||||
|  |  | ||||||
| // Adds debug features like hotkeys for triggering dev tools and reload | // Adds debug features like hotkeys for triggering dev tools and reload | ||||||
| // in development mode | // in development mode | ||||||
| @@ -82,11 +81,7 @@ function createMainWindow() { | |||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	win.once('ready-to-show', () => { | 	win.once('ready-to-show', () => { | ||||||
| 		if (ConfigUtil.getConfigItem('startMinimized')) { | 		win.show(); | ||||||
| 			win.minimize(); |  | ||||||
| 		} else { |  | ||||||
| 			win.show(); |  | ||||||
| 		} |  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	win.loadURL(mainURL); | 	win.loadURL(mainURL); | ||||||
| @@ -129,9 +124,6 @@ function createMainWindow() { | |||||||
| 	return win; | 	return win; | ||||||
| } | } | ||||||
|  |  | ||||||
| // Decrease load on GPU (experimental) |  | ||||||
| app.disableHardwareAcceleration(); |  | ||||||
|  |  | ||||||
| // eslint-disable-next-line max-params | // eslint-disable-next-line max-params | ||||||
| app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { | app.on('certificate-error', (event, webContents, url, error, certificate, callback) => { | ||||||
| 	event.preventDefault(); | 	event.preventDefault(); | ||||||
| @@ -153,11 +145,7 @@ app.on('ready', () => { | |||||||
| 	const page = mainWindow.webContents; | 	const page = mainWindow.webContents; | ||||||
|  |  | ||||||
| 	page.on('dom-ready', () => { | 	page.on('dom-ready', () => { | ||||||
| 		if (ConfigUtil.getConfigItem('startMinimized')) { | 		mainWindow.show(); | ||||||
| 			mainWindow.minimize(); |  | ||||||
| 		} else { |  | ||||||
| 			mainWindow.show(); |  | ||||||
| 		} |  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	page.once('did-frame-finish-load', () => { | 	page.once('did-frame-finish-load', () => { | ||||||
| @@ -167,8 +155,7 @@ app.on('ready', () => { | |||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	electron.powerMonitor.on('resume', () => { | 	electron.powerMonitor.on('resume', () => { | ||||||
| 		mainWindow.reload(); | 		page.send('reload-viewer'); | ||||||
| 		page.send('destroytray'); |  | ||||||
| 	}); | 	}); | ||||||
|  |  | ||||||
| 	ipcMain.on('focus-app', () => { | 	ipcMain.on('focus-app', () => { | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ class AppMenu { | |||||||
| 			role: 'togglefullscreen' | 			role: 'togglefullscreen' | ||||||
| 		}, { | 		}, { | ||||||
| 			label: 'Zoom In', | 			label: 'Zoom In', | ||||||
| 			accelerator: process.platform === 'darwin' ? 'Command+Plus' : 'Control+=', | 			accelerator: 'CommandOrControl+=', | ||||||
| 			click(item, focusedWindow) { | 			click(item, focusedWindow) { | ||||||
| 				if (focusedWindow) { | 				if (focusedWindow) { | ||||||
| 					AppMenu.sendAction('zoomIn'); | 					AppMenu.sendAction('zoomIn'); | ||||||
| @@ -121,7 +121,7 @@ class AppMenu { | |||||||
| 				shell.openExternal('https://zulipchat.com/help/'); | 				shell.openExternal('https://zulipchat.com/help/'); | ||||||
| 			} | 			} | ||||||
| 		}, { | 		}, { | ||||||
| 			label: `${appName + ' Desktop'} - ${app.getVersion()}`, | 			label: `${appName + 'Desktop'} - ${app.getVersion()}`, | ||||||
| 			enabled: false | 			enabled: false | ||||||
| 		}, { | 		}, { | ||||||
| 			label: 'Report an Issue...', | 			label: 'Report an Issue...', | ||||||
|   | |||||||
							
								
								
									
										31
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										31
									
								
								app/package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -101,12 +101,6 @@ | |||||||
|         "tweetnacl": "0.14.5" |         "tweetnacl": "0.14.5" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "bindings": { |  | ||||||
|       "version": "1.3.0", |  | ||||||
|       "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.3.0.tgz", |  | ||||||
|       "integrity": "sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==", |  | ||||||
|       "optional": true |  | ||||||
|     }, |  | ||||||
|     "bluebird": { |     "bluebird": { | ||||||
|       "version": "3.5.1", |       "version": "3.5.1", | ||||||
|       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", |       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", | ||||||
| @@ -324,12 +318,6 @@ | |||||||
|       "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz", |       "resolved": "https://registry.npmjs.org/event-kit/-/event-kit-2.4.0.tgz", | ||||||
|       "integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A==" |       "integrity": "sha512-ZXd9jxUoc/f/zdLdR3OUcCzT84WnpaNWefquLyE125akIC90sDs8S3T/qihliuVsaj7Osc0z8lLL2fjooE9Z4A==" | ||||||
|     }, |     }, | ||||||
|     "event-target-shim": { |  | ||||||
|       "version": "1.1.1", |  | ||||||
|       "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-1.1.1.tgz", |  | ||||||
|       "integrity": "sha1-qG5e5r2qFgVEddp5fM3fDFVphJE=", |  | ||||||
|       "optional": true |  | ||||||
|     }, |  | ||||||
|     "extend": { |     "extend": { | ||||||
|       "version": "3.0.1", |       "version": "3.0.1", | ||||||
|       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", |       "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", | ||||||
| @@ -634,25 +622,6 @@ | |||||||
|         "mkdirp": "0.5.1" |         "mkdirp": "0.5.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "node-mac-notifier": { |  | ||||||
|       "version": "0.0.13", |  | ||||||
|       "resolved": "https://registry.npmjs.org/node-mac-notifier/-/node-mac-notifier-0.0.13.tgz", |  | ||||||
|       "integrity": "sha1-1kt27RgfR5XURFui060Nb3KY9+I=", |  | ||||||
|       "optional": true, |  | ||||||
|       "requires": { |  | ||||||
|         "bindings": "1.3.0", |  | ||||||
|         "event-target-shim": "1.1.1", |  | ||||||
|         "node-uuid": "1.4.8" |  | ||||||
|       }, |  | ||||||
|       "dependencies": { |  | ||||||
|         "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 |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "oauth-sign": { |     "oauth-sign": { | ||||||
|       "version": "0.8.2", |       "version": "0.8.2", | ||||||
|       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", |       "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "name": "zulip", |   "name": "zulip", | ||||||
|   "productName": "Zulip", |   "productName": "Zulip", | ||||||
|   "version": "1.8.1", |   "version": "1.7.0", | ||||||
|   "description": "Zulip Desktop App", |   "description": "Zulip Desktop App", | ||||||
|   "license": "Apache-2.0", |   "license": "Apache-2.0", | ||||||
|   "copyright": "Kandra Labs, Inc.", |   "copyright": "Kandra Labs, Inc.", | ||||||
| @@ -30,13 +30,10 @@ | |||||||
|     "electron-is-dev": "0.3.0", |     "electron-is-dev": "0.3.0", | ||||||
|     "electron-log": "2.2.7", |     "electron-log": "2.2.7", | ||||||
|     "electron-spellchecker": "1.1.2", |     "electron-spellchecker": "1.1.2", | ||||||
|     "electron-updater": "2.18.2", |  | ||||||
|     "electron-window-state": "4.1.1", |     "electron-window-state": "4.1.1", | ||||||
|  |     "electron-updater": "2.16.2", | ||||||
|     "node-json-db": "0.7.3", |     "node-json-db": "0.7.3", | ||||||
|     "request": "2.81.0", |     "request": "2.81.0", | ||||||
|     "wurl": "2.5.0" |     "wurl": "2.5.0" | ||||||
|   }, |  | ||||||
|   "optionalDependencies": { |  | ||||||
|     "node-mac-notifier": "0.0.13" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -263,13 +263,7 @@ img.server-info-icon { | |||||||
|     margin: 10px 0 20px 0; |     margin: 10px 0 20px 0; | ||||||
|     background: #fff; |     background: #fff; | ||||||
|     width: 70%; |     width: 70%; | ||||||
|     transition: all 0.2s; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .settings-card:hover { |  | ||||||
|     border-left: 8px solid #bcbcbc; |     border-left: 8px solid #bcbcbc; | ||||||
|     box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), |  | ||||||
|                 0 2px 0px 0px rgba(0,0,0,0.12); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| .hidden { | .hidden { | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ const Tab = require(__dirname + '/../components/tab.js'); | |||||||
|  |  | ||||||
| class FunctionalTab extends Tab { | class FunctionalTab extends Tab { | ||||||
| 	template() { | 	template() { | ||||||
| 		return `<div class="tab functional-tab" data-tab-id="${this.props.tabIndex}"> | 		return `<div class="tab functional-tab"> | ||||||
| 					<div class="server-tab-badge close-button"> | 					<div class="server-tab-badge close-button"> | ||||||
| 						<i class="material-icons">close</i> | 						<i class="material-icons">close</i> | ||||||
| 					</div> | 					</div> | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ const {ipcRenderer} = require('electron'); | |||||||
|  |  | ||||||
| class ServerTab extends Tab { | class ServerTab extends Tab { | ||||||
| 	template() { | 	template() { | ||||||
| 		return `<div class="tab" data-tab-id="${this.props.tabIndex}"> | 		return `<div class="tab"> | ||||||
| 					<div class="server-tooltip" style="display:none"></div> | 					<div class="server-tooltip" style="display:none"></div> | ||||||
| 					<div class="server-tab-badge"></div> | 					<div class="server-tab-badge"></div> | ||||||
| 					<div class="server-tab"> | 					<div class="server-tab"> | ||||||
|   | |||||||
| @@ -4,14 +4,12 @@ const path = require('path'); | |||||||
| const fs = require('fs'); | const fs = require('fs'); | ||||||
|  |  | ||||||
| const DomainUtil = require(__dirname + '/../utils/domain-util.js'); | const DomainUtil = require(__dirname + '/../utils/domain-util.js'); | ||||||
| const ConfigUtil = require(__dirname + '/../utils/config-util.js'); |  | ||||||
| const SystemUtil = require(__dirname + '/../utils/system-util.js'); | const SystemUtil = require(__dirname + '/../utils/system-util.js'); | ||||||
| const LinkUtil = require(__dirname + '/../utils/link-util.js'); | const LinkUtil = require(__dirname + '/../utils/link-util.js'); | ||||||
| const { shell, app } = require('electron').remote; | const { shell, app } = require('electron').remote; | ||||||
|  |  | ||||||
| const BaseComponent = require(__dirname + '/../components/base.js'); | const BaseComponent = require(__dirname + '/../components/base.js'); | ||||||
|  |  | ||||||
| const shouldSilentWebview = ConfigUtil.getConfigItem('silent'); |  | ||||||
| class WebView extends BaseComponent { | class WebView extends BaseComponent { | ||||||
| 	constructor(props) { | 	constructor(props) { | ||||||
| 		super(); | 		super(); | ||||||
| @@ -26,7 +24,6 @@ class WebView extends BaseComponent { | |||||||
| 	template() { | 	template() { | ||||||
| 		return `<webview | 		return `<webview | ||||||
| 					class="disabled" | 					class="disabled" | ||||||
| 					data-tab-id="${this.props.tabIndex}" |  | ||||||
| 					src="${this.props.url}" | 					src="${this.props.url}" | ||||||
| 					${this.props.nodeIntegration ? 'nodeIntegration' : ''} | 					${this.props.nodeIntegration ? 'nodeIntegration' : ''} | ||||||
| 					disablewebsecurity | 					disablewebsecurity | ||||||
| @@ -57,12 +54,6 @@ class WebView extends BaseComponent { | |||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		if (shouldSilentWebview) { |  | ||||||
| 			this.$el.addEventListener('dom-ready', () => { |  | ||||||
| 				this.$el.setAudioMuted(true); |  | ||||||
| 			}); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		this.$el.addEventListener('page-title-updated', event => { | 		this.$el.addEventListener('page-title-updated', event => { | ||||||
| 			const { title } = event; | 			const { title } = event; | ||||||
| 			this.badgeCount = this.getBadgeCount(title); | 			this.badgeCount = this.getBadgeCount(title); | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								app/renderer/js/console.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								app/renderer/js/console.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | const NodeConsole = require('console').Console; | ||||||
|  | const fs = require('fs'); | ||||||
|  |  | ||||||
|  | const { app } = require('electron').remote; | ||||||
|  | const isDev = require('electron-is-dev'); | ||||||
|  |  | ||||||
|  | const browserConsole = console; | ||||||
|  | const logDir = `${app.getPath('userData')}/Logs`; | ||||||
|  | if (!fs.existsSync(logDir)) { | ||||||
|  | 	fs.mkdirSync(logDir); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function customConsole(opts, type, ...args) { | ||||||
|  | 	const { nodeConsole, timestamp } = opts; | ||||||
|  | 	if (timestamp) { | ||||||
|  | 		args.unshift(timestamp()); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!isDev) { | ||||||
|  | 		const nodeConsoleLog = nodeConsole[type] || nodeConsole.log; | ||||||
|  | 		nodeConsoleLog.apply(null, args); | ||||||
|  | 	} | ||||||
|  | 	browserConsole[type].apply(null, args); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function getTimestamp() { | ||||||
|  | 	const date = new Date(); | ||||||
|  | 	const timestamp = | ||||||
|  | 		`${date.getMonth()}/${date.getDate()} ` + | ||||||
|  | 		`${date.getMinutes()}:${date.getSeconds()}`; | ||||||
|  | 	return timestamp; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function setConsoleProto(type) { | ||||||
|  | 	Object.defineProperty(this, type, { | ||||||
|  | 		value(...args) { | ||||||
|  | 			const { timestamp, nodeConsole } = this; | ||||||
|  | 			const opts = { | ||||||
|  | 				timestamp, | ||||||
|  | 				nodeConsole | ||||||
|  | 			}; | ||||||
|  | 			customConsole.apply(null, [].concat(opts, type, args)); | ||||||
|  | 		} | ||||||
|  | 	}); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class Console { | ||||||
|  | 	constructor(opts = {}) { | ||||||
|  | 		let { timestamp, file } = opts; | ||||||
|  | 		file = `${logDir}/${file || 'console.log'}`; | ||||||
|  | 		if (timestamp === true) { | ||||||
|  | 			timestamp = getTimestamp; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		const fileStream = fs.createWriteStream(file); | ||||||
|  | 		const nodeConsole = new NodeConsole(fileStream); | ||||||
|  | 		this.nodeConsole = nodeConsole; | ||||||
|  | 		this.timestamp = timestamp; | ||||||
|  | 		this.setUpConsole(); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	setUpConsole() { | ||||||
|  | 		for (const type in browserConsole) { | ||||||
|  | 			setConsoleProto.call(this, type); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | module.exports = Console; | ||||||
| @@ -1,10 +1,10 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
|  | require(__dirname + '/js/tray.js'); | ||||||
| const { ipcRenderer, remote } = require('electron'); | const { ipcRenderer, remote } = require('electron'); | ||||||
|  |  | ||||||
| const { session } = remote; | const { session } = remote; | ||||||
|  |  | ||||||
| require(__dirname + '/js/tray.js'); |  | ||||||
| const DomainUtil = require(__dirname + '/js/utils/domain-util.js'); | const DomainUtil = require(__dirname + '/js/utils/domain-util.js'); | ||||||
| const WebView = require(__dirname + '/js/components/webview.js'); | const WebView = require(__dirname + '/js/components/webview.js'); | ||||||
| const ServerTab = require(__dirname + '/js/components/server-tab.js'); | const ServerTab = require(__dirname + '/js/components/server-tab.js'); | ||||||
| @@ -35,7 +35,6 @@ class ServerManagerView { | |||||||
| 		this.activeTabIndex = -1; | 		this.activeTabIndex = -1; | ||||||
| 		this.tabs = []; | 		this.tabs = []; | ||||||
| 		this.functionalTabs = {}; | 		this.functionalTabs = {}; | ||||||
| 		this.tabIndex = 0; |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	init() { | 	init() { | ||||||
| @@ -78,7 +77,6 @@ class ServerManagerView { | |||||||
| 			showSidebar: true, | 			showSidebar: true, | ||||||
| 			badgeOption: true, | 			badgeOption: true, | ||||||
| 			startAtLogin: false, | 			startAtLogin: false, | ||||||
| 			startMinimized: false, |  | ||||||
| 			enableSpellchecker: true, | 			enableSpellchecker: true, | ||||||
| 			showNotification: true, | 			showNotification: true, | ||||||
| 			betaUpdate: false, | 			betaUpdate: false, | ||||||
| @@ -86,13 +84,6 @@ class ServerManagerView { | |||||||
| 			lastActiveTab: 0 | 			lastActiveTab: 0 | ||||||
| 		}; | 		}; | ||||||
|  |  | ||||||
| 		// Platform specific settings |  | ||||||
|  |  | ||||||
| 		if (process.platform === 'win32') { |  | ||||||
| 			// Only available on Windows |  | ||||||
| 			settingOptions.flashTaskbarOnMessage = true; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for (const i in settingOptions) { | 		for (const i in settingOptions) { | ||||||
| 			if (ConfigUtil.getConfigItem(i) === null) { | 			if (ConfigUtil.getConfigItem(i) === null) { | ||||||
| 				ConfigUtil.setConfigItem(i, settingOptions[i]); | 				ConfigUtil.setConfigItem(i, settingOptions[i]); | ||||||
| @@ -121,20 +112,17 @@ class ServerManagerView { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	initServer(server, index) { | 	initServer(server, index) { | ||||||
| 		const tabIndex = this.getTabIndex(); |  | ||||||
| 		this.tabs.push(new ServerTab({ | 		this.tabs.push(new ServerTab({ | ||||||
| 			role: 'server', | 			role: 'server', | ||||||
| 			icon: server.icon, | 			icon: server.icon, | ||||||
| 			$root: this.$tabsContainer, | 			$root: this.$tabsContainer, | ||||||
| 			onClick: this.activateLastTab.bind(this, index), | 			onClick: this.activateLastTab.bind(this, index), | ||||||
| 			index, | 			index, | ||||||
| 			tabIndex, |  | ||||||
| 			onHover: this.onHover.bind(this, index, server.alias), | 			onHover: this.onHover.bind(this, index, server.alias), | ||||||
| 			onHoverOut: this.onHoverOut.bind(this, index), | 			onHoverOut: this.onHoverOut.bind(this, index), | ||||||
| 			webview: new WebView({ | 			webview: new WebView({ | ||||||
| 				$root: this.$webviewsContainer, | 				$root: this.$webviewsContainer, | ||||||
| 				index, | 				index, | ||||||
| 				tabIndex, |  | ||||||
| 				url: server.url, | 				url: server.url, | ||||||
| 				name: server.alias, | 				name: server.alias, | ||||||
| 				isActive: () => { | 				isActive: () => { | ||||||
| @@ -159,24 +147,11 @@ class ServerManagerView { | |||||||
| 			this.openSettings('General'); | 			this.openSettings('General'); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		const $serverImgs = document.querySelectorAll('.server-icons'); |  | ||||||
| 		$serverImgs.forEach($serverImg => { |  | ||||||
| 			$serverImg.addEventListener('error', () => { |  | ||||||
| 				$serverImg.src = 'img/icon.png'; |  | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip); | 		this.sidebarHoverEvent(this.$addServerButton, this.$addServerTooltip); | ||||||
| 		this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip); | 		this.sidebarHoverEvent(this.$settingsButton, this.$settingsTooltip); | ||||||
| 		this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip); | 		this.sidebarHoverEvent(this.$reloadButton, this.$reloadTooltip); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	getTabIndex() { |  | ||||||
| 		const currentIndex = this.tabIndex; |  | ||||||
| 		this.tabIndex++; |  | ||||||
| 		return currentIndex; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	sidebarHoverEvent(SidebarButton, SidebarTooltip) { | 	sidebarHoverEvent(SidebarButton, SidebarTooltip) { | ||||||
| 		SidebarButton.addEventListener('mouseover', () => { | 		SidebarButton.addEventListener('mouseover', () => { | ||||||
| 			SidebarTooltip.removeAttribute('style'); | 			SidebarTooltip.removeAttribute('style'); | ||||||
| @@ -203,19 +178,16 @@ class ServerManagerView { | |||||||
|  |  | ||||||
| 		this.functionalTabs[tabProps.name] = this.tabs.length; | 		this.functionalTabs[tabProps.name] = this.tabs.length; | ||||||
|  |  | ||||||
| 		const tabIndex = this.getTabIndex(); |  | ||||||
| 		this.tabs.push(new FunctionalTab({ | 		this.tabs.push(new FunctionalTab({ | ||||||
| 			role: 'function', | 			role: 'function', | ||||||
| 			materialIcon: tabProps.materialIcon, | 			materialIcon: tabProps.materialIcon, | ||||||
| 			$root: this.$tabsContainer, | 			$root: this.$tabsContainer, | ||||||
| 			index: this.functionalTabs[tabProps.name], | 			index: this.functionalTabs[tabProps.name], | ||||||
| 			tabIndex, |  | ||||||
| 			onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]), | 			onClick: this.activateTab.bind(this, this.functionalTabs[tabProps.name]), | ||||||
| 			onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]), | 			onDestroy: this.destroyTab.bind(this, tabProps.name, this.functionalTabs[tabProps.name]), | ||||||
| 			webview: new WebView({ | 			webview: new WebView({ | ||||||
| 				$root: this.$webviewsContainer, | 				$root: this.$webviewsContainer, | ||||||
| 				index: this.functionalTabs[tabProps.name], | 				index: this.functionalTabs[tabProps.name], | ||||||
| 				tabIndex, |  | ||||||
| 				url: tabProps.url, | 				url: tabProps.url, | ||||||
| 				name: tabProps.name, | 				name: tabProps.name, | ||||||
| 				isActive: () => { | 				isActive: () => { | ||||||
| @@ -283,27 +255,6 @@ class ServerManagerView { | |||||||
| 			tabs: this.tabs, | 			tabs: this.tabs, | ||||||
| 			activeTabIndex: this.activeTabIndex | 			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('toogle-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(); |  | ||||||
| 					}); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	destroyTab(name, index) { | 	destroyTab(name, index) { | ||||||
| @@ -436,18 +387,6 @@ class ServerManagerView { | |||||||
| 			this.$fullscreenPopup.classList.remove('show'); | 			this.$fullscreenPopup.classList.remove('show'); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		ipcRenderer.on('focus-webview-with-id', (event, webviewId) => { |  | ||||||
| 			const webviews = document.querySelectorAll('webview'); |  | ||||||
| 			webviews.forEach(webview => { |  | ||||||
| 				const currentId = webview.getWebContents().id; |  | ||||||
| 				const tabId = webview.getAttribute('data-tab-id'); |  | ||||||
| 				const concurrentTab = document.querySelector(`div[data-tab-id="${tabId}"]`); |  | ||||||
| 				if (currentId === webviewId) { |  | ||||||
| 					concurrentTab.click(); |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		ipcRenderer.on('render-taskbar-icon', (event, messageCount) => { | 		ipcRenderer.on('render-taskbar-icon', (event, messageCount) => { | ||||||
| 			// Create a canvas from unread messagecounts | 			// Create a canvas from unread messagecounts | ||||||
| 			function createOverlayIcon(messageCount) { | 			function createOverlayIcon(messageCount) { | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								app/renderer/js/notification.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								app/renderer/js/notification.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | 'use strict'; | ||||||
|  |  | ||||||
|  | const { remote } = require('electron'); | ||||||
|  |  | ||||||
|  | const ConfigUtil = require(__dirname + '/utils/config-util.js'); | ||||||
|  |  | ||||||
|  | const app = remote.app; | ||||||
|  |  | ||||||
|  | // From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid | ||||||
|  | // On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work. | ||||||
|  | app.setAppUserModelId('org.zulip.zulip-electron'); | ||||||
|  |  | ||||||
|  | const NativeNotification = window.Notification; | ||||||
|  |  | ||||||
|  | class baseNotification extends NativeNotification { | ||||||
|  | 	constructor(title, opts) { | ||||||
|  | 		opts.silent = ConfigUtil.getConfigItem('silent') || false; | ||||||
|  | 		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; | ||||||
|  |  | ||||||
| @@ -1,100 +0,0 @@ | |||||||
| 'use strict'; |  | ||||||
|  |  | ||||||
| const { ipcRenderer } = require('electron'); |  | ||||||
| const url = require('url'); |  | ||||||
| const MacNotifier = require('node-mac-notifier'); |  | ||||||
| const ConfigUtil = require('../utils/config-util'); |  | ||||||
| const { |  | ||||||
| 	appId, customReply, focusCurrentServer, parseReply, setupReply |  | ||||||
| } = require('./helpers'); |  | ||||||
|  |  | ||||||
| let replyHandler; |  | ||||||
| let clickHandler; |  | ||||||
| class DarwinNotification { |  | ||||||
| 	constructor(title, opts) { |  | ||||||
| 		const silent = ConfigUtil.getConfigItem('silent') || false; |  | ||||||
| 		const { host, protocol } = location; |  | ||||||
| 		const { icon } = opts; |  | ||||||
| 		const profilePic = url.resolve(`${protocol}//${host}`, icon); |  | ||||||
|  |  | ||||||
| 		this.tag = opts.tag; |  | ||||||
| 		const notification = new MacNotifier(title, Object.assign(opts, { |  | ||||||
| 			bundleId: appId, |  | ||||||
| 			canReply: true, |  | ||||||
| 			silent, |  | ||||||
| 			icon: profilePic |  | ||||||
| 		})); |  | ||||||
|  |  | ||||||
| 		notification.addEventListener('click', () => { |  | ||||||
| 			// focus to the server who sent the |  | ||||||
| 			// notification if not focused already |  | ||||||
| 			if (clickHandler) { |  | ||||||
| 				clickHandler(); |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			focusCurrentServer(); |  | ||||||
| 			ipcRenderer.send('focus-app'); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		notification.addEventListener('reply', this.notificationHandler); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static requestPermission() { |  | ||||||
| 		return; // eslint-disable-line no-useless-return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Override default Notification permission |  | ||||||
| 	static get permission() { |  | ||||||
| 		return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied'; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	set onreply(handler) { |  | ||||||
| 		replyHandler = handler; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	get onreply() { |  | ||||||
| 		return replyHandler; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	set onclick(handler) { |  | ||||||
| 		clickHandler = handler; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	get onclick() { |  | ||||||
| 		return clickHandler; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// not something that is common or |  | ||||||
| 	// used by zulip server but added to be |  | ||||||
| 	// future proff. |  | ||||||
| 	addEventListener(event, handler) { |  | ||||||
| 		if (event === 'click') { |  | ||||||
| 			clickHandler = handler; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (event === 'reply') { |  | ||||||
| 			replyHandler = handler; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	notificationHandler({ response }) { |  | ||||||
| 		response = parseReply(response); |  | ||||||
| 		focusCurrentServer(); |  | ||||||
| 		setupReply(this.tag); |  | ||||||
|  |  | ||||||
| 		if (replyHandler) { |  | ||||||
| 			replyHandler(response); |  | ||||||
| 			return; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		customReply(response); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// method specific to notification api |  | ||||||
| 	// used by zulip |  | ||||||
| 	close() { |  | ||||||
| 		return; // eslint-disable-line no-useless-return |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = DarwinNotification; |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| 'use strict'; |  | ||||||
|  |  | ||||||
| const { ipcRenderer } = require('electron'); |  | ||||||
| const ConfigUtil = require('../utils/config-util'); |  | ||||||
| const { focusCurrentServer } = require('./helpers'); |  | ||||||
|  |  | ||||||
| const NativeNotification = window.Notification; |  | ||||||
| class BaseNotification extends NativeNotification { |  | ||||||
| 	constructor(title, opts) { |  | ||||||
| 		opts.silent = true; |  | ||||||
| 		super(title, opts); |  | ||||||
|  |  | ||||||
| 		this.addEventListener('click', () => { |  | ||||||
| 			// focus to the server who sent the |  | ||||||
| 			// notification if not focused already |  | ||||||
| 			focusCurrentServer(); |  | ||||||
| 			ipcRenderer.send('focus-app'); |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	static requestPermission() { |  | ||||||
| 		return; // eslint-disable-line no-useless-return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Override default Notification permission |  | ||||||
| 	static get permission() { |  | ||||||
| 		return ConfigUtil.getConfigItem('showNotification') ? 'granted' : 'denied'; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = BaseNotification; |  | ||||||
| @@ -1,102 +0,0 @@ | |||||||
| const { remote } = require('electron'); |  | ||||||
|  |  | ||||||
| // Do not change this |  | ||||||
| const appId = 'org.zulip.zulip-electron'; |  | ||||||
|  |  | ||||||
| function checkElements(...elements) { |  | ||||||
| 	let status = true; |  | ||||||
| 	elements.forEach(element => { |  | ||||||
| 		if (element === null || element === undefined) { |  | ||||||
| 			status = false; |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 	return status; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function customReply(reply) { |  | ||||||
| 	// server does not support notification reply yet. |  | ||||||
| 	const buttonSelector = '.messagebox #send_controls button[type=submit]'; |  | ||||||
| 	const messageboxSelector = '.selected_message .messagebox .messagebox-border .messagebox-content'; |  | ||||||
| 	const textarea = document.querySelector('#compose-textarea'); |  | ||||||
| 	const messagebox = document.querySelector(messageboxSelector); |  | ||||||
| 	const sendButton = document.querySelector(buttonSelector); |  | ||||||
|  |  | ||||||
| 	// sanity check for old server versions |  | ||||||
| 	const elementsExists = checkElements(textarea, messagebox, sendButton); |  | ||||||
| 	if (!elementsExists) { |  | ||||||
| 		return; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	textarea.value = reply; |  | ||||||
| 	messagebox.click(); |  | ||||||
| 	sendButton.click(); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const currentWindow = remote.getCurrentWindow(); |  | ||||||
| const webContents = remote.getCurrentWebContents(); |  | ||||||
| const webContentsId = webContents.id; |  | ||||||
|  |  | ||||||
| // this function will focus the server that sent |  | ||||||
| // the notification. Main function implemented in main.js |  | ||||||
| function focusCurrentServer() { |  | ||||||
| 	currentWindow.send('focus-webview-with-id', webContentsId); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // this function parses the reply from to notification |  | ||||||
| // making it easier to reply from notification eg |  | ||||||
| // @username in reply will be converted to @**username** |  | ||||||
| // #stream in reply will be converted to #**stream** |  | ||||||
| // bot mentions are not yet supported |  | ||||||
| function parseReply(reply) { |  | ||||||
| 	const usersDiv = document.querySelectorAll('#user_presences li'); |  | ||||||
| 	const streamHolder = document.querySelectorAll('#stream_filters li'); |  | ||||||
| 	const users = []; |  | ||||||
| 	const streams = []; |  | ||||||
|  |  | ||||||
| 	usersDiv.forEach(userRow => { |  | ||||||
| 		const anchor = userRow.querySelector('span a'); |  | ||||||
| 		if (anchor !== null) { |  | ||||||
| 			const user = `@${anchor.textContent.trim()}`; |  | ||||||
| 			const mention = `@**${user.replace(/^@/, '')}**`; |  | ||||||
| 			users.push([user, mention]); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	streamHolder.forEach(stream => { |  | ||||||
| 		const streamAnchor = stream.querySelector('div a'); |  | ||||||
| 		if (streamAnchor !== null) { |  | ||||||
| 			const streamName = `#${streamAnchor.textContent.trim()}`; |  | ||||||
| 			const streamMention = `#**${streamName.replace(/^#/, '')}**`; |  | ||||||
| 			streams.push([streamName, streamMention]); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	users.forEach(([user, mention]) => { |  | ||||||
| 		if (reply.includes(user)) { |  | ||||||
| 			const regex = new RegExp(user, 'g'); |  | ||||||
| 			reply = reply.replace(regex, mention); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	streams.forEach(([stream, streamMention]) => { |  | ||||||
| 		const regex = new RegExp(stream, 'g'); |  | ||||||
| 		reply = reply.replace(regex, streamMention); |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	reply = reply.replace(/\\n/, '\n'); |  | ||||||
| 	return reply; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function setupReply(id) { |  | ||||||
| 	const { narrow } = window; |  | ||||||
| 	narrow.by_subject(id, { trigger: 'notification' }); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
| 	appId, |  | ||||||
| 	checkElements, |  | ||||||
| 	customReply, |  | ||||||
| 	parseReply, |  | ||||||
| 	setupReply, |  | ||||||
| 	focusCurrentServer |  | ||||||
| }; |  | ||||||
| @@ -1,18 +0,0 @@ | |||||||
| 'use strict'; |  | ||||||
|  |  | ||||||
| const { |  | ||||||
|   remote: { app } |  | ||||||
| } = require('electron'); |  | ||||||
|  |  | ||||||
| const DefaultNotification = require('./default-notification'); |  | ||||||
| const { appId } = require('./helpers'); |  | ||||||
|  |  | ||||||
| // From https://github.com/felixrieseberg/electron-windows-notifications#appusermodelid |  | ||||||
| // On windows 8 we have to explicitly set the appUserModelId otherwise notification won't work. |  | ||||||
| app.setAppUserModelId(appId); |  | ||||||
|  |  | ||||||
| window.Notification = DefaultNotification; |  | ||||||
| if (process.platform === 'darwin') { |  | ||||||
| 	const DarwinNotification = require('./darwin-notifications'); |  | ||||||
| 	window.Notification = DarwinNotification; |  | ||||||
| } |  | ||||||
| @@ -1,10 +1,11 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const { ipcRenderer, remote } = require('electron'); |  | ||||||
|  | const { ipcRenderer } = require('electron'); | ||||||
|  | const { app, dialog } = require('electron').remote; | ||||||
|  |  | ||||||
| const fs = require('fs-extra'); | const fs = require('fs-extra'); | ||||||
|  |  | ||||||
| const { app, dialog } = remote; |  | ||||||
| const currentBrowserWindow = remote.getCurrentWindow(); |  | ||||||
| const BaseSection = require(__dirname + '/base-section.js'); | const BaseSection = require(__dirname + '/base-section.js'); | ||||||
| const ConfigUtil = require(__dirname + '/../../utils/config-util.js'); | const ConfigUtil = require(__dirname + '/../../utils/config-util.js'); | ||||||
|  |  | ||||||
| @@ -60,14 +61,10 @@ class GeneralSection extends BaseSection { | |||||||
| 						<div class="setting-description">Start app at login</div> | 						<div class="setting-description">Start app at login</div> | ||||||
| 						<div class="setting-control"></div> | 						<div class="setting-control"></div> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="setting-row" id="start-minimize-option"> |  | ||||||
| 						<div class="setting-description">Always start minimized</div> |  | ||||||
| 						<div class="setting-control"></div> |  | ||||||
| 					</div> |  | ||||||
| 					<div class="setting-row" id="enable-spellchecker-option"> | 					<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 class="setting-control"></div> | ||||||
| 					</div> | 				</div> | ||||||
| 				</div> | 				</div> | ||||||
| 				<div class="title">Reset Application Data</div> | 				<div class="title">Reset Application Data</div> | ||||||
|                 <div class="settings-card"> |                 <div class="settings-card"> | ||||||
| @@ -92,7 +89,6 @@ class GeneralSection extends BaseSection { | |||||||
| 		this.updateResetDataOption(); | 		this.updateResetDataOption(); | ||||||
| 		this.showDesktopNotification(); | 		this.showDesktopNotification(); | ||||||
| 		this.enableSpellchecker(); | 		this.enableSpellchecker(); | ||||||
| 		this.minimizeOnStart(); |  | ||||||
|  |  | ||||||
| 		// Platform specific settings | 		// Platform specific settings | ||||||
| 		// Flashing taskbar on Windows | 		// Flashing taskbar on Windows | ||||||
| @@ -159,7 +155,6 @@ class GeneralSection extends BaseSection { | |||||||
| 				const newValue = !ConfigUtil.getConfigItem('silent', true); | 				const newValue = !ConfigUtil.getConfigItem('silent', true); | ||||||
| 				ConfigUtil.setConfigItem('silent', newValue); | 				ConfigUtil.setConfigItem('silent', newValue); | ||||||
| 				this.updateSilentOption(); | 				this.updateSilentOption(); | ||||||
| 				currentBrowserWindow.send('toogle-silent', newValue); |  | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
| @@ -239,18 +234,6 @@ class GeneralSection extends BaseSection { | |||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	minimizeOnStart() { |  | ||||||
| 		this.generateSettingOption({ |  | ||||||
| 			$element: document.querySelector('#start-minimize-option .setting-control'), |  | ||||||
| 			value: ConfigUtil.getConfigItem('startMinimized', false), |  | ||||||
| 			clickHandler: () => { |  | ||||||
| 				const newValue = !ConfigUtil.getConfigItem('startMinimized'); |  | ||||||
| 				ConfigUtil.setConfigItem('startMinimized', newValue); |  | ||||||
| 				this.minimizeOnStart(); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| module.exports = GeneralSection; | module.exports = GeneralSection; | ||||||
|   | |||||||
| @@ -13,9 +13,9 @@ class NewServerForm extends BaseComponent { | |||||||
| 		return ` | 		return ` | ||||||
| 			<div class="settings-card"> | 			<div class="settings-card"> | ||||||
| 				<div class="server-info-right"> | 				<div class="server-info-right"> | ||||||
| 					<div class="title">URL of Zulip organization</div> | 					<div class="title">URL of your Zulip organization</div> | ||||||
| 					<div class="server-info-row"> | 					<div class="server-info-row"> | ||||||
| 						<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or chat.your-organization.com"/> | 						<input class="setting-input-value" autofocus placeholder="acme.zulipchat.com"/> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="server-info-row"> | 					<div class="server-info-row"> | ||||||
| 						<div class="action blue server-save-action"> | 						<div class="action blue server-save-action"> | ||||||
| @@ -43,13 +43,11 @@ class NewServerForm extends BaseComponent { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	submitFormHandler() { | 	submitFormHandler() { | ||||||
| 		this.$saveServerButton.children[1].innerHTML = 'Adding...'; |  | ||||||
| 		DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => { | 		DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => { | ||||||
| 			DomainUtil.addDomain(serverConf).then(() => { | 			DomainUtil.addDomain(serverConf).then(() => { | ||||||
| 				this.props.onChange(this.props.index); | 				this.props.onChange(this.props.index); | ||||||
| 			}); | 			}); | ||||||
| 		}, errorMessage => { | 		}, errorMessage => { | ||||||
| 			this.$saveServerButton.children[1].innerHTML = 'Add'; |  | ||||||
| 			alert(errorMessage); | 			alert(errorMessage); | ||||||
| 		}); | 		}); | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -69,27 +69,10 @@ class PreferenceView extends BaseComponent { | |||||||
| 		window.location.hash = `#${navItem}`; | 		window.location.hash = `#${navItem}`; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Handle toggling and reflect changes in preference page |  | ||||||
| 	handleToggle(elementName, state) { |  | ||||||
| 		const inputSelector = `#${elementName} .action .switch input`; |  | ||||||
| 		const input = document.querySelector(inputSelector); |  | ||||||
| 		if (input) { |  | ||||||
| 			input.checked = state; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	registerIpcs() { | 	registerIpcs() { | ||||||
| 		ipcRenderer.on('switch-settings-nav', (event, navItem) => { | 		ipcRenderer.on('switch-settings-nav', (event, navItem) => { | ||||||
| 			this.handleNavigation(navItem); | 			this.handleNavigation(navItem); | ||||||
| 		}); | 		}); | ||||||
|  |  | ||||||
| 		ipcRenderer.on('toggle-sidebar', (event, state) => { |  | ||||||
| 			this.handleToggle('sidebar-option', state); |  | ||||||
| 		}); |  | ||||||
|  |  | ||||||
| 		ipcRenderer.on('toggletray', (event, state) => { |  | ||||||
| 			this.handleToggle('tray-option', state); |  | ||||||
| 		}); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -197,16 +197,13 @@ ipcRenderer.on('tray', (event, arg) => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| function toggleTray() { | function toggleTray() { | ||||||
| 	let state; |  | ||||||
| 	if (window.tray) { | 	if (window.tray) { | ||||||
| 		state = false; |  | ||||||
| 		window.tray.destroy(); | 		window.tray.destroy(); | ||||||
| 		if (window.tray.isDestroyed()) { | 		if (window.tray.isDestroyed()) { | ||||||
| 			window.tray = null; | 			window.tray = null; | ||||||
| 		} | 		} | ||||||
| 		ConfigUtil.setConfigItem('trayIcon', false); | 		ConfigUtil.setConfigItem('trayIcon', false); | ||||||
| 	} else { | 	} else { | ||||||
| 		state = true; |  | ||||||
| 		createTray(); | 		createTray(); | ||||||
| 		if (process.platform === 'linux' || process.platform === 'win32') { | 		if (process.platform === 'linux' || process.platform === 'win32') { | ||||||
| 			renderNativeImage(unread).then(image => { | 			renderNativeImage(unread).then(image => { | ||||||
| @@ -216,10 +213,6 @@ function toggleTray() { | |||||||
| 		} | 		} | ||||||
| 		ConfigUtil.setConfigItem('trayIcon', true); | 		ConfigUtil.setConfigItem('trayIcon', true); | ||||||
| 	} | 	} | ||||||
| 	const selector = 'webview:not([class*=disabled])'; |  | ||||||
| 	const webview = document.querySelector(selector); |  | ||||||
| 	const webContents = webview.getWebContents(); |  | ||||||
| 	webContents.send('toggletray', state); |  | ||||||
| } | } | ||||||
|  |  | ||||||
| ipcRenderer.on('toggletray', toggleTray); | ipcRenderer.on('toggletray', toggleTray); | ||||||
|   | |||||||
| @@ -1,29 +1,16 @@ | |||||||
| 'use strict'; | 'use strict'; | ||||||
|  |  | ||||||
| const fs = require('fs'); |  | ||||||
| const path = require('path'); |  | ||||||
| const process = require('process'); | const process = require('process'); | ||||||
| const JsonDB = require('node-json-db'); | const JsonDB = require('node-json-db'); | ||||||
| const Logger = require('./logger-util'); |  | ||||||
|  |  | ||||||
| const logger = new Logger({ |  | ||||||
| 	file: 'config-util.log', |  | ||||||
| 	timestamp: true |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| let instance = null; | let instance = null; | ||||||
| let dialog = null; |  | ||||||
| let app = null; | let app = null; | ||||||
|  |  | ||||||
| /* To make the util runnable in both main and renderer process */ | /* To make the util runnable in both main and renderer process */ | ||||||
| if (process.type === 'renderer') { | if (process.type === 'renderer') { | ||||||
| 	const remote = require('electron').remote; | 	app = require('electron').remote.app; | ||||||
| 	dialog = remote.dialog; |  | ||||||
| 	app = remote.app; |  | ||||||
| } else { | } else { | ||||||
| 	const electron = require('electron'); | 	app = require('electron').app; | ||||||
| 	dialog = electron.dialog; |  | ||||||
| 	app = electron.app; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| class ConfigUtil { | class ConfigUtil { | ||||||
| @@ -60,22 +47,7 @@ class ConfigUtil { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	reloadDB() { | 	reloadDB() { | ||||||
| 		const settingsJsonPath = path.join(app.getPath('userData'), '/settings.json'); | 		this.db = new JsonDB(app.getPath('userData') + '/settings.json', true, true); | ||||||
| 		try { |  | ||||||
| 			const file = fs.readFileSync(settingsJsonPath, 'utf8'); |  | ||||||
| 			JSON.parse(file); |  | ||||||
| 		} catch (err) { |  | ||||||
| 			if (fs.existsSync(settingsJsonPath)) { |  | ||||||
| 				fs.unlinkSync(settingsJsonPath); |  | ||||||
| 				dialog.showErrorBox( |  | ||||||
| 					'Error saving settings', |  | ||||||
| 					'We encountered error while saving current settings.' |  | ||||||
| 				); |  | ||||||
| 				logger.error('Error while JSON parsing settings.json: '); |  | ||||||
| 				logger.error(err); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		this.db = new JsonDB(settingsJsonPath, true, true); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,31 +0,0 @@ | |||||||
| const fs = require('fs'); |  | ||||||
|  |  | ||||||
| let app = null; |  | ||||||
| let setupCompleted = false; |  | ||||||
| if (process.type === 'renderer') { |  | ||||||
| 	app = require('electron').remote.app; |  | ||||||
| } else { |  | ||||||
| 	app = require('electron').app; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const zulipDir = app.getPath('userData'); |  | ||||||
| const logDir = `${zulipDir}/Logs/`; |  | ||||||
| const initSetUp = () => { |  | ||||||
| 	// if it is the first time the app is running |  | ||||||
| 	// create zulip dir in userData folder to |  | ||||||
| 	// avoid errors |  | ||||||
| 	if (!setupCompleted) { |  | ||||||
| 		if (!fs.existsSync(zulipDir)) { |  | ||||||
| 			fs.mkdirSync(zulipDir); |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if (!fs.existsSync(logDir)) { |  | ||||||
| 			fs.mkdirSync(logDir); |  | ||||||
| 		} |  | ||||||
| 		setupCompleted = true; |  | ||||||
| 	} |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
| 	initSetUp |  | ||||||
| }; |  | ||||||
| @@ -5,12 +5,6 @@ const fs = require('fs'); | |||||||
| const path = require('path'); | const path = require('path'); | ||||||
| const JsonDB = require('node-json-db'); | const JsonDB = require('node-json-db'); | ||||||
| const request = require('request'); | const request = require('request'); | ||||||
| const Logger = require('./logger-util'); |  | ||||||
|  |  | ||||||
| const logger = new Logger({ |  | ||||||
| 	file: `domain-util.log`, |  | ||||||
| 	timestamp: true |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| let instance = null; | let instance = null; | ||||||
|  |  | ||||||
| @@ -99,7 +93,8 @@ class DomainUtil { | |||||||
| 	checkDomain(domain, silent = false) { | 	checkDomain(domain, silent = false) { | ||||||
| 		if (!silent && this.duplicateDomain(domain)) { | 		if (!silent && this.duplicateDomain(domain)) { | ||||||
| 			// Do not check duplicate in silent mode | 			// Do not check duplicate in silent mode | ||||||
| 			return Promise.reject('This server has been added.'); | 			alert('This server has been added.'); | ||||||
|  | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		domain = this.formatUrl(domain); | 		domain = this.formatUrl(domain); | ||||||
| @@ -120,16 +115,11 @@ class DomainUtil { | |||||||
| 						'Error: unable to verify the first certificate', | 						'Error: unable to verify the first certificate', | ||||||
| 						'Error: unable to get local issuer certificate' | 						'Error: unable to get local issuer certificate' | ||||||
| 					]; | 					]; | ||||||
|  |  | ||||||
| 				// If the domain contains following strings we just bypass the server | 				// If the domain contains following strings we just bypass the server | ||||||
| 				const whitelistDomains = [ | 				const whitelistDomains = [ | ||||||
| 					'zulipdev.org' | 					'zulipdev.org' | ||||||
| 				]; | 				]; | ||||||
|  | 				if (!error && response.statusCode !== 404) { | ||||||
| 				// make sure that error is a error or string not undefined |  | ||||||
| 				// so validation does not throw error. |  | ||||||
| 				error = error || ''; |  | ||||||
| 				if (!error && response.statusCode < 400) { |  | ||||||
| 					// Correct | 					// Correct | ||||||
| 					this.getServerSettings(domain).then(serverSettings => { | 					this.getServerSettings(domain).then(serverSettings => { | ||||||
| 						resolve(serverSettings); | 						resolve(serverSettings); | ||||||
| @@ -169,7 +159,7 @@ class DomainUtil { | |||||||
| 					} | 					} | ||||||
| 				} else { | 				} else { | ||||||
| 					const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \ | 					const invalidZulipServerError = `${domain} does not appear to be a valid Zulip server. Make sure that \ | ||||||
| 					\n(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 \n (3) its a zulip server`; | 					\n(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); | 					reject(invalidZulipServerError); | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
| @@ -235,23 +225,7 @@ class DomainUtil { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	reloadDB() { | 	reloadDB() { | ||||||
| 		const domainJsonPath = path.join(app.getPath('userData'), '/domain.json'); | 		this.db = new JsonDB(app.getPath('userData') + '/domain.json', true, true); | ||||||
| 		try { |  | ||||||
| 			const file = fs.readFileSync(domainJsonPath, 'utf8'); |  | ||||||
| 			JSON.parse(file); |  | ||||||
| 		} catch (err) { |  | ||||||
| 			if (fs.existsSync(domainJsonPath)) { |  | ||||||
| 				fs.unlinkSync(domainJsonPath); |  | ||||||
| 				dialog.showErrorBox( |  | ||||||
| 					'Error saving new organization', |  | ||||||
| 					'There seems to be error while saving new organization, ' + |  | ||||||
| 					'you may have to re-add your previous organizations back.' |  | ||||||
| 				); |  | ||||||
| 				logger.error('Error while JSON parsing domain.json: '); |  | ||||||
| 				logger.error(err); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		this.db = new JsonDB(domainJsonPath, true, true); |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	generateFilePath(url) { | 	generateFilePath(url) { | ||||||
|   | |||||||
| @@ -1,87 +0,0 @@ | |||||||
| const NodeConsole = require('console').Console; |  | ||||||
| const fs = require('fs'); |  | ||||||
| const isDev = require('electron-is-dev'); |  | ||||||
| const { initSetUp } = require('./default-util'); |  | ||||||
|  |  | ||||||
| initSetUp(); |  | ||||||
| let app = null; |  | ||||||
| if (process.type === 'renderer') { |  | ||||||
| 	app = require('electron').remote.app; |  | ||||||
| } else { |  | ||||||
| 	app = require('electron').app; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const browserConsole = console; |  | ||||||
| const logDir = `${app.getPath('userData')}/Logs`; |  | ||||||
|  |  | ||||||
| class Logger { |  | ||||||
| 	constructor(opts = {}) { |  | ||||||
| 		let { |  | ||||||
| 			timestamp = true, |  | ||||||
| 			file = 'console.log', |  | ||||||
| 			level = true, |  | ||||||
| 			logInDevMode = false |  | ||||||
| 		} = opts; |  | ||||||
|  |  | ||||||
| 		file = `${logDir}/${file}`; |  | ||||||
| 		if (timestamp === true) { |  | ||||||
| 			timestamp = this.getTimestamp; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		const fileStream = fs.createWriteStream(file, { flags: 'a' }); |  | ||||||
| 		const nodeConsole = new NodeConsole(fileStream); |  | ||||||
|  |  | ||||||
| 		this.nodeConsole = nodeConsole; |  | ||||||
| 		this.timestamp = timestamp; |  | ||||||
| 		this.level = level; |  | ||||||
| 		this.logInDevMode = logInDevMode; |  | ||||||
| 		this.setUpConsole(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_log(type, ...args) { |  | ||||||
| 		const { |  | ||||||
| 			nodeConsole, timestamp, level, logInDevMode |  | ||||||
| 		} = this; |  | ||||||
| 		let nodeConsoleLog; |  | ||||||
|  |  | ||||||
| 		/* eslint-disable no-fallthrough */ |  | ||||||
| 		switch (true) { |  | ||||||
| 			case typeof timestamp === 'function': |  | ||||||
| 				args.unshift(timestamp() + ' |\t'); |  | ||||||
|  |  | ||||||
| 			case (level !== false): |  | ||||||
| 				args.unshift(type.toUpperCase() + ' |'); |  | ||||||
|  |  | ||||||
| 			case isDev || logInDevMode: |  | ||||||
| 				nodeConsoleLog = nodeConsole[type] || nodeConsole.log; |  | ||||||
| 				nodeConsoleLog.apply(null, args); |  | ||||||
|  |  | ||||||
| 			default: break; |  | ||||||
| 		} |  | ||||||
| 		/* eslint-enable no-fallthrough */ |  | ||||||
|  |  | ||||||
| 		browserConsole[type].apply(null, args); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	setUpConsole() { |  | ||||||
| 		for (const type in browserConsole) { |  | ||||||
| 			this.setupConsoleMethod(type); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	setupConsoleMethod(type) { |  | ||||||
| 		this[type] = (...args) => { |  | ||||||
| 			this._log(type, ...args); |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	getTimestamp() { |  | ||||||
| 		const date = new Date(); |  | ||||||
| 		const timestamp = |  | ||||||
| 			`${date.getMonth()}/${date.getDate()} ` + |  | ||||||
| 			`${date.getMinutes()}:${date.getSeconds()}`; |  | ||||||
| 		return timestamp; |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = Logger; |  | ||||||
| @@ -41,13 +41,4 @@ | |||||||
| </body> | </body> | ||||||
| <script src="js/main.js"></script> | <script src="js/main.js"></script> | ||||||
|  |  | ||||||
| <!-- To trigger electron.reload on changes in renderer from gulp --> |  | ||||||
| <script> |  | ||||||
|   window.addEventListener('load', () => { |  | ||||||
|     const isDev = require('electron-is-dev'); |  | ||||||
|     if (isDev) { |  | ||||||
|       require('electron-connect').client.create(); |  | ||||||
|     } |  | ||||||
|   }); |  | ||||||
| </script> |  | ||||||
| </html> | </html> | ||||||
| @@ -20,6 +20,4 @@ install: | |||||||
| build: off | build: off | ||||||
|  |  | ||||||
| test_script: | test_script: | ||||||
|   - node ./tools/gitlint --ci-mode |   - npm run test-all | ||||||
|   - npm run test |  | ||||||
|   - npm run test-e2e |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ gulp.task('dev', () => { | |||||||
|   // Reload renderer process |   // Reload renderer process | ||||||
| 	gulp.watch('app/renderer/css/*.css', ['reload:renderer']); | 	gulp.watch('app/renderer/css/*.css', ['reload:renderer']); | ||||||
| 	gulp.watch('app/renderer/*.html', ['reload:renderer']); | 	gulp.watch('app/renderer/*.html', ['reload:renderer']); | ||||||
| 	gulp.watch('app/renderer/js/**/*.js', ['reload:renderer']); | 	gulp.watch('app/renderer/js/*.js', ['reload:renderer']); | ||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task('restart:browser', done => { | gulp.task('restart:browser', done => { | ||||||
| @@ -30,7 +30,7 @@ gulp.task('reload:renderer', done => { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| gulp.task('test-e2e', () => { | gulp.task('test-e2e', () => { | ||||||
| 	return gulp.src('tests/*.js') | 	return gulp.src('tests/e2e/*.js') | ||||||
| 	.pipe(tape({ | 	.pipe(tape({ | ||||||
| 		reporter: tapColorize() | 		reporter: tapColorize() | ||||||
| 	})); | 	})); | ||||||
|   | |||||||
							
								
								
									
										21
									
								
								karma.conf.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								karma.conf.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | |||||||
|  | module.exports = function (config) { | ||||||
|  | 	config.set({ | ||||||
|  | 		basePath: '', | ||||||
|  | 		frameworks: ['jasmine'], | ||||||
|  | 		browsers: ['Electron'], | ||||||
|  | 		preprocessors: { | ||||||
|  | 			'**/*.js': ['electron'] | ||||||
|  | 		}, | ||||||
|  | 		files: [ | ||||||
|  | 			{pattern: './karma.shim.js', watched: true, included: true, served: true}, | ||||||
|  | 			{pattern: './tests/unit/*.js', watched: true, included: true, served: true}, | ||||||
|  | 			{pattern: './app/renderer/**/*.js', watched: true, included: false, served: true} | ||||||
|  | 		], | ||||||
|  | 		reporters: ['mocha'], | ||||||
|  | 		client: { | ||||||
|  | 			captureConsole: true, | ||||||
|  | 			useIframe: false | ||||||
|  | 		}, | ||||||
|  | 		singleRun: true | ||||||
|  | 	}); | ||||||
|  | }; | ||||||
							
								
								
									
										5
									
								
								karma.shim.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								karma.shim.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | window.require = window.parent.require; | ||||||
|  | window.process = window.parent.process; | ||||||
|  | window.__dirname = window.parent.__dirname; | ||||||
|  | require('module').globalPaths.push('./node_modules'); | ||||||
|  | require('module').globalPaths.push('./app/renderer'); | ||||||
							
								
								
									
										39
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										39
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1561,9 +1561,9 @@ | |||||||
|       "dev": true |       "dev": true | ||||||
|     }, |     }, | ||||||
|     "electron": { |     "electron": { | ||||||
|       "version": "1.7.9", |       "version": "1.6.15", | ||||||
|       "resolved": "https://registry.npmjs.org/electron/-/electron-1.7.9.tgz", |       "resolved": "https://registry.npmjs.org/electron/-/electron-1.6.15.tgz", | ||||||
|       "integrity": "sha1-rdVOn4+D7QL2UZ7BATX2mLGTNs8=", |       "integrity": "sha1-w07FRIa39Jpm21jG8koJKEFPrqc=", | ||||||
|       "dev": true, |       "dev": true, | ||||||
|       "requires": { |       "requires": { | ||||||
|         "@types/node": "7.0.48", |         "@types/node": "7.0.48", | ||||||
| @@ -4762,34 +4762,6 @@ | |||||||
|         "inherits": "2.0.1" |         "inherits": "2.0.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|     "nodemon": { |  | ||||||
|       "version": "1.14.11", |  | ||||||
|       "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.14.11.tgz", |  | ||||||
|       "integrity": "sha512-323uPopdzYcyDR2Ze1UOLF9zocwoQEyGPiKaLm/Y8Mbfjylt/YueAJUVHqox+vgG8TqZqZApcHv5lmUvrn/KQw==", |  | ||||||
|       "dev": true, |  | ||||||
|       "requires": { |  | ||||||
|         "chokidar": "2.0.0", |  | ||||||
|         "debug": "3.1.0", |  | ||||||
|         "ignore-by-default": "1.0.1", |  | ||||||
|         "minimatch": "3.0.4", |  | ||||||
|         "pstree.remy": "1.1.0", |  | ||||||
|         "semver": "5.4.1", |  | ||||||
|         "touch": "3.1.0", |  | ||||||
|         "undefsafe": "2.0.1", |  | ||||||
|         "update-notifier": "2.3.0" |  | ||||||
|       }, |  | ||||||
|       "dependencies": { |  | ||||||
|         "debug": { |  | ||||||
|           "version": "3.1.0", |  | ||||||
|           "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", |  | ||||||
|           "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", |  | ||||||
|           "dev": true, |  | ||||||
|           "requires": { |  | ||||||
|             "ms": "2.0.0" |  | ||||||
|           } |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     }, |  | ||||||
|     "node-abi": { |     "node-abi": { | ||||||
|       "version": "2.1.2", |       "version": "2.1.2", | ||||||
|       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.2.tgz", |       "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.1.2.tgz", | ||||||
| @@ -5829,6 +5801,11 @@ | |||||||
|         "resolve-from": "1.0.1" |         "resolve-from": "1.0.1" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "resemblejs": { | ||||||
|  |       "version": "2.5.0", | ||||||
|  |       "resolved": "https://registry.npmjs.org/resemblejs/-/resemblejs-2.5.0.tgz", | ||||||
|  |       "integrity": "sha512-j2p4H8jpxch3bG9SOoap4tWbNZ4hNGrl54y7WJSwoKBQM3ZBhVyaFEYqzlv06dY1I4Pns/M8Ten6R6+/jTjycA==" | ||||||
|  |     }, | ||||||
|     "resolve": { |     "resolve": { | ||||||
|       "version": "1.5.0", |       "version": "1.5.0", | ||||||
|       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", |       "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz", | ||||||
|   | |||||||
							
								
								
									
										34
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | |||||||
| { | { | ||||||
|   "name": "zulip", |   "name": "zulip", | ||||||
|   "productName": "Zulip", |   "productName": "Zulip", | ||||||
|   "version": "1.8.1", |   "version": "1.7.0", | ||||||
|   "main": "./app/main", |   "main": "./app/main", | ||||||
|   "description": "Zulip Desktop App", |   "description": "Zulip Desktop App", | ||||||
|   "license": "Apache-2.0", |   "license": "Apache-2.0", | ||||||
| @@ -21,18 +21,18 @@ | |||||||
|     "start": "electron app --disable-http-cache", |     "start": "electron app --disable-http-cache", | ||||||
|     "reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install", |     "reinstall": "rm -rf node_modules; rm -rf app/node_modules; npm install", | ||||||
|     "postinstall": "electron-builder install-app-deps", |     "postinstall": "electron-builder install-app-deps", | ||||||
|     "test": "xo", |     "lint": "xo", | ||||||
|     "test-e2e": "gulp test-e2e", |     "test-e2e": "gulp test-e2e", | ||||||
|     "dev": "gulp dev & nodemon --watch app/main --watch app/renderer --exec 'npm test' -e html,css,js", |     "test-unit": "karma start karma.conf.js", | ||||||
|  |     "test-all": "xo && npm run test-unit && npm run test-e2e", | ||||||
|  |     "dev": "gulp dev", | ||||||
|     "pack": "electron-builder --dir", |     "pack": "electron-builder --dir", | ||||||
|     "dist": "electron-builder", |     "dist": "electron-builder", | ||||||
|     "mas": "electron-builder --mac mas", |     "mas": "electron-builder --mac mas", | ||||||
|     "setup-gitlint-hooks": "node ./tools/gitlint/setup-gitlint-hook", |  | ||||||
|     "lint-commits": "node ./tools/gitlint --all-commits", |  | ||||||
|     "travis": "cd ./scripts && ./travis-build-test.sh" |     "travis": "cd ./scripts && ./travis-build-test.sh" | ||||||
|   }, |   }, | ||||||
|   "pre-commit": [ |   "pre-commit": [ | ||||||
|     "test" |     "lint" | ||||||
|   ], |   ], | ||||||
|   "build": { |   "build": { | ||||||
|     "appId": "org.zulip.zulip-electron", |     "appId": "org.zulip.zulip-electron", | ||||||
| @@ -109,21 +109,26 @@ | |||||||
|   ], |   ], | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "assert": "1.4.1", |     "assert": "1.4.1", | ||||||
|     "chalk": "^2.3.0", |  | ||||||
|     "cp-file": "^5.0.0", |     "cp-file": "^5.0.0", | ||||||
|     "devtron": "1.4.0", |     "devtron": "1.4.0", | ||||||
|     "electron": "1.7.10", |     "electron-builder": "19.46.4", | ||||||
|     "electron-builder": "19.53.6", |     "electron": "1.7.9", | ||||||
|     "electron-connect": "0.6.2", |     "electron-connect": "0.6.2", | ||||||
|     "electron-debug": "1.4.0", |     "electron-debug": "1.4.0", | ||||||
|     "gulp": "3.9.1", |     "gulp": "3.9.1", | ||||||
|     "gulp-tape": "0.0.9", |     "gulp-tape": "0.0.9", | ||||||
|     "is-ci": "^1.0.10", |     "is-ci": "^1.0.10", | ||||||
|     "nodemon": "^1.14.11", |     "jasmine": "^2.8.0", | ||||||
|  |     "karma": "^1.7.1", | ||||||
|  |     "karma-electron": "^5.2.2", | ||||||
|  |     "karma-electron-launcher": "^0.2.0", | ||||||
|  |     "karma-jasmine": "^1.1.0", | ||||||
|  |     "karma-mocha-reporter": "^2.2.5", | ||||||
|     "pre-commit": "1.2.2", |     "pre-commit": "1.2.2", | ||||||
|     "spectron": "3.7.2", |     "spectron": "3.7.2", | ||||||
|     "tap-colorize": "^1.2.0", |     "tap-colorize": "^1.2.0", | ||||||
|     "tape": "^4.8.0", |     "tape": "^4.8.0", | ||||||
|  |     "watchify": "^3.9.0", | ||||||
|     "xo": "0.18.2" |     "xo": "0.18.2" | ||||||
|   }, |   }, | ||||||
|   "xo": { |   "xo": { | ||||||
| @@ -154,16 +159,11 @@ | |||||||
|           "import/no-extraneous-dependencies": 0, |           "import/no-extraneous-dependencies": 0, | ||||||
|           "no-prototype-builtins": 0 |           "no-prototype-builtins": 0 | ||||||
|         } |         } | ||||||
|       }, |  | ||||||
|       { |  | ||||||
|         "files": "scripts/gitlint/*.js", |  | ||||||
|         "rules": { |  | ||||||
|           "unicorn/no-process-exit": 1 |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
|     ], |     ], | ||||||
|     "ignore": [ |     "ignore": [ | ||||||
|       "tests/*.js" |       "tests/e2e/*.js", | ||||||
|  |       "tests/unit/*" | ||||||
|     ], |     ], | ||||||
|     "envs": [ |     "envs": [ | ||||||
|       "node", |       "node", | ||||||
|   | |||||||
| @@ -10,8 +10,14 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then | |||||||
|     xdpyinfo | grep dimensions |     xdpyinfo | grep dimensions | ||||||
| fi | fi | ||||||
|  |  | ||||||
| npm run test | # macOS | ||||||
|  | # Run all the tests | ||||||
| if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then | if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then | ||||||
|     npm run test-e2e |     npm run test-all | ||||||
|  | fi | ||||||
|  |  | ||||||
|  | # Linux | ||||||
|  | # Only run linting test on Linux | ||||||
|  | if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then | ||||||
|  |     npm run lint | ||||||
| fi | fi | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ module.exports = { | |||||||
| function createApp (t) { | function createApp (t) { | ||||||
|   generateTestAppPackageJson() |   generateTestAppPackageJson() | ||||||
|   return new Application({ |   return new Application({ | ||||||
|     path: path.join(__dirname, '..', 'node_modules', '.bin', |     path: path.join(__dirname,'..', '..', 'node_modules', '.bin', | ||||||
|       'electron' + (process.platform === 'win32' ? '.cmd' : '')), |       'electron' + (process.platform === 'win32' ? '.cmd' : '')), | ||||||
|     args: [path.join(__dirname)], // Ensure this dir has a package.json file with a 'main' entry piont
 |     args: [path.join(__dirname)], // Ensure this dir has a package.json file with a 'main' entry piont
 | ||||||
|     env: {NODE_ENV: 'test'}, |     env: {NODE_ENV: 'test'}, | ||||||
| @@ -34,9 +34,9 @@ function createApp (t) { | |||||||
| // Reads app package.json and updates the productName to config.TEST_APP_PRODUCT_NAME 
 | // Reads app package.json and updates the productName to config.TEST_APP_PRODUCT_NAME 
 | ||||||
| // We do this so that the app integration doesn't doesn't share the same appDataDir as the dev application
 | // We do this so that the app integration doesn't doesn't share the same appDataDir as the dev application
 | ||||||
| function generateTestAppPackageJson () { | function generateTestAppPackageJson () { | ||||||
|   let packageJson = require(path.join(__dirname, '../package.json')) |   let packageJson = require(path.join(__dirname, '..', '..', 'package.json')) | ||||||
|   packageJson.productName = config.TEST_APP_PRODUCT_NAME |   packageJson.productName = config.TEST_APP_PRODUCT_NAME | ||||||
|   packageJson.main = '../app/main' |   packageJson.main = '../../app/main' | ||||||
| 
 | 
 | ||||||
|   const testPackageJsonPath = path.join(__dirname, 'package.json') |   const testPackageJsonPath = path.join(__dirname, 'package.json') | ||||||
|   fs.writeFileSync(testPackageJsonPath, JSON.stringify(packageJson, null, ' '), 'utf-8') |   fs.writeFileSync(testPackageJsonPath, JSON.stringify(packageJson, null, ' '), 'utf-8') | ||||||
							
								
								
									
										20
									
								
								tests/unit/test-spellchecker.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								tests/unit/test-spellchecker.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | const ConfigUtil = require('js/utils/config-util.js'); | ||||||
|  | const SetupSpellChecker = require('js/spellchecker') | ||||||
|  |  | ||||||
|  | describe('test spell checker', function () { | ||||||
|  |   // enable spellchecker settings | ||||||
|  |   ConfigUtil.setConfigItem('enableSpellchecker', true); | ||||||
|  |   ConfigUtil.setConfigItem('spellcheckerLanguage', 'en'); | ||||||
|  |  | ||||||
|  |   SetupSpellChecker.init()  // re-initialize after setting update | ||||||
|  |  | ||||||
|  |   const spellCheckHandler = SetupSpellChecker.SpellCheckHandler | ||||||
|  |  | ||||||
|  |   it('mark misspelled word', function () { | ||||||
|  |     expect(spellCheckHandler.isMisspelled('helpe')).toBe(true) | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   it('verify properly spelled word', function () { | ||||||
|  |     expect(spellCheckHandler.isMisspelled('help')).toBe(false) | ||||||
|  |   }) | ||||||
|  | }) | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -e |  | ||||||
| set -x |  | ||||||
|  |  | ||||||
| if ! git diff-index --quiet HEAD; then |  | ||||||
|     set +x |  | ||||||
|     echo "There are uncommitted changes:" |  | ||||||
|     git status --short |  | ||||||
|     echo "Doing nothing to avoid losing your work." |  | ||||||
|     exit 1 |  | ||||||
| fi |  | ||||||
| request_id="$1" |  | ||||||
| remote=${2:-"upstream"} |  | ||||||
| git fetch "$remote" "pull/$request_id/head" |  | ||||||
| git checkout -B "review-original-${request_id}" |  | ||||||
| git reset --hard FETCH_HEAD |  | ||||||
| @@ -1,17 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| set -e |  | ||||||
| set -x |  | ||||||
|  |  | ||||||
| if ! git diff-index --quiet HEAD; then |  | ||||||
|     set +x |  | ||||||
|     echo "There are uncommitted changes:" |  | ||||||
|     git status --short |  | ||||||
|     echo "Doing nothing to avoid losing your work." |  | ||||||
|     exit 1 |  | ||||||
| fi |  | ||||||
| request_id="$1" |  | ||||||
| remote=${2:-"upstream"} |  | ||||||
| git fetch "$remote" "pull/$request_id/head" |  | ||||||
| git checkout -B "review-${request_id}" $remote/master |  | ||||||
| git reset --hard FETCH_HEAD |  | ||||||
| git pull --rebase |  | ||||||
| @@ -1,31 +0,0 @@ | |||||||
| const {run} = require('./helpers'); |  | ||||||
|  |  | ||||||
| module.exports = () => { |  | ||||||
| 	if (process.argv.TRAVIS !== undefined) { |  | ||||||
| 		return travis(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return appveyor(); |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| function travis() { |  | ||||||
| 	if (!process.env.TRAVIS_PULL_REQUEST) { |  | ||||||
|     // Building against master last commit |  | ||||||
| 		return run('git log -1 HEAD'); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const cmd = `git log ${process.env.TRAVIS_COMMIT_RANGE}`; |  | ||||||
| 	const commitRange = run(`git diff --name-only ${process.env.TRAVIS_COMMIT_RANGE}`); |  | ||||||
| 	process.stdout.write(`COMMIT_RANGE: ${commitRange}`, 'utf8'); |  | ||||||
| 	return run(cmd); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function appveyor() { |  | ||||||
| 	if (!process.env.APPVEYOR_PULL_REQUEST_NUMBER) { |  | ||||||
| 		return run('git log -1 HEAD'); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const cmd = |  | ||||||
|     `git log origin/master...${process.env.APPVEYOR_PULL_REQUEST_HEAD_COMMIT}`; |  | ||||||
| 	return run(cmd); |  | ||||||
| } |  | ||||||
| @@ -1,91 +0,0 @@ | |||||||
| const {spawnSync} = require('child_process'); |  | ||||||
| const chalk = require('chalk'); |  | ||||||
|  |  | ||||||
| const commitMsgRegex = /[A-Z]+.*\.$/; |  | ||||||
| const isFullCommitRegex = /(\w|\W){1,}:\s{1}/; |  | ||||||
| const fullCommitRegex = /(\w|\W){1,}:\s{1}[A-Z]+.*\.$/; |  | ||||||
|  |  | ||||||
| function run(script) { |  | ||||||
| 	script = script.split(' '); |  | ||||||
| 	const cmd = script.splice(0, 1)[0]; |  | ||||||
| 	const args = script; |  | ||||||
| 	const output = spawnSync(cmd, args, { |  | ||||||
| 		cwd: process.cwd(), |  | ||||||
| 		encoding: 'utf8', |  | ||||||
| 		windowsHide: true |  | ||||||
| 	}).stdout; |  | ||||||
|  |  | ||||||
| 	return output; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function garbageCollect(a) { |  | ||||||
| 	a.forEach((content, index) => { |  | ||||||
| 		if (content === '' || content === undefined) { |  | ||||||
| 			a.splice(index, 1); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| 	return a; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function getAllCommits(output) { |  | ||||||
| 	output = output.split('\ncommits'); |  | ||||||
| 	if (!output.length > 1) { |  | ||||||
| 		exports.error('There are no commits to lint.'); |  | ||||||
| 		process.exit(1); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	output = garbageCollect(output); |  | ||||||
| 	output.forEach((commit, index) => { |  | ||||||
| 		output[index] = 'commit' + commit; |  | ||||||
| 	}); |  | ||||||
|  |  | ||||||
| 	return output; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function parseCommit(output) { |  | ||||||
| 	output = output.split('\n\n'); |  | ||||||
|  |  | ||||||
| 	let commit = output[0].replace('commit ', ''); |  | ||||||
| 	commit = commit.replace(/\n.*/g, ''); |  | ||||||
| 	let commitHash = commit.split(''); |  | ||||||
| 	commitHash = commitHash.slice(commitHash.length - 7); |  | ||||||
| 	commitHash = commitHash.join(''); |  | ||||||
|  |  | ||||||
| 	const fullCommit = output[1].split('\n'); |  | ||||||
| 	const commitMsg = fullCommit[0]; |  | ||||||
| 	let lintingStatus = commitMsgRegex.test(commitMsg); |  | ||||||
| 	lintingStatus = (commitMsg.length <= 72); |  | ||||||
|  |  | ||||||
| 	if (lintingStatus && isFullCommitRegex(commitMsg)) { |  | ||||||
| 		lintingStatus = fullCommitRegex.test(commitMsg); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	const result = { |  | ||||||
| 		failed: !lintingStatus, |  | ||||||
| 		commitHash |  | ||||||
| 	}; |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function logSuccess() { |  | ||||||
| 	console.log(chalk`{green commit linter:} commit linter passed.`); |  | ||||||
| 	process.exit(0); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function error(...args) { |  | ||||||
| 	args.unshift(chalk.red('ERROR! ')); |  | ||||||
| 	console.error.apply(this, args); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| function warn() { |  | ||||||
| 	// console.error(chalk`{yellow ${msg}}`); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| module.exports = { |  | ||||||
| 	run, |  | ||||||
| 	getAllCommits, |  | ||||||
| 	parseCommit, |  | ||||||
| 	logSuccess, |  | ||||||
| 	error, |  | ||||||
| 	warn |  | ||||||
| }; |  | ||||||
| @@ -1,44 +0,0 @@ | |||||||
| const helpers = require('./helpers'); |  | ||||||
| const getCICmd = require('./ci'); |  | ||||||
|  |  | ||||||
| let checkAllCommits = false; |  | ||||||
| let ciMode = false; |  | ||||||
| if (process.argv[2]) { |  | ||||||
| 	checkAllCommits = process.argv[2].includes('-a'); |  | ||||||
| 	ciMode = process.argv[2] === '--ci-mode'; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| let cmd; |  | ||||||
| if (ciMode) { |  | ||||||
| 	cmd = getCICmd(); |  | ||||||
| } else { |  | ||||||
| 	cmd = |  | ||||||
|     checkAllCommits ? 'git log upstream/master...HEAD' : 'git log -1 HEAD'; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const commits = helpers.run(cmd); |  | ||||||
| const commitsArray = helpers.getAllCommits(commits); |  | ||||||
| let lintFailed = false; |  | ||||||
| commitsArray.forEach(commit => { |  | ||||||
| 	const res = helpers.parseCommit(commit); |  | ||||||
| 	if (res.failed) { |  | ||||||
| 		const {commitHash} = res; |  | ||||||
| 		helpers.error(`commit ${commitHash} does not pass our commit style.`); |  | ||||||
| 		lintFailed = true; |  | ||||||
| 	} else { |  | ||||||
| 		helpers.logSuccess('Commit[s] follow the zulip-electron commit rules.'); |  | ||||||
| 	} |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| if (lintFailed) { |  | ||||||
| 	helpers.warn(` |  | ||||||
|   commit msg linting failed |  | ||||||
|   ------------------------------- |  | ||||||
|   Reasons it does not have: |  | ||||||
|     a) capitial latter at start of message |  | ||||||
| 		b) period at the end of commit or |  | ||||||
| 		c) Commit msg length is more than 72 charaters |  | ||||||
| 	`); |  | ||||||
| 	helpers.warn('Run with --no-verify flag to skip the commit-linter'); |  | ||||||
| 	process.exit(1); |  | ||||||
| } |  | ||||||
| @@ -1,73 +0,0 @@ | |||||||
| // This script sets up the pre-push |  | ||||||
| // git hook which will be used to lint |  | ||||||
| // commits |  | ||||||
|  |  | ||||||
| const fs = require('fs'); |  | ||||||
| const path = require('path'); |  | ||||||
|  |  | ||||||
| const gitHooks = '../../.git/hooks'; |  | ||||||
| const postCommitPath = path.resolve(__dirname, `${gitHooks}/post-commit`); |  | ||||||
| const prePushPath = path.resolve(__dirname, `${gitHooks}/pre-push`); |  | ||||||
|  |  | ||||||
| function scriptTemplate(cmds) { |  | ||||||
| 	cmds = cmds.join(''); |  | ||||||
| 	const script = [ |  | ||||||
| 		'#!/bin/sh', |  | ||||||
| 		'set -e', |  | ||||||
| 		'echo running gitlint...', |  | ||||||
| 		cmds, |  | ||||||
| 		'exit $?' |  | ||||||
| 	]; |  | ||||||
|  |  | ||||||
| 	return script.join('\n'); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| const postCommitFile = scriptTemplate`node scripts/gitlint`; |  | ||||||
| const prePushFile = scriptTemplate`node scripts/gitlint --all-commits`; |  | ||||||
|  |  | ||||||
| function writeAndChmod(file, data) { |  | ||||||
| 	fs.writeFile(file, data, err => { |  | ||||||
| 		if (err) { |  | ||||||
| 			throw err; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		fs.chmod(file, '777', err => { |  | ||||||
| 			if (err) { |  | ||||||
| 				const msg = |  | ||||||
| 					'chmod post-commit, pre-push hooks, at .git/hooks 0777 so they work!'; |  | ||||||
| 				console.error(msg); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| [postCommitPath, prePushPath].forEach((file, index) => { |  | ||||||
| 	fs.open(file, 'w+', err => { |  | ||||||
| 		if (err && err.code !== 'EEXIST') { |  | ||||||
| 			throw err; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		const data = index === 0 ? postCommitFile : prePushFile; |  | ||||||
| 		writeAndChmod(file, data); |  | ||||||
| 	}); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
| // Remove .sample files since |  | ||||||
| // sometimes the hooks do not work |  | ||||||
| const postCommitSampleFile = `${postCommitPath}.sample`; |  | ||||||
| const prePushSampleFile = `${prePushPath}.sample`; |  | ||||||
| function removeSampleFile(file) { |  | ||||||
| 	fs.unlink(file, err => { |  | ||||||
| 		if (err) { |  | ||||||
| 			throw err; |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| [postCommitSampleFile, prePushSampleFile].forEach(file => { |  | ||||||
| 	fs.exists(file, exists => { |  | ||||||
| 		if (exists) { |  | ||||||
| 			removeSampleFile(file); |  | ||||||
| 		} |  | ||||||
| 	}); |  | ||||||
| }); |  | ||||||
		Reference in New Issue
	
	Block a user