Compare commits
197 Commits
v0.9.8-rc.
...
luke/new-g
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
533ca9f3c6 | ||
|
|
ab24994e37 | ||
|
|
98f62d0300 | ||
|
|
2e49014ffe | ||
|
|
e67e891040 | ||
|
|
c607b1bc89 | ||
|
|
41738ec025 | ||
|
|
f9ae456a64 | ||
|
|
1b78737f12 | ||
|
|
2983b36d4c | ||
|
|
640aa98cf4 | ||
|
|
e6133820a2 | ||
|
|
5766a6e93d | ||
|
|
c783f701dd | ||
|
|
d1db602b3a | ||
|
|
ea72e9c7a0 | ||
|
|
8e94d5240f | ||
|
|
1e5908a31e | ||
|
|
40c98fc84f | ||
|
|
9c6b3819a5 | ||
|
|
6013a3e71d | ||
|
|
0deed1b511 | ||
|
|
2c36506342 | ||
|
|
309afdb813 | ||
|
|
1ed1080bf3 | ||
|
|
a09f890619 | ||
|
|
499b04c254 | ||
|
|
8a50207189 | ||
|
|
f54708227e | ||
|
|
f14c2a0a71 | ||
|
|
c27f39714a | ||
|
|
e1cd046c1a | ||
|
|
ae76c76558 | ||
|
|
1001c98a3c | ||
|
|
23a141c5ad | ||
|
|
8731e46900 | ||
|
|
da960e776d | ||
|
|
d419024da9 | ||
|
|
45efdbdd43 | ||
|
|
7b320f8fbb | ||
|
|
e6e2e6989a | ||
|
|
2bf0074953 | ||
|
|
ff709ed8b1 | ||
|
|
cbb32968f1 | ||
|
|
095da68aac | ||
|
|
9997cc9891 | ||
|
|
d5009962cd | ||
|
|
6367344181 | ||
|
|
61c338a233 | ||
|
|
425719c72e | ||
|
|
9f3c1deae6 | ||
|
|
4a8ad53bd9 | ||
|
|
67efb8bde2 | ||
|
|
d7b2f0d3be | ||
|
|
96c867b34b | ||
|
|
4eb0ca63a9 | ||
|
|
73fb2c859e | ||
|
|
431b8aaefa | ||
|
|
b637ab33cc | ||
|
|
afb4b79d81 | ||
|
|
5defd0bb74 | ||
|
|
e6f694307d | ||
|
|
beefcfcb8c | ||
|
|
44d0e7dcba | ||
|
|
71685b5eab | ||
|
|
7b3719bc3c | ||
|
|
c070e86c1f | ||
|
|
78cabed0bf | ||
|
|
bf88620581 | ||
|
|
777087504a | ||
|
|
aa482bae28 | ||
|
|
6fd64d19f0 | ||
|
|
2d311ddbf4 | ||
|
|
e24ef2bb5c | ||
|
|
4dd6ee681d | ||
|
|
ef057e4693 | ||
|
|
f53b238c24 | ||
|
|
7957eeba10 | ||
|
|
95931918dc | ||
|
|
5326faa0b8 | ||
|
|
7f2fa09d4c | ||
|
|
b6435502fc | ||
|
|
1701ea43a4 | ||
|
|
69f5833c9f | ||
|
|
61bba6c31f | ||
|
|
c067800a4f | ||
|
|
af105b1920 | ||
|
|
a414352661 | ||
|
|
57f91086e6 | ||
|
|
9e2324fb69 | ||
|
|
b3c9229aee | ||
|
|
2139fb74bb | ||
|
|
8b4ab4bcdd | ||
|
|
40eceaf9bc | ||
|
|
ab5ec04507 | ||
|
|
b1ff0b9e65 | ||
|
|
be52787473 | ||
|
|
b3431bb750 | ||
|
|
4864716abb | ||
|
|
8a6283fedf | ||
|
|
634cf528b4 | ||
|
|
0da6ca8aea | ||
|
|
08fb20a0df | ||
|
|
a8e4a2158a | ||
|
|
2f3e389a68 | ||
|
|
fb3ef89b50 | ||
|
|
523b315985 | ||
|
|
36f08cf73c | ||
|
|
5c00af7123 | ||
|
|
38fa9f78f1 | ||
|
|
8a9b750b31 | ||
|
|
26ebd20956 | ||
|
|
e877ad2e63 | ||
|
|
dd7f9e2d5f | ||
|
|
141aafd18e | ||
|
|
8aed52ece9 | ||
|
|
ce5a7ba48a | ||
|
|
95647fb7a1 | ||
|
|
d0c0eeac0a | ||
|
|
f25612198e | ||
|
|
ca6d9063fd | ||
|
|
eec0c274d2 | ||
|
|
b5a5b472c4 | ||
|
|
9747836a4d | ||
|
|
2afe9e6928 | ||
|
|
f010afe63f | ||
|
|
60b280f166 | ||
|
|
a2b465e352 | ||
|
|
1e139ef183 | ||
|
|
9b38a97110 | ||
|
|
9c27075175 | ||
|
|
52a119244b | ||
|
|
ddd12edc06 | ||
|
|
8f20fcfa6b | ||
|
|
c1d30664de | ||
|
|
1803a0df9e | ||
|
|
a5bd8bd034 | ||
|
|
751f715e77 | ||
|
|
a5bd8b36b8 | ||
|
|
e780fe4067 | ||
|
|
0480a880f3 | ||
|
|
acbc3e9f9b | ||
|
|
a8daa53a5b | ||
|
|
053beae035 | ||
|
|
ccc33db9dd | ||
|
|
66a473863a | ||
|
|
25907301a3 | ||
|
|
d8bf57edc5 | ||
|
|
269f9a5ccc | ||
|
|
a34b5abc82 | ||
|
|
062cf47290 | ||
|
|
fa132d3945 | ||
|
|
9fd379c97c | ||
|
|
593e101521 | ||
|
|
6fd6ea9b8e | ||
|
|
f8aa2c3487 | ||
|
|
44479f24dd | ||
|
|
5ff49f4000 | ||
|
|
cc7a585126 | ||
|
|
d3331f293b | ||
|
|
c6ee221ae4 | ||
|
|
8351ec9738 | ||
|
|
27de972bfb | ||
|
|
e5e259e1f8 | ||
|
|
a74bbb424c | ||
|
|
15accf33f5 | ||
|
|
3fb54029d7 | ||
|
|
76008e8abd | ||
|
|
3f291aae5b | ||
|
|
76f140c62c | ||
|
|
a12d7a2b66 | ||
|
|
ba8745cb42 | ||
|
|
967ebb5f9f | ||
|
|
6a5d0dda2f | ||
|
|
6423f7ce03 | ||
|
|
4ead2f338d | ||
|
|
55ec7dcebc | ||
|
|
8587df1dd9 | ||
|
|
d5b1db064f | ||
|
|
4efb2b6750 | ||
|
|
cbfa4dd1ab | ||
|
|
b26c460f13 | ||
|
|
dd9150cef1 | ||
|
|
ab1b377a1d | ||
|
|
3fa8460ed7 | ||
|
|
7f6d9a625d | ||
|
|
0b266d2e83 | ||
|
|
7907d41ea8 | ||
|
|
32c6482dcf | ||
|
|
ea0c41a9f8 | ||
|
|
a1cb566761 | ||
|
|
01a96d92f4 | ||
|
|
0b1745bceb | ||
|
|
7d203f40cd | ||
|
|
b8b74402c1 | ||
|
|
99923b7b8f | ||
|
|
c4bc38f0d7 |
2
.gitignore
vendored
@@ -5,6 +5,7 @@
|
||||
/key.pem
|
||||
/lib
|
||||
/node_modules
|
||||
/electron/node_modules
|
||||
/packages/
|
||||
/webapp
|
||||
/.npmrc
|
||||
@@ -12,3 +13,4 @@
|
||||
npm-debug.log
|
||||
electron/dist
|
||||
electron/pub
|
||||
/config.json
|
||||
|
||||
43
CHANGELOG.md
@@ -1,3 +1,46 @@
|
||||
Changes in [0.9.9](https://github.com/vector-im/riot-web/releases/tag/v0.9.9) (2017-04-25)
|
||||
==========================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.9-rc.2...v0.9.9)
|
||||
|
||||
* No changes
|
||||
|
||||
|
||||
Changes in [0.9.9-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.9.9-rc.2) (2017-04-24)
|
||||
====================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.9-rc.1...v0.9.9-rc.2)
|
||||
|
||||
* Fix bug where links to Riot would fail to open.
|
||||
|
||||
|
||||
Changes in [0.9.9-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.9-rc.1) (2017-04-21)
|
||||
====================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8...v0.9.9-rc.1)
|
||||
|
||||
* Update js-sdk and matrix-react-sdk to fix registration without a captcha (https://github.com/vector-im/riot-web/issues/3621)
|
||||
|
||||
|
||||
Changes in [0.9.8](https://github.com/vector-im/riot-web/releases/tag/v0.9.8) (2017-04-12)
|
||||
==========================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8-rc.3...v0.9.8)
|
||||
|
||||
* No changes
|
||||
|
||||
Changes in [0.9.8-rc.3](https://github.com/vector-im/riot-web/releases/tag/v0.9.8-rc.3) (2017-04-11)
|
||||
====================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8-rc.2...v0.9.8-rc.3)
|
||||
|
||||
* Make the clear cache button work on desktop
|
||||
[\#3598](https://github.com/vector-im/riot-web/pull/3598)
|
||||
|
||||
Changes in [0.9.8-rc.2](https://github.com/vector-im/riot-web/releases/tag/v0.9.8-rc.2) (2017-04-10)
|
||||
====================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.8-rc.1...v0.9.8-rc.2)
|
||||
|
||||
* Redacted events bg: black lozenge -> torn paper
|
||||
[\#3596](https://github.com/vector-im/riot-web/pull/3596)
|
||||
* Add 'app' parameter to rageshake report
|
||||
[\#3594](https://github.com/vector-im/riot-web/pull/3594)
|
||||
|
||||
Changes in [0.9.8-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.8-rc.1) (2017-04-07)
|
||||
====================================================================================================
|
||||
[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.7...v0.9.8-rc.1)
|
||||
|
||||
@@ -135,7 +135,7 @@ To run as a desktop app:
|
||||
|
||||
```
|
||||
npm install electron
|
||||
node_modules/.bin/electron .
|
||||
npm run electron
|
||||
```
|
||||
|
||||
To build packages, use electron-builder. This is configured to output:
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"brand": "Riot",
|
||||
"integrations_ui_url": "https://scalar.vector.im/",
|
||||
"integrations_rest_url": "https://scalar.vector.im/api",
|
||||
"bug_report_endpoint_url": "https://vector.im/bugs",
|
||||
"bug_report_endpoint_url": "https://riot.im/bugreports/submit",
|
||||
"enableLabs": true,
|
||||
"roomDirectory": {
|
||||
"servers": [
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 7.2 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 673 B After Width: | Height: | Size: 673 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
electron_app/img/riot.ico
Normal file
|
After Width: | Height: | Size: 102 KiB |
BIN
electron_app/img/riot.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
11
electron_app/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "src/electron-main.js",
|
||||
"version": "0.9.9",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"dependencies": {
|
||||
"electron-window-state": "^4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,8 @@ const tray = require('./tray');
|
||||
|
||||
const VectorMenu = require('./vectormenu');
|
||||
|
||||
const windowStateKeeper = require('electron-window-state');
|
||||
|
||||
let vectorConfig = {};
|
||||
try {
|
||||
vectorConfig = require('../../webapp/config.json');
|
||||
@@ -171,6 +173,7 @@ const shouldQuit = electron.app.makeSingleInstance((commandLine, workingDirector
|
||||
});
|
||||
|
||||
if (shouldQuit) {
|
||||
console.log("Other instance detected: exiting");
|
||||
electron.app.quit()
|
||||
}
|
||||
|
||||
@@ -186,11 +189,21 @@ electron.app.on('ready', () => {
|
||||
process.platform == 'win32' ? 'ico' : 'png'
|
||||
);
|
||||
|
||||
// Load the previous window state with fallback to defaults
|
||||
let mainWindowState = windowStateKeeper({
|
||||
defaultWidth: 1024,
|
||||
defaultHeight: 768,
|
||||
});
|
||||
|
||||
mainWindow = new electron.BrowserWindow({
|
||||
icon: icon_path,
|
||||
width: 1024, height: 768,
|
||||
show: false,
|
||||
autoHideMenuBar: true,
|
||||
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
width: mainWindowState.width,
|
||||
height: mainWindowState.height,
|
||||
});
|
||||
mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`);
|
||||
electron.Menu.setApplicationMenu(VectorMenu);
|
||||
@@ -201,9 +214,12 @@ electron.app.on('ready', () => {
|
||||
brand: vectorConfig.brand || 'Riot'
|
||||
});
|
||||
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
if (!process.argv.includes('--hidden')) {
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
}
|
||||
|
||||
mainWindow.on('closed', () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
@@ -226,6 +242,8 @@ electron.app.on('ready', () => {
|
||||
onLinkContextMenu(ev, params);
|
||||
}
|
||||
});
|
||||
|
||||
mainWindowState.manage(mainWindow);
|
||||
});
|
||||
|
||||
electron.app.on('window-all-closed', () => {
|
||||
48
package.json
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"name": "riot-web",
|
||||
"productName": "Riot",
|
||||
"main": "electron/src/electron-main.js",
|
||||
"version": "0.9.8-rc.1",
|
||||
"main": "electron_app/src/electron-main.js",
|
||||
"version": "0.9.9",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "Vector Creations Ltd.",
|
||||
"repository": {
|
||||
@@ -30,16 +30,18 @@
|
||||
"build:res": "node scripts/copy-res.js",
|
||||
"build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js",
|
||||
"build:compile": "babel --source-maps -d lib src",
|
||||
"build:bundle": "NODE_ENV=production webpack -p --progress",
|
||||
"build:bundle": "cross-env NODE_ENV=production webpack -p --progress",
|
||||
"build:bundle:dev": "webpack --optimize-occurence-order --progress",
|
||||
"build:electron": "npm run clean && npm run build && build -wml --ia32 --x64",
|
||||
"build": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle",
|
||||
"build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle:dev",
|
||||
"build": "npm run build:res && npm run build:bundle",
|
||||
"build:dev": "npm run build:res && npm run build:bundle:dev",
|
||||
"dist": "scripts/package.sh",
|
||||
"install:electron": "install-app-deps",
|
||||
"electron": "npm run install:electron && electron .",
|
||||
"start:res": "node scripts/copy-res.js -w",
|
||||
"start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js --output-chunk-file=bundles/_dev_/[name].js -w --progress",
|
||||
"start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress",
|
||||
"start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\"",
|
||||
"start:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress",
|
||||
"start": "parallelshell \"npm run start:res\" \"npm run start:js\"",
|
||||
"start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"",
|
||||
"lint": "eslint src/",
|
||||
"lintall": "eslint src/ test/",
|
||||
@@ -56,21 +58,21 @@
|
||||
"draft-js": "^0.8.1",
|
||||
"extract-text-webpack-plugin": "^0.9.1",
|
||||
"favico.js": "^0.3.10",
|
||||
"filesize": "^3.1.2",
|
||||
"filesize": "3.5.6",
|
||||
"flux": "~2.0.3",
|
||||
"gemini-scrollbar": "matrix-org/gemini-scrollbar#b302279",
|
||||
"gfm.css": "^1.1.1",
|
||||
"highlight.js": "^9.0.0",
|
||||
"linkifyjs": "^2.1.3",
|
||||
"matrix-js-sdk": "0.7.6-rc.1",
|
||||
"matrix-react-sdk": "0.8.7-rc.1",
|
||||
"matrix-js-sdk": "matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "matrix-org/matrix-react-sdk#develop",
|
||||
"modernizr": "^3.1.0",
|
||||
"pako": "^1.0.5",
|
||||
"q": "^1.4.1",
|
||||
"react": "^15.4.0",
|
||||
"react-dnd": "^2.1.4",
|
||||
"react-dnd-html5-backend": "^2.1.2",
|
||||
"react-dom": "^15.4.0",
|
||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#5e97aef",
|
||||
"react-gemini-scrollbar": "matrix-org/react-gemini-scrollbar#39d858c",
|
||||
"sanitize-html": "^1.11.1",
|
||||
"ua-parser-js": "^0.7.10",
|
||||
"url": "^0.11.0"
|
||||
@@ -93,6 +95,7 @@
|
||||
"babel-preset-stage-2": "^6.17.0",
|
||||
"chokidar": "^1.6.1",
|
||||
"cpx": "^1.3.2",
|
||||
"cross-env": "^4.0.0",
|
||||
"css-raw-loader": "^0.1.1",
|
||||
"electron-builder": "^11.2.4",
|
||||
"electron-builder-squirrel-windows": "^11.2.1",
|
||||
@@ -138,16 +141,18 @@
|
||||
"build": {
|
||||
"appId": "im.riot.app",
|
||||
"category": "Network",
|
||||
"electronVersion": "1.4.14",
|
||||
"electronVersion": "1.6.2",
|
||||
"//asar=false": "https://github.com/electron-userland/electron-builder/issues/675",
|
||||
"asar": false,
|
||||
"dereference": true,
|
||||
"//files": "We bundle everything, so we only need to include webapp/",
|
||||
"files": [
|
||||
"electron/src/**",
|
||||
"electron/img/**",
|
||||
"webapp/**",
|
||||
"package.json"
|
||||
"node_modules/**",
|
||||
"src/**",
|
||||
"img/**"
|
||||
],
|
||||
"extraResources": [
|
||||
"webapp/**/*"
|
||||
],
|
||||
"linux": {
|
||||
"target": "deb",
|
||||
@@ -158,10 +163,11 @@
|
||||
},
|
||||
"win": {
|
||||
"target": "squirrel"
|
||||
},
|
||||
"directories": {
|
||||
"buildResources": "electron_app/build",
|
||||
"output": "electron_app/dist",
|
||||
"app": "electron_app"
|
||||
}
|
||||
},
|
||||
"directories": {
|
||||
"buildResources": "electron/build",
|
||||
"output": "electron/dist"
|
||||
}
|
||||
}
|
||||
|
||||
17
release.sh
@@ -1,12 +1,25 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
#
|
||||
# Script to perform a release of vector-web.
|
||||
#
|
||||
# Requires github-changelog-generator; to install, do
|
||||
# Requires github-changelog-generator; to install, do
|
||||
# pip install git+https://github.com/matrix-org/github-changelog-generator.git
|
||||
|
||||
set -e
|
||||
|
||||
cd `dirname $0`
|
||||
|
||||
|
||||
# bump Electron's package.json first
|
||||
release="${1#v}"
|
||||
tag="v${release}"
|
||||
echo "electron npm version"
|
||||
|
||||
cd electron_app
|
||||
npm version --no-git-tag-version "$release"
|
||||
git commit package.json -m "$tag"
|
||||
|
||||
|
||||
cd ..
|
||||
|
||||
exec ./node_modules/matrix-js-sdk/release.sh -z "$@"
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var exec = require('child_process').exec;
|
||||
|
||||
// Makes sure the babel executable in the path is babel 6 (or greater), not
|
||||
// babel 5, which it is if you upgrade from an older version of react-sdk and
|
||||
// run 'npm install' since the package has changed to babel-cli, so 'babel'
|
||||
// remains installed and the executable in node_modules/.bin remains as babel
|
||||
// 5.
|
||||
|
||||
// This script is duplicated from matrix-react-sdk because it can't reliably
|
||||
// be pulled in from react-sdk while npm install is failing, as it will do
|
||||
// if the environment is in the erroneous state this script checks for.
|
||||
|
||||
exec("babel -V", function (error, stdout, stderr) {
|
||||
if ((error && error.code) || parseInt(stdout.substr(0,1), 10) < 6) {
|
||||
console.log("\033[31m\033[1m"+
|
||||
'*****************************************\n'+
|
||||
'* vector-web has moved to babel 6 *\n'+
|
||||
'* Please "rm -rf node_modules && npm i" *\n'+
|
||||
'* then restore links as appropriate *\n'+
|
||||
'*****************************************\n'+
|
||||
"\033[91m");
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -10,6 +10,7 @@ const COPY_LIST = [
|
||||
["res/{media,vector-icons}/**", "webapp"],
|
||||
["src/skins/vector/{fonts,img}/**", "webapp"],
|
||||
["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"],
|
||||
["node_modules/emojione/assets/png/*", "webapp/emojione/png/"],
|
||||
["./config.json", "webapp", {directwatch: 1}],
|
||||
];
|
||||
|
||||
|
||||
@@ -90,8 +90,8 @@ npm run build:electron
|
||||
|
||||
popd
|
||||
|
||||
distdir="$builddir/electron/dist"
|
||||
pubdir="$projdir/electron/pub"
|
||||
distdir="$builddir/electron_app/dist"
|
||||
pubdir="$projdir/electron_app/pub"
|
||||
rm -r "$pubdir" || true
|
||||
mkdir -p "$pubdir"
|
||||
|
||||
@@ -120,11 +120,11 @@ cp $distdir/win/*.nupkg "$pubdir/update/win32/x64/"
|
||||
cp $distdir/win/RELEASES "$pubdir/update/win32/x64/"
|
||||
|
||||
# Move the debs to the main project dir's dist folder
|
||||
rm -r "$projdir/electron/dist" || true
|
||||
mkdir -p "$projdir/electron/dist"
|
||||
cp $distdir/*.deb "$projdir/electron/dist/"
|
||||
rm -r "$projdir/electron_app/dist" || true
|
||||
mkdir -p "$projdir/electron_app/dist"
|
||||
cp $distdir/*.deb "$projdir/electron_app/dist/"
|
||||
|
||||
rm -rf "$builddir"
|
||||
|
||||
echo "Riot Desktop is ready to go in $pubdir: this directory can be hosted on your web server."
|
||||
echo "deb archives are in electron/dist/ - these should be added into your debian repository"
|
||||
echo "deb archives are in electron_app/dist/ - these should be added into your debian repository"
|
||||
|
||||
40
scripts/issues-burndown.pl
Normal file → Executable file
@@ -4,16 +4,24 @@ use warnings;
|
||||
use strict;
|
||||
|
||||
use Net::GitHub;
|
||||
use DateTime;
|
||||
use DateTime::Format::ISO8601;
|
||||
use Time::Moment;
|
||||
use Term::ReadPassword;
|
||||
|
||||
# This version of the script emits the cumulative number of bugs, split into open & closed
|
||||
# suitable for drawing the 'top' and 'bottom' of a burndown graph.
|
||||
#
|
||||
# N.B. this doesn't take into account issues changing priority over time, but only their most recent priority.
|
||||
#
|
||||
# If you want instead the number of open issues on a given day, then look at issues-no-state.pl
|
||||
|
||||
my $gh = Net::GitHub->new(
|
||||
login => 'ara4n', pass => 'secret'
|
||||
login => 'ara4n', pass => read_password("github password: "),
|
||||
);
|
||||
|
||||
$gh->set_default_user_repo('vector-im', 'vector-web');
|
||||
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
#my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all' });
|
||||
while ($gh->issue->has_next_page) {
|
||||
push @issues, $gh->issue->next_page;
|
||||
}
|
||||
@@ -30,11 +38,11 @@ while ($gh->issue->has_next_page) {
|
||||
|
||||
my $days = {};
|
||||
my $schema = {};
|
||||
my $now = DateTime->now();
|
||||
my $now = Time::Moment->now;
|
||||
|
||||
foreach my $issue (@issues) {
|
||||
next if ($issue->{pull_request});
|
||||
|
||||
|
||||
# use Data::Dumper;
|
||||
# print STDERR Dumper($issue);
|
||||
|
||||
@@ -56,10 +64,10 @@ foreach my $issue (@issues) {
|
||||
my $priority = &$extract_labels(qw(p1 p2 p3 p4 p5)) || "unprioritised";
|
||||
my $severity = &$extract_labels(qw(minor major critical cosmetic network)) || "no-severity";
|
||||
|
||||
my $start = DateTime::Format::ISO8601->parse_datetime($issue->{created_at});
|
||||
my $start = Time::Moment->from_string($issue->{created_at});
|
||||
|
||||
do {
|
||||
my $ymd = $start->ymd();
|
||||
my $ymd = $start->strftime('%F');
|
||||
|
||||
$days->{ $ymd }->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
@@ -68,13 +76,14 @@ foreach my $issue (@issues) {
|
||||
$schema->{ 'created' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
}
|
||||
|
||||
$start = $start->add(days => 1);
|
||||
} while (DateTime->compare($start, $now) < 0);
|
||||
$start = $start->plus_days(1);
|
||||
# print STDERR "^";
|
||||
} while ($start->compare($now) < 0);
|
||||
|
||||
if ($state eq 'closed') {
|
||||
my $end = DateTime::Format::ISO8601->parse_datetime($issue->{closed_at});
|
||||
my $end = Time::Moment->from_string($issue->{closed_at});
|
||||
do {
|
||||
my $ymd = $end->ymd();
|
||||
my $ymd = $end->strftime('%F');
|
||||
|
||||
$days->{ $ymd }->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ total }++;
|
||||
@@ -83,9 +92,12 @@ foreach my $issue (@issues) {
|
||||
$schema->{ 'resolved' }->{ $type }->{ $priority }->{ $severity }->{ $_ }++;
|
||||
}
|
||||
|
||||
$end = $end->add(days => 1);
|
||||
} while (DateTime->compare($end, $now) < 0);
|
||||
$end = $end->plus_days(1);
|
||||
} while ($end->compare($now) < 0);
|
||||
# print STDERR "v";
|
||||
}
|
||||
|
||||
# print STDERR "\n";
|
||||
}
|
||||
|
||||
print "day,";
|
||||
|
||||
@@ -6,14 +6,22 @@ use strict;
|
||||
use Net::GitHub;
|
||||
use DateTime;
|
||||
use DateTime::Format::ISO8601;
|
||||
use Term::ReadPassword;
|
||||
|
||||
# This version of the script emits the total number of bugs open on a given day,
|
||||
# split by various tags.
|
||||
#
|
||||
# If you want instead the cumulative number of open & closed issues on a given day,
|
||||
# then look at issues-burndown.pl
|
||||
|
||||
my $gh = Net::GitHub->new(
|
||||
login => 'ara4n', pass => 'secret'
|
||||
login => 'ara4n', pass => read_password("github password: "),
|
||||
);
|
||||
|
||||
$gh->set_default_user_repo('vector-im', 'vector-web');
|
||||
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
#my @issues = $gh->issue->repos_issues({ state => 'all', milestone => 3 });
|
||||
my @issues = $gh->issue->repos_issues({ state => 'all' });
|
||||
while ($gh->issue->has_next_page) {
|
||||
push @issues, $gh->issue->next_page;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ cp "$tmpdir/256.png" "$tmpdir/Riot.iconset/icon_256x256.png"
|
||||
cp "$tmpdir/512.png" "$tmpdir/Riot.iconset/icon_256x256@2x.png"
|
||||
cp "$tmpdir/512.png" "$tmpdir/Riot.iconset/icon_512x512.png"
|
||||
cp "$tmpdir/1024.png" "$tmpdir/Riot.iconset/icon_512x512@2x.png"
|
||||
iconutil -c icns -o electron/build/icon.icns "$tmpdir/Riot.iconset"
|
||||
iconutil -c icns -o electron_app/build/icon.icns "$tmpdir/Riot.iconset"
|
||||
|
||||
cp "$tmpdir/36.png" "res/vector-icons/android-chrome-36x36.png"
|
||||
cp "$tmpdir/48.png" "res/vector-icons/android-chrome-48x48.png"
|
||||
@@ -79,15 +79,17 @@ cp "$tmpdir/144.png" "res/vector-icons/mstile-144x144.png"
|
||||
cp "$tmpdir/150.png" "res/vector-icons/mstile-150x150.png"
|
||||
cp "$tmpdir/310.png" "res/vector-icons/mstile-310x310.png"
|
||||
cp "$tmpdir/310x150.png" "res/vector-icons/mstile-310x150.png"
|
||||
cp "$tmpdir/180.png" "electron_app/img/riot.png"
|
||||
|
||||
convert "$tmpdir/16.png" "$tmpdir/32.png" "$tmpdir/64.png" "$tmpdir/128.png" "$tmpdir/256.png" "res/vector-icons/favicon.ico"
|
||||
|
||||
cp "res/vector-icons/favicon.ico" "electron/build/icon.ico"
|
||||
cp "res/vector-icons/favicon.ico" "electron_app/build/icon.ico"
|
||||
cp "res/vector-icons/favicon.ico" "electron_app/img/riot.ico"
|
||||
|
||||
# https://github.com/electron-userland/electron-builder/blob/3f97b86993d4ea5172e562b182230a194de0f621/src/targets/LinuxTargetHelper.ts#L127
|
||||
for i in 24 96 16 48 64 128 256 512
|
||||
do
|
||||
cp "$tmpdir/$i.png" "electron/build/icons/${i}x${i}.png"
|
||||
cp "$tmpdir/$i.png" "electron_app/build/icons/${i}x${i}.png"
|
||||
done
|
||||
|
||||
rm -r "$tmpdir"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
@@ -22,7 +22,14 @@ cp config.sample.json webapp/
|
||||
|
||||
mkdir -p dist
|
||||
cp -r webapp vector-$version
|
||||
echo $version > vector-$version/version
|
||||
|
||||
# if $version looks like semver with leading v, strip it before writing to file
|
||||
if [[ ${version} =~ ^v[[:digit:]]+\.[[:digit:]]+\.[[:digit:]]+(-.+)?$ ]]; then
|
||||
echo ${version:1} > vector-$version/version
|
||||
else
|
||||
echo ${version} > vector-$version/version
|
||||
fi
|
||||
|
||||
tar chvzf dist/vector-$version.tar.gz vector-$version
|
||||
rm -r vector-$version
|
||||
|
||||
|
||||
@@ -19,9 +19,6 @@ limitations under the License.
|
||||
* You can edit it you like, but your changes will be overwritten,
|
||||
* so you'd just be trying to swim upstream like a salmon.
|
||||
* You are not a salmon.
|
||||
*
|
||||
* To update it, run:
|
||||
* ./reskindex.js -h header
|
||||
*/
|
||||
|
||||
module.exports.components = require('matrix-react-sdk/lib/component-index').components;
|
||||
@@ -40,6 +37,8 @@ import structures$RoomDirectory from './components/structures/RoomDirectory';
|
||||
structures$RoomDirectory && (module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory);
|
||||
import structures$RoomSubList from './components/structures/RoomSubList';
|
||||
structures$RoomSubList && (module.exports.components['structures.RoomSubList'] = structures$RoomSubList);
|
||||
import structures$RoomSubListHeader from './components/structures/RoomSubListHeader';
|
||||
structures$RoomSubListHeader && (module.exports.components['structures.RoomSubListHeader'] = structures$RoomSubListHeader);
|
||||
import structures$SearchBox from './components/structures/SearchBox';
|
||||
structures$SearchBox && (module.exports.components['structures.SearchBox'] = structures$SearchBox);
|
||||
import structures$ViewSource from './components/structures/ViewSource';
|
||||
@@ -64,6 +63,8 @@ import views$globals$MatrixToolbar from './components/views/globals/MatrixToolba
|
||||
views$globals$MatrixToolbar && (module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar);
|
||||
import views$globals$NewVersionBar from './components/views/globals/NewVersionBar';
|
||||
views$globals$NewVersionBar && (module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar);
|
||||
import views$globals$PasswordNagBar from './components/views/globals/PasswordNagBar';
|
||||
views$globals$PasswordNagBar && (module.exports.components['views.globals.PasswordNagBar'] = views$globals$PasswordNagBar);
|
||||
import views$login$VectorCustomServerDialog from './components/views/login/VectorCustomServerDialog';
|
||||
views$login$VectorCustomServerDialog && (module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog);
|
||||
import views$login$VectorLoginFooter from './components/views/login/VectorLoginFooter';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,13 +15,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var React = require('react');
|
||||
var ReactDOM = require('react-dom');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
|
||||
import React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'BottomLeftMenu',
|
||||
@@ -30,121 +26,28 @@ module.exports = React.createClass({
|
||||
teamToken: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return({
|
||||
directoryHover : false,
|
||||
roomsHover : false,
|
||||
homeHover: false,
|
||||
peopleHover : false,
|
||||
settingsHover : false,
|
||||
});
|
||||
},
|
||||
|
||||
// Room events
|
||||
onDirectoryClick: function() {
|
||||
dis.dispatch({ action: 'view_room_directory' });
|
||||
},
|
||||
|
||||
onDirectoryMouseEnter: function() {
|
||||
this.setState({ directoryHover: true });
|
||||
},
|
||||
|
||||
onDirectoryMouseLeave: function() {
|
||||
this.setState({ directoryHover: false });
|
||||
},
|
||||
|
||||
onRoomsClick: function() {
|
||||
dis.dispatch({ action: 'view_create_room' });
|
||||
},
|
||||
|
||||
onRoomsMouseEnter: function() {
|
||||
this.setState({ roomsHover: true });
|
||||
},
|
||||
|
||||
onRoomsMouseLeave: function() {
|
||||
this.setState({ roomsHover: false });
|
||||
},
|
||||
|
||||
// Home button events
|
||||
onHomeClick: function() {
|
||||
dis.dispatch({ action: 'view_home_page' });
|
||||
},
|
||||
|
||||
onHomeMouseEnter: function() {
|
||||
this.setState({ homeHover: true });
|
||||
},
|
||||
|
||||
onHomeMouseLeave: function() {
|
||||
this.setState({ homeHover: false });
|
||||
},
|
||||
|
||||
// People events
|
||||
onPeopleClick: function() {
|
||||
dis.dispatch({ action: 'view_create_chat' });
|
||||
},
|
||||
|
||||
onPeopleMouseEnter: function() {
|
||||
this.setState({ peopleHover: true });
|
||||
},
|
||||
|
||||
onPeopleMouseLeave: function() {
|
||||
this.setState({ peopleHover: false });
|
||||
},
|
||||
|
||||
// Settings events
|
||||
onSettingsClick: function() {
|
||||
dis.dispatch({ action: 'view_user_settings' });
|
||||
},
|
||||
|
||||
onSettingsMouseEnter: function() {
|
||||
this.setState({ settingsHover: true });
|
||||
},
|
||||
|
||||
onSettingsMouseLeave: function() {
|
||||
this.setState({ settingsHover: false });
|
||||
},
|
||||
|
||||
// Get the label/tooltip to show
|
||||
getLabel: function(label, show) {
|
||||
if (show) {
|
||||
var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
|
||||
return <RoomTooltip className="mx_BottomLeftMenu_tooltip" label={label} />;
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
const HomeButton = sdk.getComponent('elements.HomeButton');
|
||||
const StartChatButton = sdk.getComponent('elements.StartChatButton');
|
||||
const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton');
|
||||
const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton');
|
||||
const SettingsButton = sdk.getComponent('elements.SettingsButton');
|
||||
|
||||
var homeButton;
|
||||
if (this.props.teamToken) {
|
||||
homeButton = (
|
||||
<AccessibleButton className="mx_BottomLeftMenu_homePage" onClick={ this.onHomeClick } onMouseEnter={ this.onHomeMouseEnter } onMouseLeave={ this.onHomeMouseLeave } >
|
||||
<TintableSvg src="img/icons-home.svg" width="25" height="25" />
|
||||
{ this.getLabel("Welcome page", this.state.homeHover) }
|
||||
</AccessibleButton>
|
||||
);
|
||||
homeButton = <HomeButton tooltip={true} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_BottomLeftMenu">
|
||||
<div className="mx_BottomLeftMenu_options">
|
||||
{ homeButton }
|
||||
<AccessibleButton className="mx_BottomLeftMenu_people" onClick={ this.onPeopleClick } onMouseEnter={ this.onPeopleMouseEnter } onMouseLeave={ this.onPeopleMouseLeave } >
|
||||
<TintableSvg src="img/icons-people.svg" width="25" height="25" />
|
||||
{ this.getLabel("Start chat", this.state.peopleHover) }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_BottomLeftMenu_directory" onClick={ this.onDirectoryClick } onMouseEnter={ this.onDirectoryMouseEnter } onMouseLeave={ this.onDirectoryMouseLeave } >
|
||||
<TintableSvg src="img/icons-directory.svg" width="25" height="25"/>
|
||||
{ this.getLabel("Room directory", this.state.directoryHover) }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_BottomLeftMenu_createRoom" onClick={ this.onRoomsClick } onMouseEnter={ this.onRoomsMouseEnter } onMouseLeave={ this.onRoomsMouseLeave } >
|
||||
<TintableSvg src="img/icons-create-room.svg" width="25" height="25" />
|
||||
{ this.getLabel("Create new room", this.state.roomsHover) }
|
||||
</AccessibleButton>
|
||||
<AccessibleButton className="mx_BottomLeftMenu_settings" onClick={ this.onSettingsClick } onMouseEnter={ this.onSettingsMouseEnter } onMouseLeave={ this.onSettingsMouseLeave } >
|
||||
<TintableSvg src="img/icons-settings.svg" width="25" height="25" />
|
||||
{ this.getLabel("Settings", this.state.settingsHover) }
|
||||
</AccessibleButton>
|
||||
<StartChatButton tooltip={true} />
|
||||
<RoomDirectoryButton tooltip={true} />
|
||||
<CreateRoomButton tooltip={true} />
|
||||
<span className="mx_BottomLeftMenu_settings">
|
||||
<SettingsButton tooltip={true} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -19,9 +19,11 @@ limitations under the License.
|
||||
var React = require('react');
|
||||
var DragDropContext = require('react-dnd').DragDropContext;
|
||||
var HTML5Backend = require('react-dnd-html5-backend');
|
||||
var KeyCode = require('matrix-react-sdk/lib/KeyCode');
|
||||
var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
|
||||
|
||||
var VectorConferenceHandler = require('../../VectorConferenceHandler');
|
||||
var CallHandler = require("matrix-react-sdk/lib/CallHandler");
|
||||
|
||||
@@ -40,6 +42,10 @@ var LeftPanel = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.focusedElement = null;
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
},
|
||||
@@ -62,6 +68,91 @@ var LeftPanel = React.createClass({
|
||||
}
|
||||
},
|
||||
|
||||
_onFocus: function(ev) {
|
||||
this.focusedElement = ev.target;
|
||||
},
|
||||
|
||||
_onBlur: function(ev) {
|
||||
this.focusedElement = null;
|
||||
},
|
||||
|
||||
_onKeyDown: function(ev) {
|
||||
if (!this.focusedElement) return;
|
||||
let handled = false;
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case KeyCode.UP:
|
||||
this._onMoveFocus(true);
|
||||
handled = true;
|
||||
break;
|
||||
case KeyCode.DOWN:
|
||||
this._onMoveFocus(false);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
_onMoveFocus: function(up) {
|
||||
var element = this.focusedElement;
|
||||
|
||||
// unclear why this isn't needed
|
||||
// var descending = (up == this.focusDirection) ? this.focusDescending : !this.focusDescending;
|
||||
// this.focusDirection = up;
|
||||
|
||||
var descending = false; // are we currently descending or ascending through the DOM tree?
|
||||
var classes;
|
||||
|
||||
do {
|
||||
var child = up ? element.lastElementChild : element.firstElementChild;
|
||||
var sibling = up ? element.previousElementSibling : element.nextElementSibling;
|
||||
|
||||
if (descending) {
|
||||
if (child) {
|
||||
element = child;
|
||||
}
|
||||
else if (sibling) {
|
||||
element = sibling;
|
||||
}
|
||||
else {
|
||||
descending = false;
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (sibling) {
|
||||
element = sibling;
|
||||
descending = true;
|
||||
}
|
||||
else {
|
||||
element = element.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
if (element) {
|
||||
classes = element.classList;
|
||||
if (classes.contains("mx_LeftPanel")) { // we hit the top
|
||||
element = up ? element.lastElementChild : element.firstElementChild;
|
||||
descending = true;
|
||||
}
|
||||
}
|
||||
|
||||
} while(element && !(
|
||||
classes.contains("mx_RoomTile") ||
|
||||
classes.contains("mx_SearchBox_search") ||
|
||||
classes.contains("mx_RoomSubList_ellipsis")));
|
||||
|
||||
if (element) {
|
||||
element.focus();
|
||||
this.focusedElement = element;
|
||||
this.focusedDescending = descending;
|
||||
}
|
||||
},
|
||||
|
||||
_recheckCallElement: function(selectedRoomId) {
|
||||
// if we aren't viewing a room with an ongoing call, but there is an
|
||||
// active call, show the call element - we need to do this to make
|
||||
@@ -120,7 +211,8 @@ var LeftPanel = React.createClass({
|
||||
}
|
||||
|
||||
return (
|
||||
<aside className={classes} style={{ opacity: this.props.opacity }}>
|
||||
<aside className={classes} style={{ opacity: this.props.opacity }}
|
||||
onKeyDown={ this._onKeyDown } onFocus={ this._onFocus } onBlur={ this._onBlur }>
|
||||
<SearchBox collapsed={ this.props.collapsed } onSearch={ this.onSearch } />
|
||||
{ collapseButton }
|
||||
{ callPreview }
|
||||
|
||||
@@ -23,7 +23,6 @@ var ContentRepo = require("matrix-js-sdk").ContentRepo;
|
||||
var Modal = require('matrix-react-sdk/lib/Modal');
|
||||
var sdk = require('matrix-react-sdk');
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var GeminiScrollbar = require('react-gemini-scrollbar');
|
||||
|
||||
var linkify = require('linkifyjs');
|
||||
var linkifyString = require('linkifyjs/string');
|
||||
@@ -162,7 +161,7 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to get public room list",
|
||||
description: "The server may be unavailable or overloaded",
|
||||
description: ((err && err.message) ? err.message : "The server may be unavailable or overloaded"),
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -205,13 +204,13 @@ module.exports = React.createClass({
|
||||
}).done(() => {
|
||||
modal.close();
|
||||
this.refreshRoomList();
|
||||
}, function(err) {
|
||||
}, (err) => {
|
||||
modal.close();
|
||||
this.refreshRoomList();
|
||||
console.error("Failed to " + step + ": " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to " + step,
|
||||
title: "Failed to " + step,
|
||||
description: ((err && err.message) ? err.message : "The server may be unavailable or overloaded"),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -460,6 +459,17 @@ module.exports = React.createClass({
|
||||
return fields;
|
||||
},
|
||||
|
||||
/**
|
||||
* called by the parent component when PageUp/Down/etc is pressed.
|
||||
*
|
||||
* We pass it down to the scroll panel.
|
||||
*/
|
||||
handleScrollKey: function(ev) {
|
||||
if (this.scrollPanel) {
|
||||
this.scrollPanel.handleScrollKey(ev);
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader');
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
@@ -538,7 +548,7 @@ module.exports = React.createClass({
|
||||
const DirectorySearchBox = sdk.getComponent('elements.DirectorySearchBox');
|
||||
return (
|
||||
<div className="mx_RoomDirectory">
|
||||
<SimpleRoomHeader title="Directory" />
|
||||
<SimpleRoomHeader title="Directory" icon="img/icons-directory.svg"/>
|
||||
<div className="mx_RoomDirectory_list">
|
||||
<div className="mx_RoomDirectory_listheader">
|
||||
<DirectorySearchBox
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -27,8 +28,11 @@ var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg');
|
||||
var RoomNotifs = require('matrix-react-sdk/lib/RoomNotifs');
|
||||
var FormattingUtils = require('matrix-react-sdk/lib/utils/FormattingUtils');
|
||||
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
|
||||
var ConstantTimeDispatcher = require('matrix-react-sdk/lib/ConstantTimeDispatcher');
|
||||
var RoomSubListHeader = require('./RoomSubListHeader.js');
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
|
||||
// turn this on for drop & drag console debugging galore
|
||||
// turn this on for drag & drop console debugging galore
|
||||
var debug = false;
|
||||
|
||||
const TRUNCATE_AT = 10;
|
||||
@@ -71,17 +75,17 @@ var RoomSubList = React.createClass({
|
||||
|
||||
order: React.PropTypes.string.isRequired,
|
||||
|
||||
// undefined if no room is selected (eg we are showing settings)
|
||||
selectedRoom: React.PropTypes.string,
|
||||
|
||||
startAsHidden: React.PropTypes.bool,
|
||||
showSpinner: React.PropTypes.bool, // true to show a spinner if 0 elements when expanded
|
||||
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
||||
onHeaderClick: React.PropTypes.func,
|
||||
alwaysShowHeader: React.PropTypes.bool,
|
||||
selectedRoom: React.PropTypes.string,
|
||||
incomingCall: React.PropTypes.object,
|
||||
onShowMoreRooms: React.PropTypes.func,
|
||||
searchFilter: React.PropTypes.string,
|
||||
emptyContent: React.PropTypes.node, // content shown if the list is empty
|
||||
headerItems: React.PropTypes.node, // content shown in the sublist header
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
@@ -100,13 +104,31 @@ var RoomSubList = React.createClass({
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
constantTimeDispatcher.register("RoomSubList.sort", this.props.tagName, this.onSort);
|
||||
constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
|
||||
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
|
||||
this._fixUndefinedOrder(this.props.list);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
constantTimeDispatcher.unregister("RoomSubList.sort", this.props.tagName, this.onSort);
|
||||
constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(newProps) {
|
||||
// order the room list appropriately before we re-render
|
||||
//if (debug) console.log("received new props, list = " + newProps.list);
|
||||
this.sortList(this.applySearchFilter(newProps.list, newProps.searchFilter), newProps.order);
|
||||
this._fixUndefinedOrder(newProps.list);
|
||||
},
|
||||
|
||||
onSort: function() {
|
||||
this.sortList(this.applySearchFilter(this.props.list, this.props.searchFilter), this.props.order);
|
||||
// we deliberately don't waste time trying to fix undefined ordering here
|
||||
},
|
||||
|
||||
onRefresh: function() {
|
||||
this.forceUpdate();
|
||||
},
|
||||
|
||||
applySearchFilter: function(list, filter) {
|
||||
@@ -119,7 +141,7 @@ var RoomSubList = React.createClass({
|
||||
// The header is collapsable if it is hidden or not stuck
|
||||
// The dataset elements are added in the RoomList _initAndPositionStickyHeaders method
|
||||
isCollapsableOnClick: function() {
|
||||
var stuck = this.refs.header.dataset.stuck;
|
||||
var stuck = this.refs.header.refs.header.dataset.stuck;
|
||||
if (this.state.hidden || stuck === undefined || stuck === "none") {
|
||||
return true;
|
||||
} else {
|
||||
@@ -142,14 +164,15 @@ var RoomSubList = React.createClass({
|
||||
this.props.onHeaderClick(isHidden);
|
||||
} else {
|
||||
// The header is stuck, so the click is to be interpreted as a scroll to the header
|
||||
this.props.onHeaderClick(this.state.hidden, this.refs.header.dataset.originalPosition);
|
||||
this.props.onHeaderClick(this.state.hidden, this.refs.header.refs.header.dataset.originalPosition);
|
||||
}
|
||||
},
|
||||
|
||||
onRoomTileClick(roomId) {
|
||||
onRoomTileClick(roomId, ev) {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: roomId,
|
||||
clear_search: (ev && (ev.keyCode == 13 || ev.keyCode == 32)),
|
||||
});
|
||||
},
|
||||
|
||||
@@ -211,9 +234,6 @@ var RoomSubList = React.createClass({
|
||||
if (order === "manual") comparator = this.manualComparator;
|
||||
if (order === "recent") comparator = this.recentsComparator;
|
||||
|
||||
// Fix undefined orders here, and make sure the backend gets updated as well
|
||||
this._fixUndefinedOrder(list);
|
||||
|
||||
//if (debug) console.log("sorting list for sublist " + this.props.label + " with length " + list.length + ", this.props.list = " + this.props.list);
|
||||
this.setState({ sortedList: list.sort(comparator) });
|
||||
},
|
||||
@@ -248,10 +268,9 @@ var RoomSubList = React.createClass({
|
||||
|
||||
if (badges) {
|
||||
result[0] += notificationCount;
|
||||
if (highlight) {
|
||||
result[1] = true;
|
||||
}
|
||||
}
|
||||
|
||||
result[1] |= highlight;
|
||||
}
|
||||
return result;
|
||||
}, [0, false]);
|
||||
@@ -359,7 +378,6 @@ var RoomSubList = React.createClass({
|
||||
var self = this;
|
||||
var DNDRoomTile = sdk.getComponent("rooms.DNDRoomTile");
|
||||
return this.state.sortedList.map(function(room) {
|
||||
var selected = room.roomId == self.props.selectedRoom;
|
||||
// XXX: is it evil to pass in self as a prop to RoomTile?
|
||||
return (
|
||||
<DNDRoomTile
|
||||
@@ -367,9 +385,7 @@ var RoomSubList = React.createClass({
|
||||
roomSubList={ self }
|
||||
key={ room.roomId }
|
||||
collapsed={ self.props.collapsed || false}
|
||||
selected={ selected }
|
||||
unread={ Unread.doesRoomHaveUnreadMessages(room) }
|
||||
highlight={ room.getUnreadNotificationCount('highlight') > 0 || self.props.label === 'Invites' }
|
||||
selectedRoom={ self.props.selectedRoom }
|
||||
isInvite={ self.props.label === 'Invites' }
|
||||
refreshSubList={ self._updateSubListCount }
|
||||
incomingCall={ null }
|
||||
@@ -379,70 +395,6 @@ var RoomSubList = React.createClass({
|
||||
});
|
||||
},
|
||||
|
||||
_getHeaderJsx: function() {
|
||||
var TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
|
||||
var subListNotifications = this.roomNotificationCount();
|
||||
var subListNotifCount = subListNotifications[0];
|
||||
var subListNotifHighlight = subListNotifications[1];
|
||||
|
||||
var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
|
||||
|
||||
var chevronClasses = classNames({
|
||||
'mx_RoomSubList_chevron': true,
|
||||
'mx_RoomSubList_chevronRight': this.state.hidden,
|
||||
'mx_RoomSubList_chevronDown': !this.state.hidden,
|
||||
});
|
||||
|
||||
var badgeClasses = classNames({
|
||||
'mx_RoomSubList_badge': true,
|
||||
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
||||
});
|
||||
|
||||
var badge;
|
||||
if (subListNotifCount > 0) {
|
||||
badge = <div className={badgeClasses}>{ FormattingUtils.formatCount(subListNotifCount) }</div>;
|
||||
}
|
||||
|
||||
// When collapsed, allow a long hover on the header to show user
|
||||
// the full tag name and room count
|
||||
var title;
|
||||
if (this.props.collapsed) {
|
||||
title = this.props.label;
|
||||
if (roomCount !== '') {
|
||||
title += " [" + roomCount + "]";
|
||||
}
|
||||
}
|
||||
|
||||
var incomingCall;
|
||||
if (this.props.incomingCall) {
|
||||
var self = this;
|
||||
// Check if the incoming call is for this section
|
||||
var incomingCallRoom = this.props.list.filter(function(room) {
|
||||
return self.props.incomingCall.roomId === room.roomId;
|
||||
});
|
||||
|
||||
if (incomingCallRoom.length === 1) {
|
||||
var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
||||
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
|
||||
}
|
||||
}
|
||||
|
||||
var tabindex = this.props.searchFilter === "" ? "0" : "-1";
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
||||
<AccessibleButton onClick={ this.onClick } className="mx_RoomSubList_label" tabIndex={tabindex}>
|
||||
{ this.props.collapsed ? '' : this.props.label }
|
||||
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
|
||||
<div className={chevronClasses}></div>
|
||||
{ badge }
|
||||
{ incomingCall }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
||||
_createOverflowTile: function(overflowCount, totalCount) {
|
||||
var content = <div className="mx_RoomSubList_chevronDown"></div>;
|
||||
|
||||
@@ -498,7 +450,7 @@ var RoomSubList = React.createClass({
|
||||
// gets triggered and another list is passed in. Doing it one at a time means that
|
||||
// we always correctly calculate the highest order for the list - stops multiple
|
||||
// rooms getting the same order. This is only really relevant for the first time this
|
||||
// is run with historical room tag data, after that there should only be undefined
|
||||
// is run with historical room tag data, after that there should only be one undefined
|
||||
// in the list at a time anyway.
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].tags[self.props.tagName] && list[i].tags[self.props.tagName].order === undefined) {
|
||||
@@ -508,8 +460,8 @@ var RoomSubList = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to add tag " + self.props.tagName + " to room" + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to add tag " + self.props.tagName + " to room",
|
||||
title: "Failed to add tag " + self.props.tagName + " to room",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
break;
|
||||
@@ -520,16 +472,25 @@ var RoomSubList = React.createClass({
|
||||
|
||||
render: function() {
|
||||
var connectDropTarget = this.props.connectDropTarget;
|
||||
var RoomDropTarget = sdk.getComponent('rooms.RoomDropTarget');
|
||||
var TruncatedList = sdk.getComponent('elements.TruncatedList');
|
||||
|
||||
var label = this.props.collapsed ? null : this.props.label;
|
||||
|
||||
//console.log("render: " + JSON.stringify(this.state.sortedList));
|
||||
let content;
|
||||
if (this.state.sortedList.length == 0) {
|
||||
content = this.props.emptyContent;
|
||||
} else {
|
||||
content = this.makeRoomTiles();
|
||||
}
|
||||
|
||||
var target;
|
||||
if (this.state.sortedList.length == 0 && this.props.editable) {
|
||||
target = <RoomDropTarget label={ 'Drop here to ' + this.props.verb }/>;
|
||||
var roomCount = this.props.list.length > 0 ? this.props.list.length : '';
|
||||
|
||||
var isIncomingCallRoom;
|
||||
if (this.props.incomingCall) {
|
||||
// Check if the incoming call is for this section
|
||||
isIncomingCallRoom = this.props.list.find(room=>{
|
||||
return this.props.incomingCall.roomId === room.roomId;
|
||||
}) ? true : false;
|
||||
}
|
||||
|
||||
if (this.state.sortedList.length > 0 || this.props.editable) {
|
||||
@@ -539,8 +500,7 @@ var RoomSubList = React.createClass({
|
||||
if (!this.state.hidden) {
|
||||
subList = <TruncatedList className={ classes } truncateAt={this.state.truncateAt}
|
||||
createOverflowElement={this._createOverflowTile} >
|
||||
{ target }
|
||||
{ this.makeRoomTiles() }
|
||||
{ content }
|
||||
</TruncatedList>;
|
||||
}
|
||||
else {
|
||||
@@ -550,7 +510,20 @@ var RoomSubList = React.createClass({
|
||||
|
||||
return connectDropTarget(
|
||||
<div>
|
||||
{ this._getHeaderJsx() }
|
||||
<RoomSubListHeader
|
||||
ref='header'
|
||||
label={ this.props.label }
|
||||
tagName={ this.props.tagName }
|
||||
roomCount={ roomCount }
|
||||
collapsed={ this.props.collapsed }
|
||||
hidden={ this.state.hidden }
|
||||
incomingCall={ this.props.incomingCall }
|
||||
isIncomingCallRoom={ isIncomingCallRoom }
|
||||
roomNotificationCount={ this.roomNotificationCount() }
|
||||
onClick={ this.onClick }
|
||||
onHeaderClick={ this.props.onHeaderClick }
|
||||
headerItems={this.props.headerItems}
|
||||
/>
|
||||
{ subList }
|
||||
</div>
|
||||
);
|
||||
@@ -559,7 +532,21 @@ var RoomSubList = React.createClass({
|
||||
var Loader = sdk.getComponent("elements.Spinner");
|
||||
return (
|
||||
<div className="mx_RoomSubList">
|
||||
{ this.props.alwaysShowHeader ? this._getHeaderJsx() : undefined }
|
||||
{ this.props.alwaysShowHeader ?
|
||||
<RoomSubListHeader
|
||||
ref='header'
|
||||
label={ this.props.label }
|
||||
tagName={ this.props.tagName }
|
||||
roomCount={ roomCount }
|
||||
collapsed={ this.props.collapsed }
|
||||
hidden={ this.state.hidden }
|
||||
isIncomingCallRoom={ isIncomingCallRoom }
|
||||
roomNotificationCount={ this.roomNotificationCount() }
|
||||
onClick={ this.onClick }
|
||||
onHeaderClick={ this.props.onHeaderClick }
|
||||
headerItems={this.props.headerItems}
|
||||
/>
|
||||
: undefined }
|
||||
{ (this.props.showSpinner && !this.state.hidden) ? <Loader /> : undefined }
|
||||
</div>
|
||||
);
|
||||
|
||||
117
src/components/structures/RoomSubListHeader.js
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import { formatCount } from 'matrix-react-sdk/lib/utils/FormattingUtils';
|
||||
import AccessibleButton from 'matrix-react-sdk/lib/components/views/elements/AccessibleButton';
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'RoomSubListHeader',
|
||||
|
||||
propTypes: {
|
||||
label: React.PropTypes.string.isRequired,
|
||||
tagName: React.PropTypes.string,
|
||||
roomCount: React.PropTypes.oneOfType([
|
||||
React.PropTypes.string,
|
||||
React.PropTypes.number
|
||||
]),
|
||||
collapsed: React.PropTypes.bool.isRequired, // is LeftPanel collapsed?
|
||||
incomingCall: React.PropTypes.object,
|
||||
isIncomingCallRoom: React.PropTypes.bool,
|
||||
roomNotificationCount: React.PropTypes.array,
|
||||
hidden: React.PropTypes.bool,
|
||||
onClick: React.PropTypes.func,
|
||||
onHeaderClick: React.PropTypes.func,
|
||||
headerItems: React.PropTypes.node, // content shown in the sublist header
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
onHeaderClick: function() {}, // NOP
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
// constantTimeDispatcher.register("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
// constantTimeDispatcher.unregister("RoomSubList.refreshHeader", this.props.tagName, this.onRefresh);
|
||||
},
|
||||
|
||||
// onRefresh: function() {
|
||||
// this.forceUpdate();
|
||||
// },
|
||||
|
||||
render: function() {
|
||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||
|
||||
const subListNotifications = this.props.roomNotificationCount;
|
||||
const subListNotifCount = subListNotifications[0];
|
||||
const subListNotifHighlight = subListNotifications[1];
|
||||
|
||||
const chevronClasses = classNames({
|
||||
'mx_RoomSubList_chevron': true,
|
||||
'mx_RoomSubList_chevronRight': this.props.hidden,
|
||||
'mx_RoomSubList_chevronDown': !this.props.hidden,
|
||||
});
|
||||
|
||||
const badgeClasses = classNames({
|
||||
'mx_RoomSubList_badge': true,
|
||||
'mx_RoomSubList_badgeHighlight': subListNotifHighlight,
|
||||
});
|
||||
|
||||
let badge;
|
||||
if (subListNotifCount > 0) {
|
||||
badge = <div className={badgeClasses}>{ formatCount(subListNotifCount) }</div>;
|
||||
} else if (subListNotifHighlight) {
|
||||
badge = <div className={badgeClasses}>!</div>;
|
||||
}
|
||||
|
||||
// When collapsed, allow a long hover on the header to show user
|
||||
// the full tag name and room count
|
||||
let title;
|
||||
const roomCount = this.props.roomCount;
|
||||
if (this.props.collapsed) {
|
||||
title = this.props.label;
|
||||
if (roomCount !== '') {
|
||||
title += " [" + roomCount + "]";
|
||||
}
|
||||
}
|
||||
|
||||
let incomingCall;
|
||||
if (this.props.isIncomingCallRoom) {
|
||||
const IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
|
||||
incomingCall = <IncomingCallBox className="mx_RoomSubList_incomingCall" incomingCall={ this.props.incomingCall }/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_RoomSubList_labelContainer" title={ title } ref="header">
|
||||
<AccessibleButton onClick={ this.props.onClick } className="mx_RoomSubList_label" tabIndex="0">
|
||||
{ this.props.collapsed ? '' : this.props.label }
|
||||
{this.props.headerItems}
|
||||
<div className="mx_RoomSubList_roomCount">{ roomCount }</div>
|
||||
<div className={chevronClasses}></div>
|
||||
{ badge }
|
||||
</AccessibleButton>
|
||||
{ incomingCall }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ var sdk = require('matrix-react-sdk')
|
||||
var dis = require('matrix-react-sdk/lib/dispatcher');
|
||||
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');
|
||||
var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton');
|
||||
var KeyCode = require('matrix-react-sdk/lib/KeyCode');
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'SearchBox',
|
||||
@@ -38,25 +39,23 @@ module.exports = React.createClass({
|
||||
|
||||
componentDidMount: function() {
|
||||
this.dispatcherRef = dis.register(this.onAction);
|
||||
document.addEventListener('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
dis.unregister(this.dispatcherRef);
|
||||
document.removeEventListener('keydown', this._onKeyDown);
|
||||
},
|
||||
|
||||
onAction: function(payload) {
|
||||
// Disabling this as I find it really really annoying, and was used to the
|
||||
// previous behaviour - see https://github.com/vector-im/riot-web/issues/3348
|
||||
/*
|
||||
switch (payload.action) {
|
||||
// Clear up the text field when a room is selected.
|
||||
case 'view_room':
|
||||
if (this.refs.search) {
|
||||
if (payload.clear_search && this.refs.search) {
|
||||
this._clearSearch();
|
||||
}
|
||||
break;
|
||||
}
|
||||
*/
|
||||
},
|
||||
|
||||
onChange: function() {
|
||||
@@ -90,6 +89,38 @@ module.exports = React.createClass({
|
||||
this.onChange();
|
||||
},
|
||||
|
||||
_onKeyDown: function(ev) {
|
||||
let handled = false;
|
||||
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
|
||||
let ctrlCmdOnly;
|
||||
if (isMac) {
|
||||
ctrlCmdOnly = ev.metaKey && !ev.altKey && !ev.ctrlKey && !ev.shiftKey;
|
||||
} else {
|
||||
ctrlCmdOnly = ev.ctrlKey && !ev.altKey && !ev.metaKey && !ev.shiftKey;
|
||||
}
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case KeyCode.ESCAPE:
|
||||
this._clearSearch();
|
||||
dis.dispatch({action: 'focus_composer'});
|
||||
break;
|
||||
case KeyCode.KEY_K:
|
||||
if (ctrlCmdOnly) {
|
||||
if (this.refs.search) {
|
||||
this.refs.search.focus();
|
||||
this.refs.search.select();
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to remove tag " + tagNameOff + " from room",
|
||||
description: err.toString()
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -88,7 +88,7 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to add tag " + tagNameOn + " to room",
|
||||
description: err.toString()
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -149,7 +149,7 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Failed to set Direct Message status of room",
|
||||
description: err.toString()
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
},
|
||||
@@ -187,8 +187,8 @@ module.exports = React.createClass({
|
||||
var errCode = err.errcode || "unknown error code";
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: `Failed to forget room (${errCode})`
|
||||
title: `Failed to forget room (${errCode})`,
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
|
||||
import React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import rageshake from '../../../vector/rageshake';
|
||||
import SdkConfig from 'matrix-react-sdk/lib/SdkConfig';
|
||||
|
||||
export default class BugReportDialog extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -26,11 +26,18 @@ export default class BugReportDialog extends React.Component {
|
||||
busy: false,
|
||||
err: null,
|
||||
text: "",
|
||||
progress: null,
|
||||
};
|
||||
this._unmounted = false;
|
||||
this._onSubmit = this._onSubmit.bind(this);
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
this._onTextChange = this._onTextChange.bind(this);
|
||||
this._onSendLogsChange = this._onSendLogsChange.bind(this);
|
||||
this._sendProgressCallback = this._sendProgressCallback.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this._unmounted = true;
|
||||
}
|
||||
|
||||
_onCancel(ev) {
|
||||
@@ -46,12 +53,27 @@ export default class BugReportDialog extends React.Component {
|
||||
});
|
||||
return;
|
||||
}
|
||||
this.setState({ busy: true, err: null });
|
||||
rageshake.sendBugReport(userText, sendLogs).then(() => {
|
||||
this.setState({ busy: false });
|
||||
this.props.onFinished(false);
|
||||
}, (err) => {
|
||||
this.setState({ busy: false, err: `Failed: ${err.message}` });
|
||||
this.setState({ busy: true, progress: null, err: null });
|
||||
this._sendProgressCallback("Loading bug report module");
|
||||
|
||||
require(['../../../vector/submit-rageshake'], (s) => {
|
||||
s(SdkConfig.get().bug_report_endpoint_url, {
|
||||
userText: userText,
|
||||
sendLogs: sendLogs,
|
||||
progressCallback: this._sendProgressCallback,
|
||||
}).then(() => {
|
||||
if (!this._unmounted) {
|
||||
this.setState({ busy: false, progress: null });
|
||||
this.props.onFinished(false);
|
||||
}
|
||||
}, (err) => {
|
||||
if (!this._unmounted) {
|
||||
this.setState({
|
||||
busy: false, progress: null,
|
||||
err: `Failed to send report: ${err.message}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -63,6 +85,13 @@ export default class BugReportDialog extends React.Component {
|
||||
this.setState({ sendLogs: ev.target.checked });
|
||||
}
|
||||
|
||||
_sendProgressCallback(progress) {
|
||||
if (this._unmounted) {
|
||||
return;
|
||||
}
|
||||
this.setState({progress: progress});
|
||||
}
|
||||
|
||||
render() {
|
||||
const Loader = sdk.getComponent("elements.Spinner");
|
||||
|
||||
@@ -73,8 +102,6 @@ export default class BugReportDialog extends React.Component {
|
||||
</div>;
|
||||
}
|
||||
|
||||
const okLabel = this.state.busy ? <Loader /> : 'Send';
|
||||
|
||||
let cancelButton = null;
|
||||
if (!this.state.busy) {
|
||||
cancelButton = <button onClick={this._onCancel}>
|
||||
@@ -82,6 +109,16 @@ export default class BugReportDialog extends React.Component {
|
||||
</button>;
|
||||
}
|
||||
|
||||
let progress = null;
|
||||
if (this.state.busy) {
|
||||
progress = (
|
||||
<div className="progress">
|
||||
<Loader />
|
||||
{this.state.progress} ...
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_BugReportDialog">
|
||||
<div className="mx_Dialog_title">
|
||||
@@ -104,6 +141,7 @@ export default class BugReportDialog extends React.Component {
|
||||
<input type="checkbox" checked={this.state.sendLogs}
|
||||
onChange={this._onSendLogsChange} id="mx_BugReportDialog_logs"/>
|
||||
<label htmlFor="mx_BugReportDialog_logs">Send logs</label>
|
||||
{progress}
|
||||
{error}
|
||||
</div>
|
||||
<div className="mx_Dialog_buttons">
|
||||
@@ -111,8 +149,9 @@ export default class BugReportDialog extends React.Component {
|
||||
className="mx_Dialog_primary danger"
|
||||
onClick={this._onSubmit}
|
||||
autoFocus={true}
|
||||
disabled={this.state.busy}
|
||||
>
|
||||
{okLabel}
|
||||
Send
|
||||
</button>
|
||||
|
||||
{cancelButton}
|
||||
|
||||
@@ -176,7 +176,7 @@ module.exports = React.createClass({
|
||||
{ this.getName() }
|
||||
</div>
|
||||
{ eventMeta }
|
||||
<a className="mx_ImageView_link" href={ this.props.src } target="_blank" rel="noopener">
|
||||
<a className="mx_ImageView_link" href={ this.props.src } download={ this.props.name } target="_blank" rel="noopener">
|
||||
<div className="mx_ImageView_download">
|
||||
Download this file<br/>
|
||||
<span className="mx_ImageView_size">{ size_res }</span>
|
||||
|
||||
52
src/components/views/globals/PasswordNagBar.js
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import sdk from 'matrix-react-sdk';
|
||||
import Modal from 'matrix-react-sdk/lib/Modal';
|
||||
|
||||
export default React.createClass({
|
||||
onUpdateClicked: function() {
|
||||
// TODO: Implement dialog to set password
|
||||
// const SetPasswordDialog = sdk.getComponent('dialogs.SetPasswordDialog');
|
||||
// Modal.createDialog(SetPasswordDialog, {
|
||||
// onFinished: () => {
|
||||
// }
|
||||
// });
|
||||
},
|
||||
|
||||
render: function() {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
return (
|
||||
<div className="mx_MatrixToolbar">
|
||||
<img className="mx_MatrixToolbar_warning"
|
||||
src="img/warning.svg"
|
||||
width="24"
|
||||
height="23"
|
||||
alt="Warning"
|
||||
/>
|
||||
<div className="mx_MatrixToolbar_content">
|
||||
To be able to return to your account, you need to set a password.
|
||||
</div>
|
||||
<button className="mx_MatrixToolbar_action" onClick={this.onUpdateClicked}>
|
||||
Set Password
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -18,6 +18,9 @@ limitations under the License.
|
||||
|
||||
var React = require('react');
|
||||
|
||||
const i = [1, 2, 3, 4, 5][Math.floor(Math.random() * 5)];
|
||||
const DEFAULT_LOGO_URI = "img/logos/riot-logo-" + i + ".svg";
|
||||
|
||||
module.exports = React.createClass({
|
||||
displayName: 'VectorLoginHeader',
|
||||
statics: {
|
||||
@@ -30,7 +33,7 @@ module.exports = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="mx_Login_logo">
|
||||
<img src={this.props.icon || "img/logo.png"} alt="Riot"/>
|
||||
<img src={this.props.icon || DEFAULT_LOGO_URI} alt="Riot"/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ module.exports = React.createClass({
|
||||
render: function() {
|
||||
var date = new Date(this.props.ts);
|
||||
return (
|
||||
<span className="mx_MessageTimestamp">
|
||||
<span className="mx_MessageTimestamp" title={ DateUtils.formatDate(date) }>
|
||||
{ DateUtils.formatTime(date) }
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -90,8 +90,8 @@ var roomTileSource = {
|
||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to set direct chat tag " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to set direct chat tag",
|
||||
title: "Failed to set direct chat tag",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
return;
|
||||
@@ -115,8 +115,8 @@ var roomTileSource = {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to remove tag " + prevTag + " from room: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to remove tag " + prevTag + " from room",
|
||||
title: "Failed to remove tag " + prevTag + " from room",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -137,8 +137,8 @@ var roomTileSource = {
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to add tag " + newTag + " to room: " + err);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to add tag " + newTag + " to room",
|
||||
title: "Failed to add tag " + newTag + " to room",
|
||||
description: ((err && err.message) ? err.message : "Operation failed"),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,20 +22,12 @@ module.exports = React.createClass({
|
||||
displayName: 'RoomDropTarget',
|
||||
|
||||
render: function() {
|
||||
if (this.props.placeholder) {
|
||||
return (
|
||||
<div className="mx_RoomDropTarget mx_RoomDropTarget_placeholder">
|
||||
return (
|
||||
<div className="mx_RoomDropTarget">
|
||||
<div className="mx_RoomDropTarget_label">
|
||||
{ this.props.label }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
else {
|
||||
return (
|
||||
<div className="mx_RoomDropTarget">
|
||||
<div className="mx_RoomDropTarget_label">
|
||||
{ this.props.label }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -240,8 +240,8 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to change settings: " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to change settings",
|
||||
title: "Failed to change settings",
|
||||
description: ((error && error.message) ? error.message : "Operation failed"),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
});
|
||||
@@ -310,8 +310,8 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Can't update user notification settings: " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Can't update user notification settings",
|
||||
title: "Can't update user notification settings",
|
||||
description: ((error && error.message) ? error.message : "Operation failed"),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
});
|
||||
@@ -352,8 +352,8 @@ module.exports = React.createClass({
|
||||
var ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||
console.error("Failed to update keywords: " + error);
|
||||
Modal.createDialog(ErrorDialog, {
|
||||
title: "Error",
|
||||
description: "Failed to update keywords",
|
||||
title: "Failed to update keywords",
|
||||
description: ((error && error.message) ? error.message : "Operation failed"),
|
||||
onFinished: self._refreshFromServer
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ input[type=text].error, input[type=password].error {
|
||||
border: 1px solid $warning-color;
|
||||
}
|
||||
|
||||
input[type=text]:focus, textarea:focus {
|
||||
input[type=text]:focus, input[type=password]:focus, textarea:focus {
|
||||
border: 1px solid $accent-color;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
@@ -225,6 +225,10 @@ textarea {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.mx_Dialog button:focus, .mx_Dialog input[type="submit"]:focus {
|
||||
filter: brightness($focus-brightness);
|
||||
}
|
||||
|
||||
.mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary {
|
||||
color: $accent-fg-color;
|
||||
background-color: $accent-color;
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
@import "./matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss";
|
||||
@import "./matrix-react-sdk/views/dialogs/_ConfirmUserActionDialog.scss";
|
||||
@import "./matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss";
|
||||
@import "./matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss";
|
||||
@import "./matrix-react-sdk/views/dialogs/_SetMxIdDialog.scss";
|
||||
@import "./matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_AccessibleButton.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_AddressSelector.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_AddressTile.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_DirectorySearchBox.scss";
|
||||
@@ -26,6 +27,7 @@
|
||||
@import "./matrix-react-sdk/views/elements/_MemberEventListSummary.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_ProgressBar.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RichText.scss";
|
||||
@import "./matrix-react-sdk/views/elements/_RoleButton.scss";
|
||||
@import "./matrix-react-sdk/views/login/_InteractiveAuthEntryComponents.scss";
|
||||
@import "./matrix-react-sdk/views/login/_ServerConfig.scss";
|
||||
@import "./matrix-react-sdk/views/messages/_MEmoteBody.scss";
|
||||
|
||||
@@ -160,7 +160,8 @@ hr.mx_RoomView_myReadMarker {
|
||||
border-bottom: solid 1px $accent-color;
|
||||
margin-top: 0px;
|
||||
position: relative;
|
||||
top: 5px;
|
||||
top: -1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.mx_RoomView_statusArea {
|
||||
@@ -171,7 +172,7 @@ hr.mx_RoomView_myReadMarker {
|
||||
|
||||
max-height: 0px;
|
||||
background-color: $primary-bg-color;
|
||||
z-index: 1000;
|
||||
z-index: 5;
|
||||
overflow: hidden;
|
||||
|
||||
-webkit-transition: all .2s ease-out;
|
||||
@@ -259,4 +260,4 @@ hr.mx_RoomView_myReadMarker {
|
||||
|
||||
.mx_RoomView_ongoingConfCallNotification a {
|
||||
color: $accent-fg-color ! important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +176,15 @@ limitations under the License.
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_UserSettings_phoneSection {
|
||||
display:table;
|
||||
}
|
||||
|
||||
.mx_UserSettings_phoneCountry {
|
||||
width: 70px;
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
input.mx_UserSettings_phoneNumberField {
|
||||
margin-left: 3px;
|
||||
width: 172px;
|
||||
@@ -213,3 +222,9 @@ input.mx_UserSettings_phoneNumberField {
|
||||
.mx_UserSettings_avatarPicker_edit > input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mx_UserSettings_advanced_spoiler {
|
||||
cursor: pointer;
|
||||
color: $accent-color;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ limitations under the License.
|
||||
|
||||
.mx_Login_logo {
|
||||
text-align: center;
|
||||
height: 195px;
|
||||
height: 150px;
|
||||
margin-bottom: 45px;
|
||||
}
|
||||
|
||||
.mx_Login_logo img {
|
||||
@@ -66,10 +67,6 @@ limitations under the License.
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.mx_Login_username {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_fieldLabel {
|
||||
margin-top: -10px;
|
||||
margin-left: 8px;
|
||||
@@ -167,16 +164,82 @@ limitations under the License.
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneSection {
|
||||
display: table;
|
||||
.mx_Login_type_container {
|
||||
display: flex;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.mx_Login_type_label {
|
||||
flex-grow: 1;
|
||||
line-height: 35px;
|
||||
}
|
||||
|
||||
.mx_Login_type_dropdown {
|
||||
width: 125px;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.mx_Login_field_group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_Login_field_prefix {
|
||||
height: 33px;
|
||||
padding: 0px 5px;
|
||||
line-height: 33px;
|
||||
|
||||
background-color: #eee;
|
||||
border: 1px solid #c7c7c7;
|
||||
border-right: 0px;
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.mx_Login_field_suffix {
|
||||
height: 33px;
|
||||
padding: 0px 5px;
|
||||
line-height: 33px;
|
||||
|
||||
background-color: #eee;
|
||||
border: 1px solid #c7c7c7;
|
||||
border-left: 0px;
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
|
||||
text-align: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.mx_Login_username {
|
||||
flex-shrink: 1;
|
||||
min-width: 0px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.mx_Login_field_has_prefix {
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_field_has_suffix {
|
||||
border-top-right-radius: 0px;
|
||||
border-bottom-right-radius: 0px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneCountry {
|
||||
display: table-cell;
|
||||
width: 70px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneNumberField {
|
||||
width: 210px;
|
||||
margin-left: 3px;
|
||||
.mx_Login_phoneCountry .mx_Dropdown_option {
|
||||
/*
|
||||
To match height of mx_Login_field
|
||||
33px + 2px border from mx_Dropdown_option = 35px
|
||||
*/
|
||||
height: 33px;
|
||||
line-height: 33px;
|
||||
}
|
||||
|
||||
.mx_Login_phoneCountry .mx_Dropdown_option img {
|
||||
margin: 4px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_SetMxIdDialog .mx_Dialog_title {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.mx_SetMxIdDialog_input_group {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.mx_SetMxIdDialog_input {
|
||||
border-radius: 3px;
|
||||
border: 1px solid $input-border-color;
|
||||
padding: 9px;
|
||||
color: $primary-fg-color;
|
||||
background-color: $primary-bg-color;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
}
|
||||
|
||||
.mx_SetMxIdDialog_input.error,
|
||||
.mx_SetMxIdDialog_input.error:focus {
|
||||
border: 1px solid $warning-color;
|
||||
}
|
||||
|
||||
.mx_SetMxIdDialog_input_group .mx_Spinner {
|
||||
height: 37px;
|
||||
padding-left: 10px;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.mx_SetMxIdDialog .success {
|
||||
color: $accent-color;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -14,11 +14,8 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_SetDisplayNameDialog_input {
|
||||
border-radius: 3px;
|
||||
border: 1px solid $input-border-color;
|
||||
padding: 9px;
|
||||
color: $primary-fg-color;
|
||||
background-color: $primary-bg-color;
|
||||
font-size: 15px;
|
||||
}
|
||||
.mx_AccessibleButton:focus {
|
||||
outline: 0;
|
||||
filter: brightness($focus-brightness);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,15 @@ limitations under the License.
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mx_Dropdown_input:focus {
|
||||
border-color: $accent-color;
|
||||
}
|
||||
|
||||
/* Disable dropdown highlight on focus */
|
||||
.mx_Dropdown_input.mx_AccessibleButton:focus {
|
||||
filter: none;
|
||||
}
|
||||
|
||||
.mx_Dropdown_arrow {
|
||||
border-color: $primary-fg-color transparent transparent;
|
||||
border-style: solid;
|
||||
@@ -74,7 +83,7 @@ input.mx_Dropdown_option, input.mx_Dropdown_option:focus {
|
||||
border: 1px solid $accent-color;
|
||||
background-color: $primary-bg-color;
|
||||
max-height: 200px;
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.mx_Dropdown_menu .mx_Dropdown_option_highlight {
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
|
||||
.mx_TextualEvent.mx_MemberEventListSummary_summary {
|
||||
font-size: 14px;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.mx_MemberEventListSummary_avatars {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Copyright 2107 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
.mx_RoleButton {
|
||||
margin-left: 4px;
|
||||
margin-right: 4px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.mx_RoleButton object {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_RoleButton_tooltip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -25px;
|
||||
left: 6px;
|
||||
}
|
||||
@@ -133,10 +133,18 @@ limitations under the License.
|
||||
.mx_EventTile_redacted .mx_EventTile_line .mx_UnknownBody {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
height: 24px;
|
||||
border-radius: 4px;
|
||||
background-color: black;
|
||||
height: 36px;
|
||||
background-image: $event-redacted-img;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.mx_EventTile.mx_EventTile_redacted .mx_EventTile_line {
|
||||
/*
|
||||
Prevent changing colour of the background because
|
||||
$event-redacted-img matches $primary-bg-color
|
||||
*/
|
||||
background-color: initial !important;
|
||||
}
|
||||
|
||||
.mx_EventTile_highlight,
|
||||
@@ -210,8 +218,8 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_EventTile_continuation .mx_EventTile_readAvatars,
|
||||
.mx_EventTile_info .mx_EventTile_readAvatars
|
||||
{
|
||||
.mx_EventTile_info .mx_EventTile_readAvatars,
|
||||
.mx_EventTile_emote .mx_EventTile_readAvatars {
|
||||
top: 7px;
|
||||
}
|
||||
|
||||
@@ -323,6 +331,14 @@ limitations under the License.
|
||||
font-family: inherit ! important;
|
||||
}
|
||||
|
||||
|
||||
/* Make h1 and h2 the same size as h3. */
|
||||
.mx_EventTile_content .markdown-body h1,
|
||||
.mx_EventTile_content .markdown-body h2
|
||||
{
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.mx_EventTile_content .markdown-body a {
|
||||
color: $accent-color;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ limitations under the License.
|
||||
margin-left: -2px;
|
||||
order: 1;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_spinner {
|
||||
@@ -95,6 +96,12 @@ limitations under the License.
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_simpleHeader .mx_RoomHeader_icon {
|
||||
margin-left: 14px;
|
||||
margin-right: 24px;
|
||||
vertical-align: -4px;
|
||||
}
|
||||
|
||||
.mx_RoomHeader_name {
|
||||
vertical-align: middle;
|
||||
width: 100%;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2107 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@@ -37,3 +38,25 @@ limitations under the License.
|
||||
.mx_RoomList_scrollbar .gm-scrollbar.-vertical {
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.mx_RoomList_emptySubListTip {
|
||||
font-size: 13px;
|
||||
margin-left: 18px;
|
||||
margin-right: 18px;
|
||||
margin-top: 8px;
|
||||
margin-bottom: 7px;
|
||||
padding: 5px;
|
||||
border: 1px dashed $accent-color;
|
||||
color: $primary-fg-color;
|
||||
background-color: $droptarget-bg-color;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mx_RoomList_emptySubListTip .mx_RoleButton {
|
||||
vertical-align: -3px;
|
||||
}
|
||||
|
||||
.mx_RoomList_headerButtons {
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ limitations under the License.
|
||||
}
|
||||
|
||||
.mx_RoomTile:focus {
|
||||
outline: 0;
|
||||
filter: none ! important;
|
||||
background-color: $roomtile-focused-bg-color;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,20 +17,15 @@ limitations under the License.
|
||||
.mx_TopUnreadMessagesBar {
|
||||
margin: auto; /* centre horizontally */
|
||||
max-width: 960px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid $primary-hairline-color;
|
||||
|
||||
/* in absence of img */
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.mx_TopUnreadMessagesBar_scrollUp {
|
||||
display: inline;
|
||||
cursor: pointer;
|
||||
|
||||
/* in absence of img */
|
||||
padding-left: 65px;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.mx_TopUnreadMessagesBar_scrollUp img {
|
||||
|
||||
@@ -15,6 +15,8 @@ $accent-color: #76CFA6;
|
||||
|
||||
$selection-fg-color: $primary-bg-color;
|
||||
|
||||
$focus-brightness: 125%;
|
||||
|
||||
// red warning colour
|
||||
$warning-color: #ff0064;
|
||||
|
||||
@@ -93,6 +95,9 @@ $event-encrypting-color: #abddbc;
|
||||
$event-sending-color: #ddd;
|
||||
$event-notsent-color: #f44;
|
||||
|
||||
// event redaction
|
||||
$event-redacted-img: url('../../img/redacted.jpg');
|
||||
|
||||
// event timestamp
|
||||
$event-timestamp-color: #acacac;
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ $accent-color: #76CFA6;
|
||||
|
||||
$selection-fg-color: $primary-fg-color;
|
||||
|
||||
$focus-brightness: 200%;
|
||||
|
||||
// red warning colour
|
||||
$warning-color: #ff0064;
|
||||
|
||||
@@ -93,6 +95,9 @@ $event-encrypting-color: rgba(171, 221, 188, 0.4);
|
||||
$event-sending-color: #888;
|
||||
$event-notsent-color: #f44;
|
||||
|
||||
// event redaction
|
||||
$event-redacted-img: url('../../img/redacted-dark.jpg');
|
||||
|
||||
// event timestamp
|
||||
$event-timestamp-color: #acacac;
|
||||
|
||||
|
||||
@@ -64,43 +64,25 @@ limitations under the License.
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_homePage,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_directory,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_people,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_settings {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapsed .mx_BottomLeftMenu_homePage,
|
||||
.collapsed .mx_BottomLeftMenu_directory,
|
||||
.collapsed .mx_BottomLeftMenu_createRoom,
|
||||
.collapsed .mx_BottomLeftMenu_people,
|
||||
.collapsed .mx_BottomLeftMenu_settings {
|
||||
.collapsed .mx_RoleButton {
|
||||
margin-right: 0px ! important;
|
||||
padding-top: 3px ! important;
|
||||
padding-bottom: 3px ! important;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_homePage,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_directory,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_createRoom,
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_people {
|
||||
.mx_BottomLeftMenu_options .mx_RoleButton {
|
||||
margin-left: 0px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_settings {
|
||||
.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.mx_BottomLeftMenu_options .mx_BottomLeftMenu_settings .mx_RoleButton {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.mx_LeftPanel.collapsed .mx_BottomLeftMenu_settings {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.mx_LeftPanel .mx_BottomLeftMenu_tooltip {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: -25px;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
@@ -21,12 +21,17 @@ limitations under the License.
|
||||
margin-right: auto;
|
||||
margin-bottom: 12px;
|
||||
color: $primary-fg-color;
|
||||
word-break: break-word;
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.mx_RoomDirectory .mx_RoomHeader_simpleHeader {
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.mx_RoomDirectory_list {
|
||||
flex: 1;
|
||||
|
||||
|
||||
@@ -33,11 +33,6 @@ limitations under the License.
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.mx_RoomDropTarget_placeholder {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
.mx_RoomDropTarget_label {
|
||||
position: relative;
|
||||
margin-top: 3px;
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="25px" height="25px" viewBox="0 0 25 25" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: sketchtool 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>E34C64ED-EBD7-49B6-BDD9-CB729162705A</title>
|
||||
<desc>Created with sketchtool.</desc>
|
||||
<defs>
|
||||
<rect id="path-1" x="6" y="10" width="13" height="8" rx="1"></rect>
|
||||
<mask id="mask-2" maskContentUnits="userSpaceOnUse" maskUnits="objectBoundingBox" x="0" y="0" width="13" height="8" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
</defs>
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>icons_directory</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Symbols" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Room-list-Copy-3" transform="translate(-58.000000, -726.000000)">
|
||||
<g id="icons_directory" transform="translate(58.000000, 726.000000)">
|
||||
<path d="M12.5,25 C19.4035594,25 25,19.4035594 25,12.5 C25,5.59644063 19.4035594,0 12.5,0 C5.59644063,0 0,5.59644063 0,12.5 C0,19.4035594 5.59644063,25 12.5,25 Z" id="Oval-1-Copy-7" fill="#76CFA6"></path>
|
||||
<use id="Rectangle-9" stroke="#FFFFFF" mask="url(#mask-2)" stroke-width="2" opacity="0.8" xlink:href="#path-1"></use>
|
||||
<path d="M6,9 L6,6.99895656 C6,6.44724809 6.45097518,6 6.99077797,6 L11.009222,6 C11.5564136,6 12,6.44386482 12,7 L12,8 L17.9970707,8 C18.5509732,8 19,8.45318604 19,9 L19,9 L6,9 Z" id="Path-Copy" fill="#FFFFFF" opacity="0.6"></path>
|
||||
<g id="Left-panel" transform="translate(-83.000000, -726.000000)">
|
||||
<g id="icons_directory">
|
||||
<g transform="translate(83.000000, 726.000000)">
|
||||
<path d="M12.5,25 C19.4035594,25 25,19.4035594 25,12.5 C25,5.59644063 19.4035594,0 12.5,0 C5.59644063,0 0,5.59644063 0,12.5 C0,19.4035594 5.59644063,25 12.5,25 Z" id="Oval-1-Copy-7" fill="#76CFA6"></path>
|
||||
<g id="Lines" transform="translate(6.000000, 7.000000)" stroke="#FFFFFF" stroke-linecap="round">
|
||||
<path d="M4,5.5 L9,5.5" id="Line"></path>
|
||||
<path d="M4,1.5 L13,1.5" id="Line-Copy-4"></path>
|
||||
<path d="M0,1.5 L2,1.5" id="Line" opacity="0.6"></path>
|
||||
<path d="M0,5.5 L2,5.5" id="Line" opacity="0.6"></path>
|
||||
<path d="M4,9.5 L11,9.5" id="Line-Copy-6"></path>
|
||||
<path d="M0,9.5 L2,9.5" id="Line-Copy-3" opacity="0.6"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 19 KiB |
BIN
src/skins/vector/img/redacted-dark.jpg
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
src/skins/vector/img/redacted.jpg
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
21
src/skins/vector/img/scrollto.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 43.2 (39069) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Slice 1</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="scrollup">
|
||||
<g id="02-Chat" transform="translate(12.000000, 12.000000) scale(-1, 1) rotate(-180.000000) translate(-12.000000, -12.000000) ">
|
||||
<g id="02_7-Chat-new-messages">
|
||||
<g id="icon_newmessages">
|
||||
<circle id="Oval-1909" fill-opacity="0.5" fill="#454545" fill-rule="nonzero" cx="12" cy="12" r="12"></circle>
|
||||
<circle id="Oval" stroke="#FFFFFF" cx="12" cy="12" r="7"></circle>
|
||||
<circle id="Oval" stroke="#FFFFFF" cx="12" cy="12" r="4"></circle>
|
||||
<circle id="Oval" fill="#FFFFFF" cx="12" cy="12" r="1"></circle>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
@@ -259,11 +259,15 @@ async function loadApp() {
|
||||
let configError;
|
||||
try {
|
||||
configJson = await getConfig();
|
||||
rageshake.setBugReportEndpoint(configJson.bug_report_endpoint_url);
|
||||
} catch (e) {
|
||||
configError = e;
|
||||
}
|
||||
|
||||
if (window.localStorage && window.localStorage.getItem('mx_accepts_unsupported_browser')) {
|
||||
console.log('User has previously accepted risks in using an unsupported browser');
|
||||
validBrowser = true;
|
||||
}
|
||||
|
||||
console.log("Vector starting at "+window.location);
|
||||
if (configError) {
|
||||
window.matrixChat = ReactDOM.render(<div className="error">
|
||||
@@ -295,6 +299,7 @@ async function loadApp() {
|
||||
var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage");
|
||||
window.matrixChat = ReactDOM.render(
|
||||
<CompatibilityPage onAccept={function() {
|
||||
if (window.localStorage) window.localStorage.setItem('mx_accepts_unsupported_browser', true);
|
||||
validBrowser = true;
|
||||
console.log("User accepts the compatibility risks.");
|
||||
loadApp();
|
||||
|
||||
@@ -20,22 +20,20 @@ limitations under the License.
|
||||
import VectorBasePlatform from './VectorBasePlatform';
|
||||
import dis from 'matrix-react-sdk/lib/dispatcher';
|
||||
import q from 'q';
|
||||
import electron, {remote} from 'electron';
|
||||
|
||||
const electron = require('electron');
|
||||
const remote = electron.remote;
|
||||
remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
|
||||
|
||||
electron.remote.autoUpdater.on('update-downloaded', onUpdateDownloaded);
|
||||
|
||||
function onUpdateDownloaded(ev, releaseNotes, ver, date, updateURL) {
|
||||
function onUpdateDownloaded(ev: Event, releaseNotes: string, ver: string, date: Date, updateURL: string) {
|
||||
dis.dispatch({
|
||||
action: 'new_version',
|
||||
currentVersion: electron.remote.app.getVersion(),
|
||||
currentVersion: remote.app.getVersion(),
|
||||
newVersion: ver,
|
||||
releaseNotes: releaseNotes,
|
||||
});
|
||||
}
|
||||
|
||||
function platformFriendlyName() {
|
||||
function platformFriendlyName(): string {
|
||||
console.log(window.process);
|
||||
switch (window.process.platform) {
|
||||
case 'darwin':
|
||||
@@ -68,40 +66,52 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
try {
|
||||
remote.app.setBadgeCount(count);
|
||||
} catch (e) {
|
||||
console.error("Failed to set notification count", e);
|
||||
console.error('Failed to set notification count', e);
|
||||
}
|
||||
}
|
||||
|
||||
supportsNotifications() : boolean {
|
||||
supportsNotifications(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
maySendNotifications() : boolean {
|
||||
maySendNotifications(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
displayNotification(title: string, msg: string, avatarUrl: string, room: Object): Notification {
|
||||
|
||||
// GNOME notification spec parses HTML tags for styling...
|
||||
// Electron Docs state all supported linux notification systems follow this markup spec
|
||||
// https://github.com/electron/electron/blob/master/docs/tutorial/desktop-environment-integration.md#linux
|
||||
// maybe we should pass basic styling (italics, bold, underline) through from MD
|
||||
// we only have to strip out < and > as the spec doesn't include anything about things like &
|
||||
// so we shouldn't assume that all implementations will treat those properly. Very basic tag parsing is done.
|
||||
if (window.process.platform === 'linux') {
|
||||
msg = msg.replace(/</g, '<').replace(/>/g, '>');
|
||||
}
|
||||
|
||||
// Notifications in Electron use the HTML5 notification API
|
||||
const notification = new global.Notification(
|
||||
title,
|
||||
{
|
||||
body: msg,
|
||||
icon: avatarUrl,
|
||||
tag: "vector",
|
||||
tag: 'vector',
|
||||
silent: true, // we play our own sounds
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
notification.onclick = function() {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId
|
||||
room_id: room.roomId,
|
||||
});
|
||||
global.focus();
|
||||
const currentWin = electron.remote.getCurrentWindow();
|
||||
currentWin.show();
|
||||
currentWin.restore();
|
||||
currentWin.focus();
|
||||
const win = remote.getCurrentWindow();
|
||||
|
||||
if (win.isMinimized()) win.restore();
|
||||
else if (!win.isVisible()) win.show();
|
||||
else win.focus();
|
||||
};
|
||||
|
||||
return notification;
|
||||
@@ -111,8 +121,8 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
notif.close();
|
||||
}
|
||||
|
||||
getAppVersion() {
|
||||
return q(electron.remote.app.getVersion());
|
||||
getAppVersion(): Promise<string> {
|
||||
return q(remote.app.getVersion());
|
||||
}
|
||||
|
||||
pollForUpdate() {
|
||||
@@ -128,15 +138,19 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
electron.ipcRenderer.send('install_update');
|
||||
}
|
||||
|
||||
getDefaultDeviceDisplayName() {
|
||||
return "Riot Desktop on " + platformFriendlyName();
|
||||
getDefaultDeviceDisplayName(): string {
|
||||
return 'Riot Desktop on ' + platformFriendlyName();
|
||||
}
|
||||
|
||||
screenCaptureErrorString() {
|
||||
screenCaptureErrorString(): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
requestNotificationPermission() : Promise {
|
||||
requestNotificationPermission(): Promise<string> {
|
||||
return q('granted');
|
||||
}
|
||||
|
||||
reload() {
|
||||
remote.getCurrentWebContents().reload();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class VectorBasePlatform extends BasePlatform {
|
||||
* Get a sensible default display name for the
|
||||
* device Vector is running on
|
||||
*/
|
||||
getDefaultDeviceDisplayName() {
|
||||
getDefaultDeviceDisplayName(): string {
|
||||
return "Unknown device";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
this.favicon.badge(notif, {
|
||||
bgColor: bgColor
|
||||
bgColor: bgColor,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn(`Failed to set badge count: ${e.message}`);
|
||||
@@ -75,7 +75,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
* Returns true if the platform supports displaying
|
||||
* notifications, otherwise false.
|
||||
*/
|
||||
supportsNotifications() : boolean {
|
||||
supportsNotifications(): boolean {
|
||||
return Boolean(global.Notification);
|
||||
}
|
||||
|
||||
@@ -83,8 +83,8 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
* Returns true if the application currently has permission
|
||||
* to display notifications. Otherwise false.
|
||||
*/
|
||||
maySendNotifications() : boolean {
|
||||
return global.Notification.permission == 'granted';
|
||||
maySendNotifications(): boolean {
|
||||
return global.Notification.permission === 'granted';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +94,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
* that is 'granted' if the user allowed the request or
|
||||
* 'denied' otherwise.
|
||||
*/
|
||||
requestNotificationPermission() : Promise {
|
||||
requestNotificationPermission(): Promise<string> {
|
||||
// annoyingly, the latest spec says this returns a
|
||||
// promise, but this is only supported in Chrome 46
|
||||
// and Firefox 47, so adapt the callback API.
|
||||
@@ -113,13 +113,13 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
icon: avatarUrl,
|
||||
tag: "vector",
|
||||
silent: true, // we play our own sounds
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
notification.onclick = function() {
|
||||
dis.dispatch({
|
||||
action: 'view_room',
|
||||
room_id: room.roomId
|
||||
room_id: room.roomId,
|
||||
});
|
||||
global.focus();
|
||||
notification.close();
|
||||
@@ -132,7 +132,7 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}, 5 * 1000);
|
||||
}
|
||||
|
||||
_getVersion() {
|
||||
_getVersion(): Promise<string> {
|
||||
const deferred = q.defer();
|
||||
|
||||
// We add a cachebuster to the request to make sure that we know about
|
||||
@@ -148,19 +148,19 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
},
|
||||
(err, response, body) => {
|
||||
if (err || response.status < 200 || response.status >= 300) {
|
||||
if (err == null) err = { status: response.status };
|
||||
if (err === null) err = { status: response.status };
|
||||
deferred.reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
const ver = body.trim();
|
||||
deferred.resolve(ver);
|
||||
}
|
||||
},
|
||||
);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
getAppVersion() {
|
||||
getAppVersion(): Promise<string> {
|
||||
if (this.runningVersion !== null) {
|
||||
return q(this.runningVersion);
|
||||
}
|
||||
@@ -169,9 +169,9 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
|
||||
pollForUpdate() {
|
||||
this._getVersion().done((ver) => {
|
||||
if (this.runningVersion == null) {
|
||||
if (this.runningVersion === null) {
|
||||
this.runningVersion = ver;
|
||||
} else if (this.runningVersion != ver) {
|
||||
} else if (this.runningVersion !== ver) {
|
||||
dis.dispatch({
|
||||
action: 'new_version',
|
||||
currentVersion: this.runningVersion,
|
||||
@@ -187,23 +187,28 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
getDefaultDeviceDisplayName() {
|
||||
getDefaultDeviceDisplayName(): string {
|
||||
// strip query-string and fragment from uri
|
||||
let u = url.parse(window.location.href);
|
||||
const u = url.parse(window.location.href);
|
||||
u.search = "";
|
||||
u.hash = "";
|
||||
let app_name = u.format();
|
||||
const appName = u.format();
|
||||
|
||||
let ua = new UAParser();
|
||||
return app_name + " via " + ua.getBrowser().name +
|
||||
" on " + ua.getOS().name;
|
||||
const ua = new UAParser();
|
||||
return `${appName} via ${ua.getBrowser().name} on ${ua.getOS().name}`;
|
||||
}
|
||||
|
||||
screenCaptureErrorString() {
|
||||
screenCaptureErrorString(): ?string {
|
||||
// it won't work at all if you're not on HTTPS so whine whine whine
|
||||
if (!global.window || global.window.location.protocol !== "https:") {
|
||||
return "You need to be using HTTPS to place a screen-sharing call.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
reload() {
|
||||
// forceReload=false since we don't really need new HTML/JS files
|
||||
// we just need to restart the JS runtime.
|
||||
window.location.reload(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
import request from "browser-request";
|
||||
import q from "q";
|
||||
|
||||
// This module contains all the code needed to log the console, persist it to
|
||||
@@ -205,9 +203,6 @@ class IndexedDBLogStore {
|
||||
}
|
||||
let txn = this.db.transaction(["logs", "logslastmod"], "readwrite");
|
||||
let objStore = txn.objectStore("logs");
|
||||
objStore.add(this._generateLogEntry(lines));
|
||||
let lastModStore = txn.objectStore("logslastmod");
|
||||
lastModStore.put(this._generateLastModifiedTime());
|
||||
txn.oncomplete = (event) => {
|
||||
resolve();
|
||||
};
|
||||
@@ -219,6 +214,9 @@ class IndexedDBLogStore {
|
||||
new Error("Failed to write logs: " + event.target.errorCode)
|
||||
);
|
||||
}
|
||||
objStore.add(this._generateLogEntry(lines));
|
||||
let lastModStore = txn.objectStore("logslastmod");
|
||||
lastModStore.put(this._generateLastModifiedTime());
|
||||
});
|
||||
return this.flushPromise;
|
||||
}
|
||||
@@ -396,7 +394,6 @@ function selectQuery(store, keyRange, resultMapper) {
|
||||
let store = null;
|
||||
let logger = null;
|
||||
let initPromise = null;
|
||||
let bugReportEndpoint = null;
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
@@ -430,80 +427,29 @@ module.exports = {
|
||||
await store.consume();
|
||||
},
|
||||
|
||||
setBugReportEndpoint: function(url) {
|
||||
bugReportEndpoint = url;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a bug report.
|
||||
* @param {string} userText Any additional user input.
|
||||
* @param {boolean} sendLogs True to send logs
|
||||
* @return {Promise} Resolved when the bug report is sent.
|
||||
* Get a recent snapshot of the logs, ready for attaching to a bug report
|
||||
*
|
||||
* @return {Array<{lines: string, id, string}>} list of log data
|
||||
*/
|
||||
sendBugReport: async function(userText, sendLogs) {
|
||||
getLogsForReport: async function() {
|
||||
if (!logger) {
|
||||
throw new Error(
|
||||
"No console logger, did you forget to call init()?"
|
||||
);
|
||||
}
|
||||
if (!bugReportEndpoint) {
|
||||
throw new Error("No bug report endpoint has been set.");
|
||||
}
|
||||
|
||||
let version = "UNKNOWN";
|
||||
try {
|
||||
version = await PlatformPeg.get().getAppVersion();
|
||||
}
|
||||
catch (err) {} // PlatformPeg already logs this.
|
||||
|
||||
let userAgent = "UNKNOWN";
|
||||
if (window.navigator && window.navigator.userAgent) {
|
||||
userAgent = window.navigator.userAgent;
|
||||
}
|
||||
|
||||
// If in incognito mode, store is null, but we still want bug report
|
||||
// sending to work going off the in-memory console logs.
|
||||
console.log("Sending bug report.");
|
||||
let logs = [];
|
||||
if (sendLogs) {
|
||||
if (store) {
|
||||
// flush most recent logs
|
||||
await store.flush();
|
||||
logs = await store.consume();
|
||||
}
|
||||
else {
|
||||
logs.push({
|
||||
lines: logger.flush(true),
|
||||
id: "-",
|
||||
});
|
||||
}
|
||||
if (store) {
|
||||
// flush most recent logs
|
||||
await store.flush();
|
||||
return await store.consume();
|
||||
}
|
||||
|
||||
await q.Promise((resolve, reject) => {
|
||||
request({
|
||||
method: "POST",
|
||||
url: bugReportEndpoint,
|
||||
body: {
|
||||
logs: logs,
|
||||
text: (
|
||||
userText || "User did not supply any additional text."
|
||||
),
|
||||
version: version,
|
||||
user_agent: userAgent,
|
||||
},
|
||||
json: true,
|
||||
timeout: 5 * 60 * 1000,
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
if (res.status < 200 || res.status >= 400) {
|
||||
reject(new Error(`HTTP ${res.status}`));
|
||||
return;
|
||||
}
|
||||
resolve();
|
||||
})
|
||||
});
|
||||
}
|
||||
else {
|
||||
return [{
|
||||
lines: logger.flush(true),
|
||||
id: "-",
|
||||
}];
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
124
src/vector/submit-rageshake.js
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
Copyright 2017 OpenMarket Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import pako from 'pako';
|
||||
import q from "q";
|
||||
|
||||
import MatrixClientPeg from 'matrix-react-sdk/lib/MatrixClientPeg';
|
||||
import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg';
|
||||
|
||||
import rageshake from './rageshake'
|
||||
|
||||
|
||||
// polyfill textencoder if necessary
|
||||
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
|
||||
let TextEncoder = window.TextEncoder;
|
||||
if (!TextEncoder) {
|
||||
TextEncoder = TextEncodingUtf8.TextEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a bug report.
|
||||
*
|
||||
* @param {string} bugReportEndpoint HTTP url to send the report to
|
||||
*
|
||||
* @param {object} opts optional dictionary of options
|
||||
*
|
||||
* @param {string} opts.userText Any additional user input.
|
||||
*
|
||||
* @param {boolean} opts.sendLogs True to send logs
|
||||
*
|
||||
* @param {function(string)} opts.progressCallback Callback to call with progress updates
|
||||
*
|
||||
* @return {Promise} Resolved when the bug report is sent.
|
||||
*/
|
||||
export default async function sendBugReport(bugReportEndpoint, opts) {
|
||||
if (!bugReportEndpoint) {
|
||||
throw new Error("No bug report endpoint has been set.");
|
||||
}
|
||||
|
||||
opts = opts || {};
|
||||
const progressCallback = opts.progressCallback || (() => {});
|
||||
|
||||
progressCallback("Collecting app version information");
|
||||
let version = "UNKNOWN";
|
||||
try {
|
||||
version = await PlatformPeg.get().getAppVersion();
|
||||
}
|
||||
catch (err) {} // PlatformPeg already logs this.
|
||||
|
||||
let userAgent = "UNKNOWN";
|
||||
if (window.navigator && window.navigator.userAgent) {
|
||||
userAgent = window.navigator.userAgent;
|
||||
}
|
||||
|
||||
const client = MatrixClientPeg.get();
|
||||
|
||||
console.log("Sending bug report.");
|
||||
|
||||
const body = new FormData();
|
||||
body.append('text', opts.userText || "User did not supply any additional text.");
|
||||
body.append('app', 'riot-web');
|
||||
body.append('version', version);
|
||||
body.append('user_agent', userAgent);
|
||||
|
||||
if (client) {
|
||||
body.append('user_id', client.credentials.userId);
|
||||
body.append('device_id', client.deviceId);
|
||||
}
|
||||
|
||||
if (opts.sendLogs) {
|
||||
progressCallback("Collecting logs");
|
||||
const logs = await rageshake.getLogsForReport();
|
||||
for (let entry of logs) {
|
||||
// encode as UTF-8
|
||||
const buf = new TextEncoder().encode(entry.lines);
|
||||
|
||||
// compress
|
||||
const compressed = pako.gzip(buf);
|
||||
|
||||
body.append('compressed-log', new Blob([compressed]), entry.id);
|
||||
}
|
||||
}
|
||||
|
||||
progressCallback("Uploading report");
|
||||
await _submitReport(bugReportEndpoint, body, progressCallback);
|
||||
}
|
||||
|
||||
function _submitReport(endpoint, body, progressCallback) {
|
||||
const deferred = q.defer();
|
||||
|
||||
const req = new XMLHttpRequest();
|
||||
req.open("POST", endpoint);
|
||||
req.timeout = 5 * 60 * 1000;
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState === XMLHttpRequest.LOADING) {
|
||||
progressCallback("Waiting for response from server");
|
||||
} else if (req.readyState === XMLHttpRequest.DONE) {
|
||||
on_done();
|
||||
}
|
||||
};
|
||||
req.send(body);
|
||||
return deferred.promise;
|
||||
|
||||
function on_done() {
|
||||
if (req.status < 200 || req.status >= 400) {
|
||||
deferred.reject(new Error(`HTTP ${req.status}`));
|
||||
return;
|
||||
}
|
||||
deferred.resolve();
|
||||
}
|
||||
}
|
||||
@@ -72,14 +72,13 @@ describe('joining a room', function () {
|
||||
var ROOM_ALIAS = '#alias:localhost';
|
||||
var ROOM_ID = '!id:localhost';
|
||||
|
||||
httpBackend.when('PUT', '/presence/'+encodeURIComponent(USER_ID)+'/status')
|
||||
.respond(200, {});
|
||||
httpBackend.when('GET', '/pushrules').respond(200, {});
|
||||
httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' });
|
||||
httpBackend.when('GET', '/sync').respond(200, {});
|
||||
httpBackend.when('POST', '/publicRooms').respond(200, {chunk: []});
|
||||
httpBackend.when('GET', '/thirdparty/protocols').respond(200, {});
|
||||
httpBackend.when('GET', '/directory/room/'+encodeURIComponent(ROOM_ALIAS)).respond(200, { room_id: ROOM_ID });
|
||||
|
||||
// note that we deliberately do *not* set an expectation for a
|
||||
// presence update - setting one makes the first httpBackend.flush
|
||||
// return before the first /sync arrives.
|
||||
|
||||
// start with a logged-in client
|
||||
localStorage.setItem("mx_hs_url", HS_URL );
|
||||
@@ -94,9 +93,18 @@ describe('joining a room', function () {
|
||||
matrixChat._setPage(PageTypes.RoomDirectory);
|
||||
|
||||
var roomView;
|
||||
|
||||
// wait for /sync to happen
|
||||
return q.delay(1).then(() => {
|
||||
return httpBackend.flush();
|
||||
}).then(() => {
|
||||
// wait for the directory requests
|
||||
httpBackend.when('POST', '/publicRooms').respond(200, {chunk: []});
|
||||
httpBackend.when('GET', '/thirdparty/protocols').respond(200, {});
|
||||
return q.all([
|
||||
httpBackend.flush('/publicRooms'),
|
||||
httpBackend.flush('/thirdparty/protocols'),
|
||||
]);
|
||||
}).then(() => {
|
||||
var roomDir = ReactTestUtils.findRenderedComponentWithType(
|
||||
matrixChat, RoomDirectory);
|
||||
@@ -110,6 +118,7 @@ describe('joining a room', function () {
|
||||
|
||||
// that should create a roomview which will start a peek; wait
|
||||
// for the peek.
|
||||
httpBackend.when('GET', '/directory/room/'+encodeURIComponent(ROOM_ALIAS)).respond(200, { room_id: ROOM_ID });
|
||||
httpBackend.when('GET', '/rooms/'+encodeURIComponent(ROOM_ID)+"/initialSync")
|
||||
.respond(401, {errcode: 'M_GUEST_ACCESS_FORBIDDEN'});
|
||||
return httpBackend.flush();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var path = require('path');
|
||||
var webpack = require('webpack');
|
||||
var ExtractTextPlugin = require("extract-text-webpack-plugin");
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
@@ -19,11 +19,11 @@ module.exports = {
|
||||
|
||||
// CSS themes
|
||||
"theme-light": "./src/skins/vector/css/themes/light.scss",
|
||||
"theme-dark": "./src/skins/vector/css/themes/dark.scss"
|
||||
"theme-dark": "./src/skins/vector/css/themes/dark.scss",
|
||||
},
|
||||
module: {
|
||||
preLoaders: [
|
||||
{ test: /\.js$/, loader: "source-map-loader" }
|
||||
{ test: /\.js$/, loader: "source-map-loader" },
|
||||
],
|
||||
loaders: [
|
||||
{ test: /\.json$/, loader: "json" },
|
||||
@@ -38,9 +38,7 @@ module.exports = {
|
||||
// would also drag in the imgs and fonts that our CSS refers to
|
||||
// as webpack inputs.)
|
||||
// 3. ExtractTextPlugin turns that string into a separate asset.
|
||||
loader: ExtractTextPlugin.extract(
|
||||
"css-raw-loader!postcss-loader?config=postcss.config.js"
|
||||
),
|
||||
loader: ExtractTextPlugin.extract("css-raw-loader!postcss-loader?config=postcss.config.js"),
|
||||
},
|
||||
{
|
||||
// this works similarly to the scss case, without postcss.
|
||||
@@ -49,15 +47,18 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
noParse: [
|
||||
// for cross platform compatibility use [\\\/] as the path separator
|
||||
// this ensures that the regex trips on both Windows and *nix
|
||||
|
||||
// don't parse the languages within highlight.js. They cause stack
|
||||
// overflows (https://github.com/webpack/webpack/issues/1721), and
|
||||
// there is no need for webpack to parse them - they can just be
|
||||
// included as-is.
|
||||
/highlight\.js\/lib\/languages/,
|
||||
/highlight\.js[\\\/]lib[\\\/]languages/,
|
||||
|
||||
// olm takes ages for webpack to process, and it's already heavily
|
||||
// optimised, so there is little to gain by us uglifying it.
|
||||
/olm\/(javascript\/)?olm\.js$/,
|
||||
/olm[\\\/](javascript[\\\/])?olm\.js$/,
|
||||
],
|
||||
},
|
||||
output: {
|
||||
@@ -83,7 +84,7 @@ module.exports = {
|
||||
// various levels of '.' and '..'
|
||||
// Also, sometimes the resource path is absolute.
|
||||
return path.relative(process.cwd(), info.resourcePath).replace(/^[\/\.]*/, '');
|
||||
}
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
@@ -106,16 +107,13 @@ module.exports = {
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
}
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
|
||||
},
|
||||
}),
|
||||
|
||||
new ExtractTextPlugin(
|
||||
"bundles/[hash]/[name].css",
|
||||
{
|
||||
allChunks: true
|
||||
}
|
||||
),
|
||||
new ExtractTextPlugin("bundles/[hash]/[name].css", {
|
||||
allChunks: true,
|
||||
}),
|
||||
|
||||
new HtmlWebpackPlugin({
|
||||
template: './src/vector/index.html',
|
||||
|
||||