Compare commits

..

7 Commits

Author SHA1 Message Date
Akash Nimare
68dd63d472 travis: Test dist path [WIP]. 2018-03-15 19:49:03 +05:30
Akash Nimare
d2f53a0b71 travis: Test dist path. 2018-03-15 19:32:56 +05:30
Akash Nimare
fba0330512 travis: Debug macOS installers. 2018-03-15 18:20:49 +05:30
Akash Nimare
4ff914cf34 travis: Enable debugging for artifacts. 2018-03-15 17:03:09 +05:30
Akash Nimare
4831bea447 travis: Update build script. 2018-03-15 16:40:56 +05:30
Akash Nimare
01849bf601 electron-builder: Update builder to latest release. 2018-03-13 19:48:52 +05:30
cPhost
349294f536 travis: Upload build artifacts for release branch. 2018-03-09 15:00:34 +05:30
39 changed files with 11518 additions and 620 deletions

7
.gitignore vendored
View File

@@ -7,13 +7,6 @@ node_modules/
# Compiled binary build directory # Compiled binary build directory
dist/ dist/
#snap generated files
snap/parts
snap/prime
snap/snap
snap/stage
snap/*.snap
# Logs # Logs
logs logs
*.log *.log

View File

@@ -2,16 +2,31 @@ sudo: required
dist: trusty dist: trusty
os: os:
- osx - osx
- linux - linux
osx_image: xcode9.0
addons: addons:
apt: apt:
packages: packages:
- build-essential - build-essential
- libxext-dev - libxext-dev
- libxtst-dev - libxtst-dev
- libxkbfile-dev - libxkbfile-dev
artifacts:
paths:
- $(ls ./dist/*.AppImage | tr "\n" ":")
- $(ls ./dist/*.deb | tr "\n" ":")
- $(ls ./dist/*.dmg | tr "\n" ":")
- $(ls ./dist/*.zip | tr "\n" ":")
- $(ls ./dist/*.dmg.blockmap | tr "\n" ":")
- $(ls ./dist/github/*.json | tr "\n" ":")
- $(ls ./dist/github/*.yml | tr "\n" ":")
- $(ls ./dist/*.yml | tr "\n" ":")
- $(ls ./dist/mac/*.yml | tr "\n" ":")
- $(ls ./dist/linux/*.yml | tr "\n" ":")
debug: true
language: node_js language: node_js
node_js: node_js:
@@ -24,11 +39,20 @@ before_install:
cache: cache:
directories: directories:
- node_modules - node_modules
- app/node_modules - app/node_modules
- ~/.cache
script: script:
- npm run travis - npm run travis
- chmod +x ./scripts/install-release-dependencies.sh
- ./scripts/install-release-dependencies.sh
- npm run dist
- node ./scripts/prepare-artifacts.js
# log out /dist files might be useful to know
# what files are uploaded
- ls dist
notifications: notifications:
webhooks: webhooks:
urls: urls:

View File

@@ -52,8 +52,8 @@ const iconPath = () => {
function createMainWindow() { function createMainWindow() {
// Load the previous state with fallback to defaults // Load the previous state with fallback to defaults
const mainWindowState = windowStateKeeper({ const mainWindowState = windowStateKeeper({
defaultWidth: 1100, defaultWidth: 1000,
defaultHeight: 720 defaultHeight: 600
}); });
// Let's keep the window position global so that we can access it in other process // Let's keep the window position global so that we can access it in other process
@@ -71,6 +71,7 @@ function createMainWindow() {
minHeight: 400, minHeight: 400,
webPreferences: { webPreferences: {
plugins: true, plugins: true,
allowDisplayingInsecureContent: true,
nodeIntegration: true nodeIntegration: true
}, },
show: false show: false

View File

@@ -2,7 +2,7 @@
const os = require('os'); const os = require('os');
const path = require('path'); const path = require('path');
const { app, shell, BrowserWindow, Menu, dialog } = require('electron'); const { app, shell, BrowserWindow, Menu } = require('electron');
const fs = require('fs-extra'); const fs = require('fs-extra');
@@ -87,7 +87,7 @@ class AppMenu {
} }
}, { }, {
label: 'Toggle Sidebar', label: 'Toggle Sidebar',
accelerator: 'CommandOrControl+Shift+S', accelerator: 'CommandOrControl+S',
click(item, focusedWindow) { click(item, focusedWindow) {
if (focusedWindow) { if (focusedWindow) {
const newValue = !ConfigUtil.getConfigItem('showSidebar'); const newValue = !ConfigUtil.getConfigItem('showSidebar');
@@ -388,32 +388,20 @@ class AppMenu {
} }
static resetAppSettings() { static resetAppSettings() {
const resetAppSettingsMessage = 'By proceeding you will be removing all connected organizations and preferences from Zulip.';
// We save App's settings/configurations in following files // We save App's settings/configurations in following files
const settingFiles = ['window-state.json', 'domain.json', 'settings.json']; const settingFiles = ['window-state.json', 'domain.json', 'settings.json'];
dialog.showMessageBox({ settingFiles.forEach(settingFileName => {
type: 'warning', const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
buttons: ['YES', 'NO'], fs.access(getSettingFilesPath, error => {
defaultId: 0, if (error) {
message: 'Are you sure?', console.log(error);
detail: resetAppSettingsMessage } else {
}, response => { fs.unlink(getSettingFilesPath, () => {
if (response === 0) { AppMenu.sendAction('clear-app-data');
settingFiles.forEach(settingFileName => {
const getSettingFilesPath = path.join(app.getPath('appData'), appName, settingFileName);
fs.access(getSettingFilesPath, error => {
if (error) {
console.log(error);
} else {
fs.unlink(getSettingFilesPath, () => {
AppMenu.sendAction('clear-app-data');
});
}
}); });
}); }
} });
}); });
} }

1380
app/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "zulip", "name": "zulip",
"productName": "Zulip", "productName": "Zulip",
"version": "1.9.0", "version": "1.8.2",
"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,12 @@
"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.21.4", "electron-updater": "2.21.0",
"electron-window-state": "4.1.1", "electron-window-state": "4.1.1",
"is-online": "7.0.0", "is-online": "7.0.0",
"node-json-db": "0.7.3", "node-json-db": "0.7.3",
"request": "2.81.0", "request": "2.81.0",
"semver": "5.4.1", "semver": "5.4.1",
"@sentry/electron": "0.5.0",
"wurl": "2.5.0" "wurl": "2.5.0"
}, },
"optionalDependencies": { "optionalDependencies": {

View File

@@ -1,50 +1,45 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<head> <meta charset="UTF-8">
<meta charset="UTF-8"> <link rel="stylesheet" href="css/about.css">
<link rel="stylesheet" href="css/about.css"> </head>
</head> <body>
<body>
<div class="about"> <div class="about">
<img class="logo" src="../resources/zulip.png" /> <img class="logo" src="../resources/zulip.png" />
<p class="detail" id="version">v?.?.?</p> <p class="detail" id="version">v?.?.?</p>
<div class="maintenance-info"> <div class="maintenance-info">
<p class="detail maintainer"> <p class="detail maintainer">
Maintained by Maintained by <a onclick="linkInBrowser('website')">Zulip</a>
<a onclick="linkInBrowser('website')">Zulip</a>
</p> </p>
<p class="detail license"> <p class="detail license">
Available under the Available under the <a onclick="linkInBrowser('license')">Apache 2.0 License</a>
<a onclick="linkInBrowser('license')">Apache 2.0 License</a>
</p> </p>
<a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a> <a class="bug" onclick="linkInBrowser('bug')" href="#">Found bug?</a>
</div> </div>
</div> </div>
<script> <script>
const { app } = require('electron').remote; const { app } = require('electron').remote;
const { shell } = require('electron'); const { shell } = require('electron');
const version_tag = document.querySelector('#version'); const version_tag = document.querySelector('#version');
version_tag.innerHTML = 'v' + app.getVersion(); version_tag.innerHTML = 'v' + app.getVersion();
function linkInBrowser(type) { function linkInBrowser(type) {
let url; let url;
switch (type) { switch (type) {
case 'website': case 'website':
url = "https://zulipchat.com"; url = "https://zulipchat.com";
break; break;
case 'license': case 'license':
url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE"; url = "https://github.com/zulip/zulip-electron/blob/master/LICENSE";
break; break;
default: default:
url = 'https://github.com/zulip/zulip-electron/issues/new?body=' + url = 'https://github.com/zulip/zulip-electron/issues/new?body=' +
'%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E'; '%3C!--Please%20describe%20your%20issue%20and%20steps%20to%20reproduce%20it.--%3E';
}
shell.openExternal(url);
} }
shell.openExternal(url);
}
</script> </script>
<script>require('./js/shared/preventdrag.js')</script> </body>
</body>
</html> </html>

View File

@@ -121,14 +121,11 @@ body {
} }
.action-button.active { .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 { .action-button.active i {
color: #1c262b; color: #eee;
} }
.tab:first-child { .tab:first-child {
@@ -262,11 +259,6 @@ webview {
flex-direction: column; flex-direction: column;
} }
webview.download-webview {
z-index: -1;
pointer-events: none;
}
webview.onload { webview.onload {
transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035); transition: opacity 1s cubic-bezier(0.95, 0.05, 0.795, 0.035);
} }
@@ -292,7 +284,7 @@ webview.focus {
#setting-tooltip { #setting-tooltip {
font-family: sans-serif; font-family: sans-serif;
background: #222c31; background: #222c31;
margin-left: 48px; margin-left: 45px;
padding: 6px 8px; padding: 6px 8px;
position: absolute; position: absolute;
margin-top: 0px; margin-top: 0px;
@@ -364,8 +356,6 @@ webview.focus {
height: 100%; height: 100%;
width: 100%; width: 100%;
position: relative; position: relative;
flex-grow: 1;
flex-basis: 0px;
} }
.hidden { .hidden {

View File

@@ -115,7 +115,7 @@ td:nth-child(odd) {
} }
.nav.active { .nav.active {
color: #4ebfac; color: #464e5a;
cursor: default; cursor: default;
position: relative; position: relative;
} }
@@ -123,19 +123,12 @@ td:nth-child(odd) {
.nav.active::before { .nav.active::before {
background: #464e5a; background: #464e5a;
width: 3px; width: 3px;
height: 18px; height: 16px;
position: absolute; position: absolute;
left: -8px; left: -8px;
content: ''; 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 { #settings-header {
font-size: 22px; font-size: 22px;
color: #222c31; color: #222c31;
@@ -151,12 +144,12 @@ td:nth-child(odd) {
} }
#new-server-container { #new-server-container {
padding-left: 42px; opacity: 1;
padding-top: 25px; transition: opacity 0.3s;
margin-right: 16px;
} }
.title { .title {
padding: 4px 0 6px 0;
font-weight: 500; font-weight: 500;
color: #222c31; color: #222c31;
} }
@@ -168,16 +161,6 @@ td:nth-child(odd) {
padding: 4px 0 6px 0; 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 { .sub-title {
padding: 4px 0 6px 0; padding: 4px 0 6px 0;
font-weight: bold; font-weight: bold;
@@ -188,40 +171,20 @@ img.server-info-icon {
width: 36px; width: 36px;
height: 36px; height: 36px;
padding: 4px; padding: 4px;
cursor: pointer;
vertical-align: middle;
} }
.server-info-left { .server-info-left {
margin: 4px 20px 0 0; margin: 10px 20px 0 0;
min-width: 40%;
} }
.server-info-right { .server-info-right {
margin-top: 4px;
width: 55%;
display: flex;
align-items: baseline;
justify-content: space-between;
flex-grow: 1; flex-grow: 1;
margin-right: 10px;
} }
.server-info-row { .server-info-row {
display: inline-block; display: flex;
margin: 5px 0 0 0; margin: 8px 0 0 0;
}
.server-info-left .server-info-row {
display: inline-flex;
align-items: inherit;
vertical-align: -2px;
position: relative;
}
.server-url {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
} }
.server-info-alias { .server-info-alias {
@@ -229,10 +192,6 @@ img.server-info-icon {
cursor: pointer; cursor: pointer;
} }
.server-url-info {
margin-right: 10px;
}
.setting-input-key { .setting-input-key {
font-size: 14px; font-size: 14px;
height: 27px; height: 27px;
@@ -246,16 +205,18 @@ img.server-info-icon {
.setting-input-value { .setting-input-value {
flex-grow: 1; flex-grow: 1;
font-size: 14px; font-size: 14px;
height: 22px;
border-radius: 3px; border-radius: 3px;
padding: 13px; padding: 7px;
border: #ededed 2px solid; border: #ededed 2px solid;
outline-width: 0; outline-width: 0;
background: transparent; background: transparent;
max-width: 450px; max-width: 500px;
} }
.setting-input-value:focus { .setting-input-value:focus {
border: #4EBFAC 2px solid; border: #7cb980 2px solid;
border-radius: 3px;
} }
.setting-block { .setting-block {
@@ -311,6 +272,7 @@ img.server-info-icon {
} }
.settings-card:hover { .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); box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 0px 0px rgba(0, 0, 0, 0.12);
} }
@@ -320,15 +282,11 @@ img.server-info-icon {
} }
.red { .red {
color: #ef5350; color: #ffffff;
padding: 8px; background: #ef5350;
border: rgba(239, 83, 80, 0.5) solid 1px; padding: 3px;
} padding-right: 10px;
padding-left: 10px;
.red:hover {
color: #e63431;
border: rgba(239, 83, 80, 0.7) solid 1px;
;
} }
.blue { .blue {
@@ -358,8 +316,8 @@ img.server-info-icon {
} }
i.open-tab-button { i.open-tab-button {
padding-left: 2px; padding: 0 5px;
font-size: 19px; font-size: 18px;
cursor: pointer; cursor: pointer;
} }
@@ -412,10 +370,17 @@ i.open-tab-button {
} }
#open-create-org-link { #open-create-org-link {
color: #666;
cursor: pointer; cursor: pointer;
text-decoration: none; text-decoration: none;
} }
#open-create-org-link:hover {
color: #005580;
;
text-decoration: underline;
}
.toggle { .toggle {
position: absolute; position: absolute;
margin-left: -9999px; margin-left: -9999px;
@@ -470,94 +435,6 @@ 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 */ /* responsive grid */
@media (max-width: 650px) { @media (max-width: 650px) {
@@ -571,53 +448,3 @@ input.toggle-round:checked+label:after {
display: none; 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;
}
}
@media (max-width: 600px) {
.divider {
margin-left: 4%;
}
.divider hr {
margin-left: 2px;
margin-right: 2px;
width: 40%;
}
}
@media (max-width: 900px) {
.settings-card {
flex-direction: column;
align-items: center;
}
.server-info-right {
flex-direction: column;
align-items: center;
}
.action {
margin-top: 10px;
}
}

View File

@@ -1,36 +0,0 @@
const { shell } = require('electron').remote;
const LinkUtil = require('../utils/link-util');
const DomainUtil = require('../utils/domain-util');
const hiddenWebView = require('../components/hidden-webview');
function handleExternalLink(event) {
const { url } = event;
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
// Whitelist URLs which are allowed to be opened in the app
const {
isInternalUrl: isWhiteListURL,
isUploadsUrl: isUploadsURL
} = LinkUtil.isInternal(domainPrefix, url);
if (isWhiteListURL) {
event.preventDefault();
// only open the pdf, mp3, mp4 etc.. in hidden webview since opening the
// image in webview will do nothing and will not save it
// whereas the pdf will be saved to user desktop once openened in
// in the hidden webview and will not trigger webview reload
if (!LinkUtil.isImage(url) && isUploadsURL) {
hiddenWebView.loadURL(url);
return;
}
// open internal urls inside the current webview.
this.$el.loadURL(url);
} else {
event.preventDefault();
shell.openExternal(url);
}
}
module.exports = handleExternalLink;

View File

@@ -1,9 +0,0 @@
// this hidden webview will be used to open pdf url and
// save it to user's computer without triggering a reload
// when navigating to pdf url to download it.
const hiddenWebView = document.createElement('webview');
hiddenWebView.classList.add('download-webview');
hiddenWebView.src = 'about:blank';
document.querySelector('#webviews-container').appendChild(hiddenWebView);
module.exports = hiddenWebView;

View File

@@ -3,12 +3,13 @@
const path = require('path'); const path = require('path');
const fs = require('fs'); const fs = require('fs');
const DomainUtil = require(__dirname + '/../utils/domain-util.js');
const ConfigUtil = require(__dirname + '/../utils/config-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 { app, dialog } = require('electron').remote; const LinkUtil = require(__dirname + '/../utils/link-util.js');
const { shell, app, dialog } = require('electron').remote;
const BaseComponent = require(__dirname + '/../components/base.js'); const BaseComponent = require(__dirname + '/../components/base.js');
const handleExternalLink = require(__dirname + '/../components/handle-external-link.js');
const shouldSilentWebview = ConfigUtil.getConfigItem('silent'); const shouldSilentWebview = ConfigUtil.getConfigItem('silent');
class WebView extends BaseComponent { class WebView extends BaseComponent {
@@ -45,7 +46,16 @@ class WebView extends BaseComponent {
registerListeners() { registerListeners() {
this.$el.addEventListener('new-window', event => { this.$el.addEventListener('new-window', event => {
handleExternalLink.call(this, event); const { url } = event;
const domainPrefix = DomainUtil.getDomain(this.props.index).url;
if (LinkUtil.isInternal(domainPrefix, url) || url === (domainPrefix + '/')) {
event.preventDefault();
this.$el.loadURL(url);
} else {
event.preventDefault();
shell.openExternal(url);
}
}); });
if (shouldSilentWebview) { if (shouldSilentWebview) {

View File

@@ -122,7 +122,7 @@ class ServerManagerView {
// Remove focus from the settings icon at sidebar bottom // Remove focus from the settings icon at sidebar bottom
this.$settingsButton.classList.remove('active'); this.$settingsButton.classList.remove('active');
} else { } else {
this.openSettings('AddServer'); this.openSettings('Servers');
} }
} }
@@ -159,7 +159,7 @@ class ServerManagerView {
this.tabs[this.activeTabIndex].webview.reload(); this.tabs[this.activeTabIndex].webview.reload();
}); });
this.$addServerButton.addEventListener('click', () => { this.$addServerButton.addEventListener('click', () => {
this.openSettings('AddServer'); this.openSettings('Servers');
}); });
this.$settingsButton.addEventListener('click', () => { this.$settingsButton.addEventListener('click', () => {
this.openSettings('General'); this.openSettings('General');
@@ -268,10 +268,10 @@ class ServerManagerView {
} }
activateLastTab(index) { activateLastTab(index) {
// Open all the tabs in background, also activate the tab based on the index // Open last active tab
this.activateTab(index);
// Save last active tab
ConfigUtil.setConfigItem('lastActiveTab', index); ConfigUtil.setConfigItem('lastActiveTab', index);
// Open all the tabs in background
this.activateTab(index);
} }
activateTab(index, hideOldTab = true) { activateTab(index, hideOldTab = true) {
@@ -311,7 +311,7 @@ class ServerManagerView {
webContents.send('toggle-sidebar', state); webContents.send('toggle-sidebar', state);
}); });
ipcRenderer.on('toggle-silent', (event, state) => { ipcRenderer.on('toogle-silent', (event, state) => {
const webviews = document.querySelectorAll('webview'); const webviews = document.querySelectorAll('webview');
webviews.forEach(webview => { webviews.forEach(webview => {
try { try {

View File

@@ -1,51 +0,0 @@
'use strict';
const BaseSection = require(__dirname + '/base-section.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const ServerInfoForm = require(__dirname + '/server-info-form.js');
class ConnectedOrgSection extends BaseSection {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="settings-pane" id="server-settings-pane">
<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>
`;
}
init() {
this.initServers();
}
initServers() {
this.props.$root.innerHTML = '';
const servers = DomainUtil.getDomains();
this.props.$root.innerHTML = this.template();
this.$serverInfoContainer = document.getElementById('server-info-container');
this.$existingServers = document.getElementById('existing-servers');
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({
$root: this.$serverInfoContainer,
server: servers[i],
index: i,
onChange: this.reloadApp
}).init();
}
}
}
module.exports = ConnectedOrgSection;

View File

@@ -0,0 +1,37 @@
'use strict';
const BaseComponent = require(__dirname + '/../../components/base.js');
const shell = require('electron').shell;
class CreateOrganziation extends BaseComponent {
constructor(props) {
super();
this.props = props;
}
template() {
return `
<div class="setting-row">
<div class="setting-description">
<span id="open-create-org-link">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;

View File

@@ -180,7 +180,7 @@ 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('toggle-silent', newValue); currentBrowserWindow.send('toogle-silent', newValue);
} }
}); });
} }

View File

@@ -8,7 +8,7 @@ class PreferenceNav extends BaseComponent {
this.props = props; this.props = props;
this.navItems = ['General', 'Network', 'AddServer', 'Organizations', 'Shortcuts']; this.navItems = ['General', 'Network', 'Servers', 'Shortcuts'];
this.init(); this.init();
} }

View File

@@ -2,7 +2,6 @@
const BaseComponent = require(__dirname + '/../../components/base.js'); const BaseComponent = require(__dirname + '/../../components/base.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js'); const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const shell = require('electron').shell;
class NewServerForm extends BaseComponent { class NewServerForm extends BaseComponent {
constructor(props) { constructor(props) {
@@ -12,26 +11,19 @@ class NewServerForm extends BaseComponent {
template() { template() {
return ` return `
<div class="server-input-container"> <div class="settings-card">
<div class="title">Organization URL</div> <div class="server-info-right">
<div class="add-server-info-row"> <div class="title">URL of Zulip organization</div>
<input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or zulip.your-organization.com"/> <div class="server-info-row">
</div> <input class="setting-input-value" autofocus placeholder="your-organization.zulipchat.com or chat.your-organization.com"/>
<div class="server-center"> </div>
<div class="server-save-action"> <div class="server-info-row">
<button id="connect">Connect</button> <div class="action blue server-save-action">
<i class="material-icons">add_box</i>
<span>Add</span>
</div>
</div> </div>
</div> </div>
<div class="server-center">
<div class="divider">
<hr class="left"/>OR<hr class="right" />
</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> </div>
`; `;
} }
@@ -51,25 +43,17 @@ class NewServerForm extends BaseComponent {
} }
submitFormHandler() { submitFormHandler() {
this.$saveServerButton.children[0].innerHTML = 'Connecting...'; 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[0].innerHTML = 'Connect'; this.$saveServerButton.children[1].innerHTML = 'Add';
alert(errorMessage); alert(errorMessage);
}); });
} }
openCreateNewOrgExternalLink() {
const link = 'https://zulipchat.com/new/';
const externalCreateNewOrgEl = document.getElementById('open-create-org-link');
externalCreateNewOrgEl.addEventListener('click', () => {
shell.openExternal(link);
});
}
initActions() { initActions() {
this.$saveServerButton.addEventListener('click', () => { this.$saveServerButton.addEventListener('click', () => {
this.submitFormHandler(); this.submitFormHandler();
@@ -81,8 +65,6 @@ class NewServerForm extends BaseComponent {
this.submitFormHandler(); this.submitFormHandler();
} }
}); });
// open create new org link in default browser
this.openCreateNewOrgExternalLink();
} }
} }

View File

@@ -7,7 +7,6 @@ const Nav = require(__dirname + '/js/pages/preference/nav.js');
const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js'); const ServersSection = require(__dirname + '/js/pages/preference/servers-section.js');
const GeneralSection = require(__dirname + '/js/pages/preference/general-section.js'); const GeneralSection = require(__dirname + '/js/pages/preference/general-section.js');
const NetworkSection = require(__dirname + '/js/pages/preference/network-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 ShortcutsSection = require(__dirname + '/js/pages/preference/shortcuts-section.js');
class PreferenceView extends BaseComponent { class PreferenceView extends BaseComponent {
@@ -40,7 +39,7 @@ class PreferenceView extends BaseComponent {
handleNavigation(navItem) { handleNavigation(navItem) {
this.nav.select(navItem); this.nav.select(navItem);
switch (navItem) { switch (navItem) {
case 'AddServer': { case 'Servers': {
this.section = new ServersSection({ this.section = new ServersSection({
$root: this.$settingsContainer $root: this.$settingsContainer
}); });
@@ -52,12 +51,6 @@ class PreferenceView extends BaseComponent {
}); });
break; break;
} }
case 'Organizations': {
this.section = new ConnectedOrgSection({
$root: this.$settingsContainer
});
break;
}
case 'Network': { case 'Network': {
this.section = new NetworkSection({ this.section = new NetworkSection({
$root: this.$settingsContainer $root: this.$settingsContainer

View File

@@ -16,18 +16,19 @@ class ServerInfoForm extends BaseComponent {
<div class="settings-card"> <div class="settings-card">
<div class="server-info-left"> <div class="server-info-left">
<img class="server-info-icon" src="${this.props.server.icon}"/> <img class="server-info-icon" src="${this.props.server.icon}"/>
</div>
<div class="server-info-right">
<div class="server-info-row"> <div class="server-info-row">
<span class="server-info-alias">${this.props.server.alias}</span> <span class="server-info-alias">${this.props.server.alias}</span>
<i class="material-icons open-tab-button">open_in_new</i> <i class="material-icons open-tab-button">open_in_new</i>
</div> </div>
</div> <div class="server-info-row">
<div class="server-info-right"> <input class="setting-input-value" disabled value="${this.props.server.url}"/>
<div class="server-info-row server-url">
<span class="server-url-info" title="${this.props.server.url}">${this.props.server.url}</span>
</div> </div>
<div class="server-info-row"> <div class="server-info-row">
<div class="action red server-delete-action"> <div class="action red server-delete-action">
<span>Disconnect</span> <i class="material-icons">indeterminate_check_box</i>
<span>Delete</span>
</div> </div>
</div> </div>
</div> </div>
@@ -43,7 +44,6 @@ class ServerInfoForm extends BaseComponent {
initForm() { initForm() {
this.$serverInfoForm = this.generateNodeFromTemplate(this.template()); this.$serverInfoForm = this.generateNodeFromTemplate(this.template());
this.$serverInfoAlias = this.$serverInfoForm.getElementsByClassName('server-info-alias')[0]; 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.$deleteServerButton = this.$serverInfoForm.getElementsByClassName('server-delete-action')[0];
this.$openServerButton = this.$serverInfoForm.getElementsByClassName('open-tab-button')[0]; this.$openServerButton = this.$serverInfoForm.getElementsByClassName('open-tab-button')[0];
this.props.$root.appendChild(this.$serverInfoForm); this.props.$root.appendChild(this.$serverInfoForm);
@@ -71,12 +71,7 @@ class ServerInfoForm extends BaseComponent {
this.$serverInfoAlias.addEventListener('click', () => { this.$serverInfoAlias.addEventListener('click', () => {
ipcRenderer.send('forward-message', 'switch-server-tab', this.props.index); 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; module.exports = ServerInfoForm;

View File

@@ -1,7 +1,10 @@
'use strict'; 'use strict';
const BaseSection = require(__dirname + '/base-section.js'); const BaseSection = require(__dirname + '/base-section.js');
const DomainUtil = require(__dirname + '/../../utils/domain-util.js');
const ServerInfoForm = require(__dirname + '/server-info-form.js');
const NewServerForm = require(__dirname + '/new-server-form.js'); const NewServerForm = require(__dirname + '/new-server-form.js');
const CreateOrganziation = require(__dirname + '/create-new-org.js');
class ServersSection extends BaseSection { class ServersSection extends BaseSection {
constructor(props) { constructor(props) {
@@ -11,14 +14,13 @@ class ServersSection extends BaseSection {
template() { template() {
return ` return `
<div class="add-server-modal"> <div class="settings-pane" id="server-settings-pane">
<div class="modal-container"> <div class="page-title">Register or login to a Zulip organization to get started</div>
<div class="settings-pane" id="server-settings-pane"> <div id="new-server-container"></div>
<div class="page-title">Add a Zulip organization</div> <div class="title" id="existing-servers"></div>
<div id="new-server-container"></div> <div id="server-info-container"></div>
</div> <div id="create-organization-container"></div>
</div> </div>
</div>
`; `;
} }
@@ -29,10 +31,35 @@ class ServersSection extends BaseSection {
initServers() { initServers() {
this.props.$root.innerHTML = ''; this.props.$root.innerHTML = '';
const servers = DomainUtil.getDomains();
this.props.$root.innerHTML = this.template(); 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.$newServerContainer = document.getElementById('new-server-container');
this.$newServerButton = document.getElementById('new-server-action');
this.$serverInfoContainer.innerHTML = servers.length ? '' : '';
// Show Existing servers if servers are there otherwise hide it
this.$existingServers.innerHTML = servers.length === 0 ? '' : 'Connected organizations';
this.initNewServerForm(); this.initNewServerForm();
this.$createOrganizationContainer = document.getElementById('create-organization-container');
this.initCreateNewOrganization();
for (let i = 0; i < servers.length; i++) {
new ServerInfoForm({
$root: this.$serverInfoContainer,
server: servers[i],
index: i,
onChange: this.reloadApp
}).init();
}
}
initCreateNewOrganization() {
new CreateOrganziation({
$root: this.$createOrganizationContainer
}).init();
} }
initNewServerForm() { initNewServerForm() {

View File

@@ -78,6 +78,14 @@ class ShortcutsSection extends BaseSection {
<td><kbd>${userOSKey}</kbd><kbd>A</kbd></td> <td><kbd>${userOSKey}</kbd><kbd>A</kbd></td>
<td>Select All</td> <td>Select All</td>
</tr> </tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>F</kbd></td>
<td>Find</td>
</tr>
<tr>
<td><kbd>${userOSKey}</kbd><kbd>G</kbd></td>
<td>Find Next</td>
</tr>
<tr> <tr>
<td><kbd>Control</kbd><kbd>${userOSKey}</kbd><kbd>Space</kbd></td> <td><kbd>Control</kbd><kbd>${userOSKey}</kbd><kbd>Space</kbd></td>
<td>Emoji & Symbols</td> <td>Emoji & Symbols</td>
@@ -113,7 +121,7 @@ class ShortcutsSection extends BaseSection {
<td>Actual Size</td> <td>Actual Size</td>
</tr> </tr>
<tr> <tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd></td> <td><kbd>${userOSKey}</kbd><kbd>S</kbd></td>
<td>Toggle Sidebar</td> <td>Toggle Sidebar</td>
</tr> </tr>
<tr> <tr>
@@ -248,7 +256,7 @@ class ShortcutsSection extends BaseSection {
<td>Actual Size</td> <td>Actual Size</td>
</tr> </tr>
<tr> <tr>
<td><kbd>${userOSKey}</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd></td> <td><kbd>${userOSKey}</kbd> + <kbd>S</kbd></td>
<td>Toggle Sidebar</td> <td>Toggle Sidebar</td>
</tr> </tr>
<tr> <tr>

View File

@@ -4,14 +4,10 @@ const { ipcRenderer } = require('electron');
const SetupSpellChecker = require('./spellchecker'); const SetupSpellChecker = require('./spellchecker');
const ConfigUtil = require(__dirname + '/utils/config-util.js'); const ConfigUtil = require(__dirname + '/utils/config-util.js');
const LinkUtil = require(__dirname + '/utils/link-util.js');
// eslint-disable-next-line import/no-unassigned-import // eslint-disable-next-line import/no-unassigned-import
require('./notification'); require('./notification');
// Prevent drag and drop event in main process which prevents remote code executaion
require(__dirname + '/shared/preventdrag.js');
const logout = () => { const logout = () => {
// Create the menu for the below // Create the menu for the below
document.querySelector('.dropdown-toggle').click(); document.querySelector('.dropdown-toggle').click();
@@ -24,7 +20,7 @@ const shortcut = () => {
// Create the menu for the below // Create the menu for the below
const node = document.querySelector('a[data-overlay-trigger=keyboard-shortcuts]'); const node = document.querySelector('a[data-overlay-trigger=keyboard-shortcuts]');
// Additional check // Additional check
if (node.text.trim().toLowerCase() === 'keyboard shortcuts (?)') { if (node.text.trim().toLowerCase() === 'keyboard shortcuts') {
node.click(); node.click();
} else { } else {
// Atleast click the dropdown // Atleast click the dropdown
@@ -56,24 +52,6 @@ document.addEventListener('DOMContentLoaded', () => {
ipcRenderer.send('forward-message', 'reload-viewer'); ipcRenderer.send('forward-message', 'reload-viewer');
}); });
} }
// Open image attachment link in the lightbox instead of opening in the default browser
const { $, lightbox } = window;
$('#main_div').on('click', '.message_content p a', function (e) {
const url = $(this).attr('href');
if (LinkUtil.isImage(url)) {
const $img = $(this).parent().siblings('.message_inline_image').find('img');
// prevent the image link from opening in a new page.
e.preventDefault();
// prevent the message compose dialog from happening.
e.stopPropagation();
lightbox.open($img);
}
});
}); });
// Clean up spellchecker events after you navigate away from this page; // Clean up spellchecker events after you navigate away from this page;
@@ -82,10 +60,3 @@ window.addEventListener('beforeunload', () => {
SetupSpellChecker.unsubscribeSpellChecker(); SetupSpellChecker.unsubscribeSpellChecker();
}); });
// electron's globalShortcut can cause unexpected results
// so adding the reload shortcut in the old-school way
document.addEventListener('keydown', event => {
if (event.code === 'F5') {
ipcRenderer.send('forward-message', 'hard-reload');
}
});

View File

@@ -1,17 +0,0 @@
'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();

View File

@@ -19,20 +19,7 @@ class LinkUtil {
const currentDomain = wurl('hostname', currentUrl); const currentDomain = wurl('hostname', currentUrl);
const newDomain = wurl('hostname', newUrl); const newDomain = wurl('hostname', newUrl);
const sameDomainUrl = (currentDomain === newDomain || newUrl === currentUrl + '/'); return (currentDomain === newDomain) && newUrl.includes('/#narrow');
const isUploadsUrl = newUrl.includes(currentUrl + '/user_uploads/');
const isInternalUrl = newUrl.includes('/#narrow') || isUploadsUrl;
return {
isInternalUrl: sameDomainUrl && isInternalUrl,
isUploadsUrl
};
}
isImage(url) {
// test for images extension as well as urls like .png?s=100
const isImageUrl = /\.(bmp|gif|jpg|jpeg|png|webp)\?*.*$/i;
return isImageUrl.test(url);
} }
} }

View File

@@ -44,5 +44,4 @@
</div> </div>
</body> </body>
<script src="js/main.js"></script> <script src="js/main.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html> </html>

View File

@@ -18,5 +18,4 @@
</div> </div>
</body> </body>
<script src="js/pages/network.js"></script> <script src="js/pages/network.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html> </html>

View File

@@ -13,5 +13,4 @@
</div> </div>
</body> </body>
<script src="js/pages/preference/preference.js"></script> <script src="js/pages/preference/preference.js"></script>
<script>require('./js/shared/preventdrag.js')</script>
</html> </html>

View File

@@ -15,7 +15,6 @@ To build and run the app from source, you'll need the following:
* [Python](https://www.python.org/downloads/release/python-2713/) * [Python](https://www.python.org/downloads/release/python-2713/)
(v2.7.x recommended) (v2.7.x recommended)
* A C++ compiler compatible with C++11 * A C++ compiler compatible with C++11
* Linux users also need [Snapcraft](https://snapcraft.io/)
* Development headers for the libXext, libXtst, and libxkbfile libraries * Development headers for the libXext, libXtst, and libxkbfile libraries
### Debian/Ubuntu and friends ### Debian/Ubuntu and friends
@@ -26,7 +25,7 @@ manager (see [here][nodesource-install] for more on the first command):
```sh ```sh
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash - $ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt install git nodejs python build-essential snapcraft libxext-dev libxtst-dev libxkbfile-dev libgconf-2-4 $ sudo apt install git nodejs python build-essential libxext-dev libxtst-dev libxkbfile-dev
``` ```
[nodesource-install]: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions [nodesource-install]: https://nodejs.org/en/download/package-manager/#debian-and-ubuntu-based-linux-distributions
@@ -77,7 +76,7 @@ This command will produce distributable packages or installers for the
operating system you're running on: operating system you're running on:
* on Windows, a Windows installer file * on Windows, a Windows installer file
* on macOS, a `.dmg` file * on macOS, a `.dmg` file
* on Linux, a plain `.zip` file as well as a `.deb` file, `.snap` file and an * on Linux, a plain `.zip` file as well as a `.deb` file and an
`AppImage` file. `AppImage` file.
To generate all three types, you will need all three operating To generate all three types, you will need all three operating
systems. systems.

9854
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{ {
"name": "zulip", "name": "zulip",
"productName": "Zulip", "productName": "Zulip",
"version": "1.9.0", "version": "1.8.2",
"main": "./app/main", "main": "./app/main",
"description": "Zulip Desktop App", "description": "Zulip Desktop App",
"license": "Apache-2.0", "license": "Apache-2.0",
@@ -22,7 +22,7 @@
}, },
"scripts": { "scripts": {
"start": "electron app --disable-http-cache --no-electron-connect", "start": "electron app --disable-http-cache --no-electron-connect",
"reinstall": "node ./tools/reinstall-node-modules.js", "reinstall": "./tools/reinstall-node-modules",
"postinstall": "electron-builder install-app-deps", "postinstall": "electron-builder install-app-deps",
"test": "xo", "test": "xo",
"test-e2e": "gulp test-e2e", "test-e2e": "gulp test-e2e",
@@ -55,8 +55,7 @@
"target": [ "target": [
"deb", "deb",
"zip", "zip",
"AppImage", "AppImage"
"snap"
], ],
"maintainer": "Akash Nimare <svnitakash@gmail.com>" "maintainer": "Akash Nimare <svnitakash@gmail.com>"
}, },
@@ -65,9 +64,6 @@
"afterInstall": "./scripts/debian-add-repo.sh", "afterInstall": "./scripts/debian-add-repo.sh",
"afterRemove": "./scripts/debian-uninstaller.sh" "afterRemove": "./scripts/debian-uninstaller.sh"
}, },
"snap": {
"synopsis": "Zulip Desktop App"
},
"dmg": { "dmg": {
"background": "build/appdmg.png", "background": "build/appdmg.png",
"icon": "build/icon.icns", "icon": "build/icon.icns",
@@ -117,8 +113,8 @@
"assert": "1.4.1", "assert": "1.4.1",
"cp-file": "^5.0.0", "cp-file": "^5.0.0",
"devtron": "1.4.0", "devtron": "1.4.0",
"electron": "1.8.4", "electron": "1.8.2",
"electron-builder": "20.8.1", "electron-builder": "20.4.1",
"electron-connect": "0.6.2", "electron-connect": "0.6.2",
"electron-debug": "1.4.0", "electron-debug": "1.4.0",
"google-translate-api": "2.3.0", "google-translate-api": "2.3.0",
@@ -127,7 +123,7 @@
"is-ci": "^1.0.10", "is-ci": "^1.0.10",
"nodemon": "^1.14.11", "nodemon": "^1.14.11",
"pre-commit": "1.2.2", "pre-commit": "1.2.2",
"spectron": "3.8.0", "spectron": "3.7.2",
"tap-colorize": "^1.2.0", "tap-colorize": "^1.2.0",
"tape": "^4.8.0", "tape": "^4.8.0",
"xo": "0.18.2" "xo": "0.18.2"
@@ -168,7 +164,8 @@
], ],
"ignore": [ "ignore": [
"tests/*.js", "tests/*.js",
"tools/locale-helper/*.js" "tools/locale-helper/*.js",
"scripts/*.js"
], ],
"envs": [ "envs": [
"node", "node",

View File

@@ -2,10 +2,6 @@
# This script runs when user install the debian package # 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 # 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 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 sudo apt-key adv --keyserver pool.sks-keyservers.net --recv 69AD12704E71A4803DCA3A682424BE5AE9BD10D9

View File

@@ -29,7 +29,3 @@ appDirectory=/home/$getSudoUser/.config/Zulip/;
if [ -d $appDirectory ]; then if [ -d $appDirectory ]; then
sudo rm -rf $appDirectory; sudo rm -rf $appDirectory;
fi fi
# Delete the link to the binary
echo 'Removing binary link'
sudo rm -f '/usr/local/bin/${executable}';

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
sudo apt-get install --no-install-recommends -y icnsutils
# to build 32 bit from a machine with 64 bit
sudo apt-get install --no-install-recommends -y gcc-multilib g++-multilib
fi

View File

@@ -0,0 +1,28 @@
#!/usr/bin/env node
if (!process.env.TRAVIS_OS_NAME === 'linux') {
process.exit(0);
}
const fs = require('fs');
const path = require('path');
// go to dist directory
process.chdir(path.resolve(__dirname, '../dist'));
const extensions = /\.deb|\.AppImage|\.zip/;
const files = fs.readdirSync(process.cwd()).filter(file => file.includes);
function changeFileName(file) {
file = file.replace(/^z/, 'Z');
file = file.replace(/_{2}/, '-');
return file;
}
// Change file name to what we want
// eg zulip_1.8.2_amd64.deb -> Zulip-1.8.2-amd64.deb
// and change file name
files.map(file => {
const newFileName = changeFileName(file);
fs.renameSync(file, newFileName);
});

View File

@@ -1,37 +0,0 @@
name: zulip
version: 1.8.2
summary: Zulip
description: Zulip Desktop Client for Linux
confinement: strict
grade: stable
icon: ../build/icon.png
apps:
zulip:
command: env TMPDIR=$XDG_RUNTIME_DIR desktop-launch $SNAP/zulip
plugs:
- desktop
- desktop-legacy
- home
- x11
- unity7
- browser-support
- network
- gsettings
- pulseaudio
- opengl
parts:
app:
plugin: dump
stage-packages:
- libasound2
- libgconf2-4
- libnotify4
- libnspr4
- libnss3
- libpcre3
- libpulse0
- libxss1
- libxtst6
source: ../dist/linux-unpacked
after:
- desktop-gtk2

View File

@@ -7,7 +7,7 @@ test('app runs', function (t) {
const app = setup.createApp() const app = setup.createApp()
setup.waitForLoad(app, t) setup.waitForLoad(app, t)
.then(() => app.client.windowByIndex(1)) // focus on webview .then(() => app.client.windowByIndex(1)) // focus on webview
.then(() => app.client.waitForExist('//*[@id="connect"]')) // id of the connect button .then(() => app.client.waitForExist('//*[@id="new-server-container"]/div/div/div[2]/input'))
.then(() => setup.endTest(app, t), .then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error')) (err) => setup.endTest(app, t, err || 'error'))
}) })

View File

@@ -1,17 +0,0 @@
const test = require('tape')
const setup = require('./setup')
// Create new org link should open in the default browser [WIP]
test('new-org-link', function (t) {
t.timeoutAfter(50e3)
setup.resetTestDataDir()
const app = setup.createApp()
setup.waitForLoad(app, t)
.then(() => app.client.windowByIndex(1)) // focus on webview
.then(() => app.client.click('#open-create-org-link')) // Click on new org link button
.then(() => setup.wait(5000))
.then(() => setup.endTest(app, t),
(err) => setup.endTest(app, t, err || 'error'))
})

View File

@@ -1,18 +0,0 @@
#!/usr/bin/env node
const {exec} = require('child_process');
const path = require('path');
const isWindows = process.platform === 'win32';
const command = path.join(__dirname, `reinstall-node-modules${isWindows ? '.cmd' : ''}`);
const proc = exec(command, error => {
if (error) {
console.error(error);
}
});
proc.stdout.on('data', data => console.log(data.toString()));
proc.stderr.on('data', data => console.error(data.toString()));
proc.on('exit', code => {
process.exit(code);
});