diff --git a/.gitignore b/.gitignore index c9e9b250be..8e51a1faec 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ npm-debug.log electron/dist electron/pub /.idea +config.json diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 33b44ce9d1..e6ffffac9b 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -201,9 +201,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; }); diff --git a/package.json b/package.json index 81b87f6204..843b69dc19 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "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", @@ -38,7 +38,7 @@ "dist": "scripts/package.sh", "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:js:prod": "cross-env NODE_ENV=production webpack-dev-server -w --progress", "start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\"", "start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"", "lint": "eslint src/", @@ -93,6 +93,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", diff --git a/scripts/copy-res.js b/scripts/copy-res.js index 826d9a96b3..4702d5cf1b 100755 --- a/scripts/copy-res.js +++ b/scripts/copy-res.js @@ -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}], ]; diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index e723cf1ad6..f8a3d9e981 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -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"), }); }); }, @@ -210,8 +209,8 @@ module.exports = React.createClass({ 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"); diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index 88630891e1..577dac9c8f 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -456,8 +456,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; @@ -515,6 +515,7 @@ var RoomSubList = React.createClass({ roomCount={ roomCount } collapsed={ this.props.collapsed } hidden={ this.state.hidden } + incomingCall={ this.props.incomingCall } isIncomingCallRoom={ isIncomingCallRoom } roomNotificationCount={ this.roomNotificationCount() } onClick={ this.onClick } diff --git a/src/components/structures/RoomSubListHeader.js b/src/components/structures/RoomSubListHeader.js index 5618b39b85..ad9aff5f70 100644 --- a/src/components/structures/RoomSubListHeader.js +++ b/src/components/structures/RoomSubListHeader.js @@ -36,6 +36,7 @@ module.exports = React.createClass({ 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, @@ -111,8 +112,8 @@ module.exports = React.createClass({
{ roomCount }
{ badge } - { incomingCall } + { incomingCall } ); }, diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index d981a36751..7efa68485c 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -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"), }); }); diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 42730aca79..ab3e9ee8f0 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -176,7 +176,7 @@ module.exports = React.createClass({ { this.getName() } { eventMeta } - +
Download this file
{ size_res } diff --git a/src/components/views/login/VectorLoginHeader.js b/src/components/views/login/VectorLoginHeader.js index daa2b09c67..1b071c6aca 100644 --- a/src/components/views/login/VectorLoginHeader.js +++ b/src/components/views/login/VectorLoginHeader.js @@ -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 (
- Riot + Riot
); } diff --git a/src/components/views/rooms/DNDRoomTile.js b/src/components/views/rooms/DNDRoomTile.js index 2fcdb47da3..4bcf29ed9a 100644 --- a/src/components/views/rooms/DNDRoomTile.js +++ b/src/components/views/rooms/DNDRoomTile.js @@ -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"), }); }); } diff --git a/src/components/views/settings/Notifications.js b/src/components/views/settings/Notifications.js index b685c1f321..11dc79ac20 100644 --- a/src/components/views/settings/Notifications.js +++ b/src/components/views/settings/Notifications.js @@ -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 }); } diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index a44a503e2d..5b1d1de6f1 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -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; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss index 9c883caa73..1286193974 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -171,7 +171,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 +259,4 @@ hr.mx_RoomView_myReadMarker { .mx_RoomView_ongoingConfCallNotification a { color: $accent-fg-color ! important; -} \ No newline at end of file +} diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss index 099b5b518f..9f33e1bcc2 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -213,3 +213,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; +} diff --git a/src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss b/src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss index f21bb16951..124304802a 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss @@ -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,6 +164,66 @@ limitations under the License. margin-bottom: 12px; } +.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_username_group { + display: flex; +} + +.mx_Login_username_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_username_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; + /* The "@" is always prefixed, so no rounded corners */ + 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_phoneSection { display: table; } @@ -176,6 +233,20 @@ limitations under the License. width: 70px; } +.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; +} + .mx_Login_phoneNumberField { width: 210px; margin-left: 3px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_Dropdown.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_Dropdown.scss index b96290bd9b..1f9cbb63dd 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_Dropdown.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_Dropdown.scss @@ -27,6 +27,10 @@ limitations under the License. user-select: none; } +.mx_Dropdown_input:focus { + border-color: $accent-color; +} + .mx_Dropdown_arrow { border-color: $primary-fg-color transparent transparent; border-style: solid; @@ -74,7 +78,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 { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss index 6b0c8863d4..7b76eb7f98 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss @@ -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 { diff --git a/src/skins/vector/img/icons-directory.svg b/src/skins/vector/img/icons-directory.svg index 1f1cc29a82..2688b84713 100644 --- a/src/skins/vector/img/icons-directory.svg +++ b/src/skins/vector/img/icons-directory.svg @@ -1,21 +1,24 @@ - - E34C64ED-EBD7-49B6-BDD9-CB729162705A - Created with sketchtool. - - - - - - + + icons_directory + Created with Sketch. + - - - - - + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/src/skins/vector/img/logo.png b/src/skins/vector/img/logo.png deleted file mode 100644 index da6bb327f9..0000000000 Binary files a/src/skins/vector/img/logo.png and /dev/null differ diff --git a/src/skins/vector/img/scrollto.svg b/src/skins/vector/img/scrollto.svg new file mode 100644 index 0000000000..75df053a68 --- /dev/null +++ b/src/skins/vector/img/scrollto.svg @@ -0,0 +1,21 @@ + + + + Slice 1 + Created with Sketch. + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/vector/index.js b/src/vector/index.js index 42a60e455c..0d704d995d 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -263,6 +263,11 @@ async function loadApp() { 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(
@@ -294,6 +299,7 @@ async function loadApp() { var CompatibilityPage = sdk.getComponent("structures.CompatibilityPage"); window.matrixChat = ReactDOM.render( 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, ">"); + } + // Notifications in Electron use the HTML5 notification API const notification = new global.Notification( title, diff --git a/webpack.config.js b/webpack.config.js index e32f717702..efeab7fe8b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -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',