mirror of
				https://github.com/zulip/zulip-desktop.git
				synced 2025-10-31 12:03:39 +00:00 
			
		
		
		
	Compare commits
	
		
			21 Commits
		
	
	
		
			test-new-d
			...
			server-pag
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 297c056090 | ||
|  | 34411e2a6b | ||
|  | 88f5a0e699 | ||
|  | 2b6e76d5cd | ||
|  | 4840cbe043 | ||
|  | c8537acdf5 | ||
|  | e776222d6b | ||
|  | 30b05571e7 | ||
|  | 0a155c63e7 | ||
|  | 13c750ac6c | ||
|  | 48799f75d1 | ||
|  | 29f4e702ad | ||
|  | 3eb4cf4f64 | ||
|  | 1a97d8a5b0 | ||
|  | 2f96ec6199 | ||
|  | 52de465457 | ||
|  | e3039cf5a9 | ||
|  | 6c120269eb | ||
|  | b31fc6b66d | ||
|  | 435e5f086e | ||
|  | 603ad7dfcd | 
| @@ -52,8 +52,8 @@ const iconPath = () => { | ||||
| function createMainWindow() { | ||||
| 	// Load the previous state with fallback to defaults | ||||
| 	const mainWindowState = windowStateKeeper({ | ||||
| 		defaultWidth: 1000, | ||||
| 		defaultHeight: 600 | ||||
| 		defaultWidth: 1100, | ||||
| 		defaultHeight: 720 | ||||
| 	}); | ||||
|  | ||||
| 	// Let's keep the window position global so that we can access it in other process | ||||
| @@ -71,7 +71,6 @@ function createMainWindow() { | ||||
| 		minHeight: 400, | ||||
| 		webPreferences: { | ||||
| 			plugins: true, | ||||
| 			allowDisplayingInsecureContent: true, | ||||
| 			nodeIntegration: true | ||||
| 		}, | ||||
| 		show: false | ||||
|   | ||||
| @@ -1,45 +1,50 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| 	<head> | ||||
| 		<meta charset="UTF-8"> | ||||
| 		<link rel="stylesheet" href="css/about.css"> | ||||
| 	</head> | ||||
| 	<body> | ||||
|  | ||||
| <head> | ||||
| 	<meta charset="UTF-8"> | ||||
| 	<link rel="stylesheet" href="css/about.css"> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
| 	<div class="about"> | ||||
| 		<img class="logo" src="../resources/zulip.png" /> | ||||
| 		<p class="detail" id="version">v?.?.?</p> | ||||
| 		<div class="maintenance-info"> | ||||
| 			<p class="detail maintainer"> | ||||
| 				Maintained by <a onclick="linkInBrowser('website')">Zulip</a> | ||||
| 				Maintained by | ||||
| 				<a onclick="linkInBrowser('website')">Zulip</a> | ||||
| 			</p> | ||||
| 			<p class="detail license"> | ||||
| 				Available under the <a onclick="linkInBrowser('license')">Apache 2.0 License</a> | ||||
| 				Available under the | ||||
| 				<a onclick="linkInBrowser('license')">Apache 2.0 License</a> | ||||
| 			</p> | ||||
| 			<a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<script> | ||||
|  | ||||
| 	const { app } = require('electron').remote; | ||||
| 	const { shell } = require('electron'); | ||||
| 	const version_tag = document.querySelector('#version'); | ||||
| 	version_tag.innerHTML = 'v' + app.getVersion(); | ||||
| 		const { app } = require('electron').remote; | ||||
| 		const { shell } = require('electron'); | ||||
| 		const version_tag = document.querySelector('#version'); | ||||
| 		version_tag.innerHTML = 'v' + app.getVersion(); | ||||
|  | ||||
| 	function linkInBrowser(type) { | ||||
| 		let url; | ||||
| 		switch (type) { | ||||
| 			case 'website':  | ||||
| 				url = "https://zulipchat.com"; | ||||
| 				break; | ||||
| 			case 'license':  | ||||
| 				url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE"; | ||||
| 				break; | ||||
| 			default: | ||||
| 				url = 'https://github.com/zulip/zulip-electron/issues/new?body=' +  | ||||
| 				      '%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E'; | ||||
| 		function linkInBrowser(type) { | ||||
| 			let url; | ||||
| 			switch (type) { | ||||
| 				case 'website': | ||||
| 					url = "https://zulipchat.com"; | ||||
| 					break; | ||||
| 				case 'license': | ||||
| 					url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE"; | ||||
| 					break; | ||||
| 				default: | ||||
| 					url = 'https://github.com/zulip/zulip-electron/issues/new?body=' + | ||||
| 						'%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E'; | ||||
| 			} | ||||
| 			shell.openExternal(url); | ||||
| 		} | ||||
| 		shell.openExternal(url); | ||||
| 	} | ||||
| 	</script> | ||||
| 	</body> | ||||
| 	<script>require('./js/shared/preventdrag.js')</script> | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
| @@ -28,7 +28,7 @@ body { | ||||
|     -webkit-app-region: drag; | ||||
|     overflow: hidden; | ||||
|     transition: all 0.5s ease; | ||||
|     z-index: 1; | ||||
|     z-index: 2; | ||||
| } | ||||
|  | ||||
| .toggle-sidebar div { | ||||
| @@ -121,11 +121,14 @@ body { | ||||
| } | ||||
|  | ||||
| .action-button.active { | ||||
|     background-color: rgba(255, 255, 255, 0.25); | ||||
|     /* background-color: rgba(255, 255, 255, 0.25); */ | ||||
|     background-color: #efefef; | ||||
|     opacity: 0.9; | ||||
|     padding-right: 14px; | ||||
| } | ||||
|  | ||||
| .action-button.active i { | ||||
|     color: #eee; | ||||
|     color: #1c262b; | ||||
| } | ||||
|  | ||||
| .tab:first-child { | ||||
| @@ -249,28 +252,34 @@ body { | ||||
| } | ||||
|  | ||||
| webview { | ||||
|     opacity: 1; | ||||
|     /* transition: opacity 0.3s ease-in; */ | ||||
|     flex-grow: 1; | ||||
|     position: absolute; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     flex-grow: 1; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
| } | ||||
|  | ||||
| webview.onload { | ||||
|     transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035); | ||||
| } | ||||
|  | ||||
| webview.disabled { | ||||
|     flex: 0 1; | ||||
|     height: 0; | ||||
|     width: 0; | ||||
|     opacity: 0; | ||||
|     transition: opacity 0.3s ease-out; | ||||
| webview.active { | ||||
|     opacity: 1; | ||||
|     z-index: 1; | ||||
|     visibility: visible; | ||||
| } | ||||
|  | ||||
| webview:focus { | ||||
| webview.disabled { | ||||
|     opacity: 0; | ||||
| } | ||||
|  | ||||
| webview.focus { | ||||
|     outline: 0px solid transparent; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Tooltip styling */ | ||||
|  | ||||
| #back-tooltip, | ||||
| @@ -278,7 +287,7 @@ webview:focus { | ||||
| #setting-tooltip { | ||||
|     font-family: sans-serif; | ||||
|     background: #222c31; | ||||
|     margin-left: 45px; | ||||
|     margin-left: 48px; | ||||
|     padding: 6px 8px; | ||||
|     position: absolute; | ||||
|     margin-top: 0px; | ||||
| @@ -350,6 +359,8 @@ webview:focus { | ||||
|     height: 100%; | ||||
|     width: 100%; | ||||
|     position: relative; | ||||
|     flex-grow: 1; | ||||
|     flex-basis: 0px; | ||||
| } | ||||
|  | ||||
| .hidden { | ||||
|   | ||||
| @@ -95,11 +95,13 @@ td:nth-child(odd) { | ||||
| } | ||||
|  | ||||
| #sidebar { | ||||
|     width: 80px; | ||||
|     padding: 30px; | ||||
|     width: 150px; | ||||
|     min-width: 100px; | ||||
|     padding: 30px 30px 30px 35px; | ||||
|     display: flex; | ||||
|     flex-direction: column; | ||||
|     font-size: 16px; | ||||
|     background: #f2f2f2; | ||||
| } | ||||
|  | ||||
| #nav-container { | ||||
| @@ -113,7 +115,7 @@ td:nth-child(odd) { | ||||
| } | ||||
|  | ||||
| .nav.active { | ||||
|     color: #464e5a; | ||||
|     color: #4ebfac; | ||||
|     cursor: default; | ||||
|     position: relative; | ||||
| } | ||||
| @@ -121,12 +123,19 @@ td:nth-child(odd) { | ||||
| .nav.active::before { | ||||
|     background: #464e5a; | ||||
|     width: 3px; | ||||
|     height: 16px; | ||||
|     height: 18px; | ||||
|     position: absolute; | ||||
|     left: -8px; | ||||
|     content: ''; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* We don't want to show this in nav item since we have the + button for adding an Organization */ | ||||
|  | ||||
| #nav-AddServer { | ||||
|     display: none; | ||||
| } | ||||
|  | ||||
| #settings-header { | ||||
|     font-size: 22px; | ||||
|     color: #222c31; | ||||
| @@ -142,12 +151,12 @@ td:nth-child(odd) { | ||||
| } | ||||
|  | ||||
| #new-server-container { | ||||
|     opacity: 1; | ||||
|     transition: opacity 0.3s; | ||||
|     padding-left: 42px; | ||||
|     padding-top: 25px; | ||||
|     margin-right: 16px; | ||||
| } | ||||
|  | ||||
| .title { | ||||
|     padding: 4px 0 6px 0; | ||||
|     font-weight: 500; | ||||
|     color: #222c31; | ||||
| } | ||||
| @@ -159,6 +168,16 @@ td:nth-child(odd) { | ||||
|     padding: 4px 0 6px 0; | ||||
| } | ||||
|  | ||||
| .add-server-info-row { | ||||
|     display: flex; | ||||
|     margin: 8px 0 0 0; | ||||
| } | ||||
|  | ||||
| .add-server-info-right { | ||||
|     flex-grow: 1; | ||||
|     margin-top: 10px; | ||||
| } | ||||
|  | ||||
| .sub-title { | ||||
|     padding: 4px 0 6px 0; | ||||
|     font-weight: bold; | ||||
| @@ -169,20 +188,34 @@ img.server-info-icon { | ||||
|     width: 36px; | ||||
|     height: 36px; | ||||
|     padding: 4px; | ||||
|     cursor: pointer; | ||||
|     vertical-align: middle; | ||||
| } | ||||
|  | ||||
| .server-info-left { | ||||
|     margin: 10px 20px 0 0; | ||||
|     margin: 4px 20px 0 0; | ||||
|     min-width: 40%; | ||||
| } | ||||
|  | ||||
| .server-info-right { | ||||
|     flex-grow: 1; | ||||
|     margin-right: 10px; | ||||
|     margin-top: 4px; | ||||
|     min-width: 55%; | ||||
| } | ||||
|  | ||||
| .server-info-row { | ||||
|     display: flex; | ||||
|     margin: 8px 0 0 0; | ||||
|     display: inline-block; | ||||
|     margin: 5px 0 0 0; | ||||
| } | ||||
|  | ||||
| .server-info-left .server-info-row { | ||||
|     display: inline-flex; | ||||
|     align-items: inherit; | ||||
|     vertical-align: -2px; | ||||
|     position: relative; | ||||
| } | ||||
|  | ||||
| .server-url { | ||||
|     min-width: 60%; | ||||
| } | ||||
|  | ||||
| .server-info-alias { | ||||
| @@ -190,6 +223,10 @@ img.server-info-icon { | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .server-url-info { | ||||
|     margin-right: 10px; | ||||
| } | ||||
|  | ||||
| .setting-input-key { | ||||
|     font-size: 14px; | ||||
|     height: 27px; | ||||
| @@ -203,18 +240,16 @@ img.server-info-icon { | ||||
| .setting-input-value { | ||||
|     flex-grow: 1; | ||||
|     font-size: 14px; | ||||
|     height: 22px; | ||||
|     border-radius: 3px; | ||||
|     padding: 7px; | ||||
|     padding: 13px; | ||||
|     border: #ededed 2px solid; | ||||
|     outline-width: 0; | ||||
|     background: transparent; | ||||
|     max-width: 500px; | ||||
|     max-width: 450px; | ||||
| } | ||||
|  | ||||
| .setting-input-value:focus { | ||||
|     border: #7cb980 2px solid; | ||||
|     border-radius: 3px; | ||||
|     border: #4EBFAC 2px solid; | ||||
| } | ||||
|  | ||||
| .setting-block { | ||||
| @@ -270,8 +305,8 @@ img.server-info-icon { | ||||
| } | ||||
|  | ||||
| .settings-card:hover { | ||||
|     /* 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); */ | ||||
|     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 { | ||||
| @@ -280,11 +315,15 @@ img.server-info-icon { | ||||
| } | ||||
|  | ||||
| .red { | ||||
|     color: #ffffff; | ||||
|     background: #ef5350; | ||||
|     padding: 3px; | ||||
|     padding-right: 10px; | ||||
|     padding-left: 10px; | ||||
|     color: #ef5350; | ||||
|     padding: 8px; | ||||
|     border: rgba(239, 83, 80, 0.5) solid 1px; | ||||
| } | ||||
|  | ||||
| .red:hover { | ||||
|     color: #e63431; | ||||
|     border: rgba(239, 83, 80, 0.7) solid 1px; | ||||
|     ; | ||||
| } | ||||
|  | ||||
| .blue { | ||||
| @@ -314,8 +353,8 @@ img.server-info-icon { | ||||
| } | ||||
|  | ||||
| i.open-tab-button { | ||||
|     padding: 0 5px; | ||||
|     font-size: 18px; | ||||
|     padding-left: 2px; | ||||
|     font-size: 19px; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| @@ -368,17 +407,10 @@ i.open-tab-button { | ||||
| } | ||||
|  | ||||
| #open-create-org-link { | ||||
|     color: #666; | ||||
|     cursor: pointer; | ||||
|     text-decoration: none; | ||||
| } | ||||
|  | ||||
| #open-create-org-link:hover { | ||||
|     color: #005580; | ||||
|     ; | ||||
|     text-decoration: underline; | ||||
| } | ||||
|  | ||||
| .toggle { | ||||
|     position: absolute; | ||||
|     margin-left: -9999px; | ||||
| @@ -433,6 +465,94 @@ input.toggle-round:checked+label:after { | ||||
| } | ||||
|  | ||||
|  | ||||
| /*  Add new server modal */ | ||||
|  | ||||
| .add-server-modal { | ||||
|     display: block; | ||||
|     position: fixed; | ||||
|     z-index: 1; | ||||
|     padding-top: 15vh; | ||||
|     left: 0; | ||||
|     top: 0; | ||||
|     margin: auto; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     /* background: rgba(61, 64, 67, 15); */ | ||||
|     background: linear-gradient(35deg, #003b52, #45b59b); | ||||
|     overflow: auto; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* Modal Content */ | ||||
|  | ||||
| .modal-container { | ||||
|     background-color: #f4f7f8; | ||||
|     margin: auto; | ||||
|     padding: 57px; | ||||
|     border: #dae1e3 1px solid; | ||||
|     width: 550px; | ||||
|     height: 370px; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .add-server-modal .page-title { | ||||
|     text-align: center; | ||||
|     font-size: 1.6rem; | ||||
| } | ||||
|  | ||||
| .divider { | ||||
|     margin-bottom: 30px; | ||||
|     margin-top: 30px; | ||||
|     color: #7d878a; | ||||
| } | ||||
|  | ||||
| .divider hr { | ||||
|     margin-left: 8px; | ||||
|     margin-right: 8px; | ||||
|     width: 44%; | ||||
| } | ||||
|  | ||||
| .left { | ||||
|     float: left; | ||||
| } | ||||
|  | ||||
| .right { | ||||
|     float: right; | ||||
| } | ||||
|  | ||||
| .server-center { | ||||
|     width: 100%; | ||||
|     text-align: center; | ||||
|     align-items: center; | ||||
|     padding-top: 13px; | ||||
|     margin-left: -5px; | ||||
| } | ||||
|  | ||||
| .server-center button { | ||||
|     font-weight: bold; | ||||
|     font-size: 1.1rem; | ||||
|     margin: auto; | ||||
|     align-items: center; | ||||
|     text-align: center; | ||||
|     color: #fff; | ||||
|     background: #4EBFAC; | ||||
|     border-color: none; | ||||
|     border: none; | ||||
|     width: 98%; | ||||
|     height: 46px; | ||||
|     border-radius: 3px; | ||||
|     cursor: pointer; | ||||
| } | ||||
|  | ||||
| .server-center button:hover { | ||||
|     background: #329588; | ||||
| } | ||||
|  | ||||
| .server-center button:focus { | ||||
|     background: #329588; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* responsive grid */ | ||||
|  | ||||
| @media (max-width: 650px) { | ||||
| @@ -445,4 +565,27 @@ input.toggle-round:checked+label:after { | ||||
|     #css-delete-action span { | ||||
|         display: none; | ||||
|     } | ||||
| } | ||||
|  | ||||
| @media (max-width: 720px) { | ||||
|     .modal-container { | ||||
|         width: 60vw; | ||||
|         padding: 40px; | ||||
|         min-width: 300px; | ||||
|     } | ||||
|     .server-center button { | ||||
|         margin-right: -12px; | ||||
|         width: 100%; | ||||
|     } | ||||
|     .divider { | ||||
|         margin-right: -8px; | ||||
|     } | ||||
|     .divider hr { | ||||
|         margin-left: 6px; | ||||
|         margin-right: 6px; | ||||
|         width: 43%; | ||||
|     } | ||||
|     #new-server-container { | ||||
|         padding-left: 0px; | ||||
|     } | ||||
| } | ||||
| @@ -130,6 +130,7 @@ class WebView extends BaseComponent { | ||||
| 		} | ||||
|  | ||||
| 		this.$el.classList.remove('disabled'); | ||||
| 		this.$el.classList.add('active'); | ||||
| 		setTimeout(() => { | ||||
| 			if (this.props.role === 'server') { | ||||
| 				this.$el.classList.remove('onload'); | ||||
| @@ -168,6 +169,7 @@ class WebView extends BaseComponent { | ||||
|  | ||||
| 	hide() { | ||||
| 		this.$el.classList.add('disabled'); | ||||
| 		this.$el.classList.remove('active'); | ||||
| 	} | ||||
|  | ||||
| 	load() { | ||||
|   | ||||
| @@ -122,7 +122,7 @@ class ServerManagerView { | ||||
| 			// Remove focus from the settings icon at sidebar bottom | ||||
| 			this.$settingsButton.classList.remove('active'); | ||||
| 		} else { | ||||
| 			this.openSettings('Servers'); | ||||
| 			this.openSettings('AddServer'); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -159,7 +159,7 @@ class ServerManagerView { | ||||
| 			this.tabs[this.activeTabIndex].webview.reload(); | ||||
| 		}); | ||||
| 		this.$addServerButton.addEventListener('click', () => { | ||||
| 			this.openSettings('Servers'); | ||||
| 			this.openSettings('AddServer'); | ||||
| 		}); | ||||
| 		this.$settingsButton.addEventListener('click', () => { | ||||
| 			this.openSettings('General'); | ||||
| @@ -268,10 +268,10 @@ class ServerManagerView { | ||||
| 	} | ||||
|  | ||||
| 	activateLastTab(index) { | ||||
| 		// Open last active tab | ||||
| 		ConfigUtil.setConfigItem('lastActiveTab', index); | ||||
| 		// Open all the tabs in background | ||||
| 		// Open all the tabs in background, also activate the tab based on the index | ||||
| 		this.activateTab(index); | ||||
| 		// Save last active tab | ||||
| 		ConfigUtil.setConfigItem('lastActiveTab', index); | ||||
| 	} | ||||
|  | ||||
| 	activateTab(index, hideOldTab = true) { | ||||
| @@ -311,7 +311,7 @@ class ServerManagerView { | ||||
| 			webContents.send('toggle-sidebar', state); | ||||
| 		}); | ||||
|  | ||||
| 		ipcRenderer.on('toogle-silent', (event, state) => { | ||||
| 		ipcRenderer.on('toggle-silent', (event, state) => { | ||||
| 			const webviews = document.querySelectorAll('webview'); | ||||
| 			webviews.forEach(webview => { | ||||
| 				try { | ||||
|   | ||||
| @@ -3,9 +3,8 @@ | ||||
| const BaseSection = require(__dirname + '/base-section.js'); | ||||
| const DomainUtil = require(__dirname + '/../../utils/domain-util.js'); | ||||
| const ServerInfoForm = require(__dirname + '/server-info-form.js'); | ||||
| const CreateOrganziation = require(__dirname + '/create-new-org.js'); | ||||
| 
 | ||||
| class AddedSection extends BaseSection { | ||||
| class ConnectedOrgSection extends BaseSection { | ||||
| 	constructor(props) { | ||||
| 		super(); | ||||
| 		this.props = props; | ||||
| @@ -14,10 +13,9 @@ class AddedSection extends BaseSection { | ||||
| 	template() { | ||||
| 		return ` | ||||
| 			<div class="settings-pane" id="server-settings-pane"> | ||||
| 				<div id="new-server-container"></div> | ||||
| 				<div class="title" id="existing-servers"></div> | ||||
| 				<div class="page-title">Connected organizations</div> | ||||
| 				<div class="title" id="existing-servers">All the connected orgnizations will appear here.</div> | ||||
| 				<div id="server-info-container"></div> | ||||
| 				<div id="create-organization-container"></div> | ||||
| 			</div> | ||||
| 		`;
 | ||||
| 	} | ||||
| @@ -34,12 +32,9 @@ class AddedSection extends BaseSection { | ||||
| 		this.$serverInfoContainer = document.getElementById('server-info-container'); | ||||
| 		this.$existingServers = document.getElementById('existing-servers'); | ||||
| 
 | ||||
| 		this.$serverInfoContainer.innerHTML = servers.length ? '' : ''; | ||||
| 		// Show Existing servers if servers are there otherwise hide it
 | ||||
| 		this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Connected organizations'; | ||||
| 
 | ||||
| 		this.$createOrganizationContainer = document.getElementById('create-organization-container'); | ||||
| 		this.initCreateNewOrganization(); | ||||
| 		const noServerText = 'All the connected orgnizations will appear here'; | ||||
| 		// Show noServerText if no servers are there otherwise hide it
 | ||||
| 		this.$existingServers.innerHTML = servers.length === 0 ? noServerText : ''; | ||||
| 
 | ||||
| 		for (let i = 0; i < servers.length; i++) { | ||||
| 			new ServerInfoForm({ | ||||
| @@ -51,11 +46,6 @@ class AddedSection extends BaseSection { | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	initCreateNewOrganization() { | ||||
| 		new CreateOrganziation({ | ||||
| 			$root: this.$createOrganizationContainer | ||||
| 		}).init(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| module.exports = AddedSection; | ||||
| module.exports = ConnectedOrgSection; | ||||
| @@ -1,37 +0,0 @@ | ||||
| '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">Or 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; | ||||
| @@ -180,7 +180,7 @@ class GeneralSection extends BaseSection { | ||||
| 				const newValue = !ConfigUtil.getConfigItem('silent', true); | ||||
| 				ConfigUtil.setConfigItem('silent', newValue); | ||||
| 				this.updateSilentOption(); | ||||
| 				currentBrowserWindow.send('toogle-silent', newValue); | ||||
| 				currentBrowserWindow.send('toggle-silent', newValue); | ||||
| 			} | ||||
| 		}); | ||||
| 	} | ||||
|   | ||||
| @@ -8,7 +8,7 @@ class PreferenceNav extends BaseComponent { | ||||
|  | ||||
| 		this.props = props; | ||||
|  | ||||
| 		this.navItems = ['General', 'Network', 'Servers', 'Added-servers', 'Shortcuts']; | ||||
| 		this.navItems = ['General', 'Network', 'AddServer', 'Organizations', 'Shortcuts']; | ||||
|  | ||||
| 		this.init(); | ||||
| 	} | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const BaseComponent = require(__dirname + '/../../components/base.js'); | ||||
| const DomainUtil = require(__dirname + '/../../utils/domain-util.js'); | ||||
| const shell = require('electron').shell; | ||||
|  | ||||
| class NewServerForm extends BaseComponent { | ||||
| 	constructor(props) { | ||||
| @@ -11,26 +12,25 @@ class NewServerForm extends BaseComponent { | ||||
|  | ||||
| 	template() { | ||||
| 		return ` | ||||
| 			<div class="settings-card"> | ||||
| 				<div class="server-info-right"> | ||||
| 					<div class="title">Organization URL</div> | ||||
| 					<div class="server-info-row"> | ||||
| 						<input class="setting-input-value" autofocus placeholder="example.zulipchat.com or chat.example.com"/> | ||||
| 					</div> | ||||
| 					<div class="server-info-row"> | ||||
| 						<div class="action blue server-save-action"> | ||||
| 							<span>Next</span> | ||||
| 						</div> | ||||
| 			<div class="server-input-container"> | ||||
| 				<div class="title">Organization URL</div> | ||||
| 				<div class="add-server-info-row"> | ||||
| 					<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or zulip.your-organization.com"/> | ||||
| 				</div> | ||||
| 				<div class="server-center"> | ||||
| 					<div class="server-save-action"> | ||||
| 						<button id="connect">Connect</button> | ||||
| 					</div> | ||||
| 				</div> | ||||
| 				<div class="server-center"> | ||||
| 				<div class="divider"> | ||||
| 				<hr class="left"/>OR<hr class="right" /> | ||||
| 					<hr class="left"/>OR<hr class="right" /> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="server-info-row"> | ||||
| 						<div class="action blue server-create-action"> | ||||
| 							<span>Create a new organization</span> | ||||
| 						</div> | ||||
| 				</div> | ||||
| 				<div class="server-center"> | ||||
| 				<div class="server-save-action"> | ||||
| 					<button id="open-create-org-link">Create a new organization</button> | ||||
| 			</div> | ||||
| 					</div> | ||||
| 			</div> | ||||
| 		`; | ||||
| @@ -51,17 +51,25 @@ class NewServerForm extends BaseComponent { | ||||
| 	} | ||||
|  | ||||
| 	submitFormHandler() { | ||||
| 		this.$saveServerButton.children[0].innerHTML = 'Adding...'; | ||||
| 		this.$saveServerButton.children[0].innerHTML = 'Connecting...'; | ||||
| 		DomainUtil.checkDomain(this.$newServerUrl.value).then(serverConf => { | ||||
| 			DomainUtil.addDomain(serverConf).then(() => { | ||||
| 				this.props.onChange(this.props.index); | ||||
| 			}); | ||||
| 		}, errorMessage => { | ||||
| 			this.$saveServerButton.children[0].innerHTML = 'Next'; | ||||
| 			this.$saveServerButton.children[0].innerHTML = 'Connect'; | ||||
| 			alert(errorMessage); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	openCreateNewOrgExternalLink() { | ||||
| 		const link = 'https://zulipchat.com/new/'; | ||||
| 		const externalCreateNewOrgEl = document.getElementById('open-create-org-link'); | ||||
| 		externalCreateNewOrgEl.addEventListener('click', () => { | ||||
| 			shell.openExternal(link); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| 	initActions() { | ||||
| 		this.$saveServerButton.addEventListener('click', () => { | ||||
| 			this.submitFormHandler(); | ||||
| @@ -73,6 +81,8 @@ class NewServerForm extends BaseComponent { | ||||
| 				this.submitFormHandler(); | ||||
| 			} | ||||
| 		}); | ||||
| 		// open create new org link in default browser | ||||
| 		this.openCreateNewOrgExternalLink(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -7,8 +7,8 @@ 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'); | ||||
| const NetworkSection = require(__dirname + '/js/pages/preference/network-section.js'); | ||||
| const ConnectedOrgSection = require(__dirname + '/js/pages/preference/connected-org-section.js'); | ||||
| const ShortcutsSection = require(__dirname + '/js/pages/preference/shortcuts-section.js'); | ||||
| const AddedSection = require(__dirname + '/js/pages/preference/added-server-section.js'); | ||||
|  | ||||
| class PreferenceView extends BaseComponent { | ||||
| 	constructor() { | ||||
| @@ -40,7 +40,7 @@ class PreferenceView extends BaseComponent { | ||||
| 	handleNavigation(navItem) { | ||||
| 		this.nav.select(navItem); | ||||
| 		switch (navItem) { | ||||
| 			case 'Servers': { | ||||
| 			case 'AddServer': { | ||||
| 				this.section = new ServersSection({ | ||||
| 					$root: this.$settingsContainer | ||||
| 				}); | ||||
| @@ -52,14 +52,14 @@ class PreferenceView extends BaseComponent { | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'Network': { | ||||
| 				this.section = new NetworkSection({ | ||||
| 			case 'Organizations': { | ||||
| 				this.section = new ConnectedOrgSection({ | ||||
| 					$root: this.$settingsContainer | ||||
| 				}); | ||||
| 				break; | ||||
| 			} | ||||
| 			case 'Added-servers': { | ||||
| 				this.section = new AddedSection({ | ||||
| 			case 'Network': { | ||||
| 				this.section = new NetworkSection({ | ||||
| 					$root: this.$settingsContainer | ||||
| 				}); | ||||
| 				break; | ||||
|   | ||||
| @@ -16,19 +16,18 @@ class ServerInfoForm extends BaseComponent { | ||||
| 			<div class="settings-card"> | ||||
| 				<div class="server-info-left"> | ||||
| 					<img class="server-info-icon" src="${this.props.server.icon}"/> | ||||
| 				</div> | ||||
| 				<div class="server-info-right"> | ||||
| 					<div class="server-info-row"> | ||||
| 						<span class="server-info-alias">${this.props.server.alias}</span> | ||||
| 						<i class="material-icons open-tab-button">open_in_new</i> | ||||
| 					</div> | ||||
| 					<div class="server-info-row"> | ||||
| 						<input class="setting-input-value" disabled value="${this.props.server.url}"/> | ||||
| 				</div> | ||||
| 				<div class="server-info-right"> | ||||
| 					<div class="server-info-row server-url"> | ||||
| 						<span class="server-url-info">${this.props.server.url}</span> | ||||
| 					</div> | ||||
| 					<div class="server-info-row"> | ||||
| 						<div class="action red server-delete-action"> | ||||
| 							<i class="material-icons">indeterminate_check_box</i> | ||||
| 							<span>Delete</span> | ||||
| 							<span>Disconnect</span> | ||||
| 						</div> | ||||
| 					</div> | ||||
| 				</div> | ||||
| @@ -44,6 +43,7 @@ class ServerInfoForm extends BaseComponent { | ||||
| 	initForm() { | ||||
| 		this.$serverInfoForm = this.generateNodeFromTemplate(this.template()); | ||||
| 		this.$serverInfoAlias = this.$serverInfoForm.getElementsByClassName('server-info-alias')[0]; | ||||
| 		this.$serverIcon = this.$serverInfoForm.getElementsByClassName('server-info-icon')[0]; | ||||
| 		this.$deleteServerButton = this.$serverInfoForm.getElementsByClassName('server-delete-action')[0]; | ||||
| 		this.$openServerButton = this.$serverInfoForm.getElementsByClassName('open-tab-button')[0]; | ||||
| 		this.props.$root.appendChild(this.$serverInfoForm); | ||||
| @@ -71,7 +71,12 @@ class ServerInfoForm extends BaseComponent { | ||||
| 		this.$serverInfoAlias.addEventListener('click', () => { | ||||
| 			ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index); | ||||
| 		}); | ||||
|  | ||||
| 		this.$serverIcon.addEventListener('click', () => { | ||||
| 			ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index); | ||||
| 		}); | ||||
| 	} | ||||
|  | ||||
| } | ||||
|  | ||||
| module.exports = ServerInfoForm; | ||||
|   | ||||
| @@ -11,16 +11,14 @@ class ServersSection extends BaseSection { | ||||
|  | ||||
| 	template() { | ||||
| 		return ` | ||||
| 		<div id="myModal" class="modal"> | ||||
| 		<div class="modal-content"> | ||||
|  | ||||
| 			<div class="settings-pane" id="server-settings-pane"> | ||||
| 				<div class="page-title">Add a Zulip organization</div> | ||||
| 				<div id="new-server-container"></div> | ||||
| 			</div> | ||||
|  | ||||
| 			</div> | ||||
| 		<div class="add-server-modal"> | ||||
| 			<div class="modal-container"> | ||||
| 				<div class="settings-pane" id="server-settings-pane"> | ||||
| 					<div class="page-title">Add a Zulip organization</div> | ||||
| 					<div id="new-server-container"></div> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		`; | ||||
| 	} | ||||
|  | ||||
| @@ -32,16 +30,9 @@ class ServersSection extends BaseSection { | ||||
| 		this.props.$root.innerHTML = ''; | ||||
|  | ||||
| 		this.props.$root.innerHTML = this.template(); | ||||
| 		this.$serverInfoContainer = document.getElementById('server-info-container'); | ||||
| 		this.$existingServers = document.getElementById('existing-servers'); | ||||
| 		this.$newServerContainer = document.getElementById('new-server-container'); | ||||
| 		this.$newServerButton = document.getElementById('new-server-action'); | ||||
|  | ||||
| 		this.initNewServerForm(); | ||||
|  | ||||
| 		this.$createOrganizationContainer = document.getElementById('create-organization-container'); | ||||
|  | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	initNewServerForm() { | ||||
|   | ||||
| @@ -8,6 +8,9 @@ const ConfigUtil = require(__dirname + '/utils/config-util.js'); | ||||
| // eslint-disable-next-line import/no-unassigned-import | ||||
| require('./notification'); | ||||
|  | ||||
| // Prevent drag and drop event in main process which prevents remote code executaion | ||||
| require(__dirname + '/shared/preventdrag.js'); | ||||
|  | ||||
| const logout = () => { | ||||
| 	// Create the menu for the below | ||||
| 	document.querySelector('.dropdown-toggle').click(); | ||||
|   | ||||
							
								
								
									
										17
									
								
								app/renderer/js/shared/preventdrag.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/renderer/js/shared/preventdrag.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| // This is a security fix. Following function prevents drag and drop event in the app | ||||
| // so that attackers can't execute any remote code within the app | ||||
| // It doesn't affect the compose box so that users can still | ||||
| // use drag and drop event to share files etc | ||||
|  | ||||
| const preventDragAndDrop = () => { | ||||
| 	const preventEvents = ['dragover', 'drop']; | ||||
| 	preventEvents.forEach(dragEvents => { | ||||
| 		document.addEventListener(dragEvents, event => { | ||||
| 			event.preventDefault(); | ||||
| 		}); | ||||
| 	}); | ||||
| }; | ||||
|  | ||||
| preventDragAndDrop(); | ||||
| @@ -44,4 +44,5 @@ | ||||
|   </div> | ||||
| </body> | ||||
| <script src="js/main.js"></script> | ||||
| <script>require('./js/shared/preventdrag.js')</script> | ||||
| </html> | ||||
| @@ -17,5 +17,6 @@ | ||||
|       <div id="reconnect">Try now</div> | ||||
|     </div> | ||||
|   </body> | ||||
|   <script src="js/pages/network.js"></script>   | ||||
|   <script src="js/pages/network.js"></script> | ||||
|   <script>require('./js/shared/preventdrag.js')</script> | ||||
| </html> | ||||
|   | ||||
| @@ -1,163 +1,17 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en" class="responsive desktop"> | ||||
|  | ||||
| <head> | ||||
|   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | ||||
|   <meta name="viewport" content="width=device-width"> | ||||
|   <title>Zulip - Settings</title> | ||||
|   <link rel="stylesheet" href="css/preference.css" type="text/css" media="screen"> | ||||
|   <style> | ||||
|     /* The Modal (background) */ | ||||
|  | ||||
|     .modal { | ||||
|       display: block; | ||||
|       /* Hidden by default */ | ||||
|       position: fixed; | ||||
|       /* Stay in place */ | ||||
|       z-index: 1; | ||||
|       /* Sit on top */ | ||||
|       padding-top: 100px; | ||||
|       /* Location of the box */ | ||||
|       left: 0; | ||||
|       top: 0; | ||||
|       width: 100%; | ||||
|       /* Full width */ | ||||
|       height: 100%; | ||||
|       /* Full height */ | ||||
|       overflow: auto; | ||||
|       /* Enable scroll if needed */ | ||||
|       /* background-color: rgb(0, 0, 0); */ | ||||
|       /* Fallback color */ | ||||
|       /* background-color: rgba(0, 0, 0, 0.4); */ | ||||
|       background: rgba(61, 64, 67, 15); | ||||
|       /* Black w/ opacity */ | ||||
|     } | ||||
|  | ||||
|     /* Modal Content */ | ||||
|  | ||||
|     .modal-content { | ||||
|       background-color: #fefefe; | ||||
|       margin: auto; | ||||
|       padding: 57px; | ||||
|       border: #dae1e3 1px solid; | ||||
|       width: 600px; | ||||
|       /* margin-bottom: 113px; */ | ||||
|       height: 344px; | ||||
|       border-radius: 4px; | ||||
|     } | ||||
|  | ||||
|     /* The Close Button */ | ||||
|  | ||||
|     .close { | ||||
|       color: #aaaaaa; | ||||
|       float: right; | ||||
|       font-size: 28px; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|  | ||||
|     .close:hover, | ||||
|     .close:focus { | ||||
|       color: #000; | ||||
|       text-decoration: none; | ||||
|       cursor: pointer; | ||||
|     } | ||||
|  | ||||
|     #server-settings-pane { | ||||
|       margin-left: 18%; | ||||
|     } | ||||
|  | ||||
|     .settings-card.server-info-right:hover { | ||||
|       box-shadow: none !important; | ||||
|       border: none !important; | ||||
|     } | ||||
|  | ||||
|     .server-save-action { | ||||
|       padding: 11px; | ||||
|       padding-left: 20px; | ||||
|       width: 292px; | ||||
|       border-radius: 4px; | ||||
|       margin-top: 16px; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .divider { | ||||
|       /* width: 250px; | ||||
|       text-align: center; | ||||
|       margin-left: 39px; | ||||
|       margin-top: 48px; */ | ||||
|  | ||||
|       width: 335px; | ||||
|       text-align: center; | ||||
|       margin-left: 0px; | ||||
|       margin-top: 48px; | ||||
|       color: #7d878a; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .divider hr { | ||||
|       margin-left: auto; | ||||
|       margin-right: auto; | ||||
|       width: 42%; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .left { | ||||
|       float: left; | ||||
|     } | ||||
|  | ||||
|     .right { | ||||
|       float: right; | ||||
|     } | ||||
|  | ||||
|     .server-create-action { | ||||
|       width: 314px; | ||||
|       margin-top: 40px; | ||||
|       padding: 11px; | ||||
|       border-radius: 4px; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     .server-create-action span { | ||||
|       margin-left: 60px; | ||||
|       font-size: 15px; | ||||
|     } | ||||
|  | ||||
|     .server-save-action span { | ||||
|       margin-left: 130px; | ||||
|     } | ||||
|  | ||||
|     .server-save-action { | ||||
|       width: 306px; | ||||
|     } | ||||
|  | ||||
|     .server-info-row { | ||||
|       display: block; | ||||
|     } | ||||
|  | ||||
|     .setting-input-value { | ||||
|       width: 319px; | ||||
|     } | ||||
|  | ||||
|     .page-title { | ||||
|       color: #222c31; | ||||
|       margin-left: 65px; | ||||
|       font-size: 22px; | ||||
|       font-weight: 100; | ||||
|       margin-bottom: 20px; | ||||
|       padding: 0; | ||||
|     } | ||||
|     .title { | ||||
|       color: #7d878a; | ||||
|     } | ||||
|   </style> | ||||
| </head> | ||||
|  | ||||
| <body> | ||||
|   <div id="content"> | ||||
|     <div id="sidebar"></div> | ||||
|     <div id="settings-container"></div> | ||||
|   </div> | ||||
| </body> | ||||
| <script src="js/pages/preference/preference.js"></script> | ||||
|  | ||||
| </html> | ||||
|   <head> | ||||
|     <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | ||||
|     <meta name="viewport" content="width=device-width"> | ||||
|     <title>Zulip - Settings</title> | ||||
|     <link rel="stylesheet" href="css/preference.css" type="text/css" media="screen"> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="content"> | ||||
|       <div id="sidebar"></div> | ||||
|       <div id="settings-container"></div> | ||||
|     </div> | ||||
|   </body> | ||||
|   <script src="js/pages/preference/preference.js"></script> | ||||
|   <script>require('./js/shared/preventdrag.js')</script> | ||||
| </html> | ||||
|   | ||||
| @@ -25,7 +25,7 @@ manager (see [here][nodesource-install] for more on the first command): | ||||
|  | ||||
| ```sh | ||||
| $ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - | ||||
| $ sudo apt install git nodejs python build-essential libxext-dev libxtst-dev libxkbfile-dev | ||||
| $ sudo apt install git nodejs python build-essential libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4 | ||||
| ``` | ||||
|  | ||||
| [nodesource-install]: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions | ||||
|   | ||||
| @@ -113,7 +113,7 @@ | ||||
|     "assert": "1.4.1", | ||||
|     "cp-file": "^5.0.0", | ||||
|     "devtron": "1.4.0", | ||||
|     "electron": "1.8.2", | ||||
|     "electron": "1.8.4", | ||||
|     "electron-builder": "19.53.6", | ||||
|     "electron-connect": "0.6.2", | ||||
|     "electron-debug": "1.4.0", | ||||
| @@ -123,7 +123,7 @@ | ||||
|     "is-ci": "^1.0.10", | ||||
|     "nodemon": "^1.14.11", | ||||
|     "pre-commit": "1.2.2", | ||||
|     "spectron": "3.7.2", | ||||
|     "spectron": "3.8.0", | ||||
|     "tap-colorize": "^1.2.0", | ||||
|     "tape": "^4.8.0", | ||||
|     "xo": "0.18.2" | ||||
|   | ||||
| @@ -2,6 +2,10 @@ | ||||
|  | ||||
| # This script runs when user install the debian package | ||||
|  | ||||
| # Link to the binary | ||||
| ln -sf '/opt/${productFilename}/${executable}' '/usr/local/bin/${executable}'; | ||||
| echo 'Successfully added /opt/${productFilename}/${executable} to /usr/local/bin/${executable}' | ||||
|  | ||||
| # Install apt repository source list if it does not exist | ||||
| if ! grep ^ /etc/apt/sources.list /etc/apt/sources.list.d/* | grep zulip.list; then | ||||
|     sudo apt-key adv --keyserver pool.sks-keyservers.net --recv 69AD12704E71A4803DCA3A682424BE5AE9BD10D9 | ||||
|   | ||||
| @@ -28,4 +28,8 @@ appDirectory=/home/$getSudoUser/.config/Zulip/; | ||||
|  | ||||
| if [ -d $appDirectory ]; then | ||||
|     sudo rm -rf $appDirectory; | ||||
| fi | ||||
| fi | ||||
|  | ||||
| # Delete the link to the binary | ||||
| echo 'Removing binary link' | ||||
| sudo rm -f '/usr/local/bin/${executable}'; | ||||
| @@ -7,7 +7,7 @@ test('app runs', function (t) { | ||||
|   const app = setup.createApp() | ||||
|   setup.waitForLoad(app, t) | ||||
|     .then(() => app.client.windowByIndex(1)) // focus on webview | ||||
|     .then(() => app.client.waitForExist('//*[@id="new-server-container"]/div/div/div[2]/input')) | ||||
|     .then(() => app.client.waitForExist('//*[@id="connect"]')) // id of the connect button | ||||
|     .then(() => setup.endTest(app, t), | ||||
|           (err) => setup.endTest(app, t, err || 'error')) | ||||
| }) | ||||
		Reference in New Issue
	
	Block a user