From 3f640139d240832d1bd1d49863096efa4f392bb7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:10:33 +0000 Subject: [PATCH 0001/1951] quick and dirty support for custom welcome pages, with an example for geektime techfest --- home-geektime.html | 25 +++++++ src/component-index.js | 2 + src/components/structures/BottomLeftMenu.js | 27 +++++++ src/components/structures/HomePage.js | 66 ++++++++++++++++++ .../css/vector-web/structures/HomePage.css | 26 +++++++ .../css/vector-web/structures/LeftPanel.css | 3 + src/skins/vector/img/geektime/geektime.png | Bin 0 -> 32638 bytes src/skins/vector/img/icons-home.svg | 28 ++++++++ 8 files changed, 177 insertions(+) create mode 100644 home-geektime.html create mode 100644 src/components/structures/HomePage.js create mode 100644 src/skins/vector/css/vector-web/structures/HomePage.css create mode 100644 src/skins/vector/img/geektime/geektime.png create mode 100644 src/skins/vector/img/icons-home.svg diff --git a/home-geektime.html b/home-geektime.html new file mode 100644 index 00000000..cea2eac3 --- /dev/null +++ b/home-geektime.html @@ -0,0 +1,25 @@ +
+ + GeekTime Techfest! + +
+ +

+Welcome to the GeekTime Techfest Riot! +

+ +

+To get started, please join some chat rooms! +

+ + + +

+To explore other rooms available on Matrix, click here: Room Directory +

+ +

+To learn more about Matrix, head over to Matrix.org - and to better understand Riot, check out the official Riot Website. +

\ No newline at end of file diff --git a/src/component-index.js b/src/component-index.js index 3141087c..068ab061 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -34,6 +34,8 @@ import structures$LeftPanel from './components/structures/LeftPanel'; module.exports.components['structures.LeftPanel'] = structures$LeftPanel; import structures$RightPanel from './components/structures/RightPanel'; module.exports.components['structures.RightPanel'] = structures$RightPanel; +import structures$HomePage from './components/structures/HomePage'; +module.exports.components['structures.HomePage'] = structures$HomePage; import structures$RoomDirectory from './components/structures/RoomDirectory'; module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory; import structures$RoomSubList from './components/structures/RoomSubList'; diff --git a/src/components/structures/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js index 0ea35b4e..793f8405 100644 --- a/src/components/structures/BottomLeftMenu.js +++ b/src/components/structures/BottomLeftMenu.js @@ -20,6 +20,7 @@ var React = require('react'); var ReactDOM = require('react-dom'); var sdk = require('matrix-react-sdk') var dis = require('matrix-react-sdk/lib/dispatcher'); +var SdkConfig = require('matrix-react-sdk/lib/SdkConfig'); module.exports = React.createClass({ displayName: 'BottomLeftMenu', @@ -32,6 +33,7 @@ module.exports = React.createClass({ return({ directoryHover : false, roomsHover : false, + homeHover: false, peopleHover : false, settingsHover : false, }); @@ -62,6 +64,19 @@ module.exports = React.createClass({ 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' }); @@ -98,9 +113,21 @@ module.exports = React.createClass({ render: function() { var TintableSvg = sdk.getComponent('elements.TintableSvg'); + + var homeButton; + if (SdkConfig.get().home_page) { + homeButton = ( +
+ + { this.getLabel("Welcome page", this.state.homeHover) } +
+ ); + } + return (
+ { homeButton }
{ this.getLabel("Start chat", this.state.peopleHover) } diff --git a/src/components/structures/HomePage.js b/src/components/structures/HomePage.js new file mode 100644 index 00000000..26bdf81e --- /dev/null +++ b/src/components/structures/HomePage.js @@ -0,0 +1,66 @@ +/* +Copyright 2016 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. +*/ + +'use strict'; + +import 'isomorphic-fetch'; + +var React = require("react"); +var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); +var sdk = require('matrix-react-sdk'); + +module.exports = React.createClass({ + displayName: 'HomePage', + + propTypes: { + config: React.PropTypes.object.isRequired, + collapsedRhs: React.PropTypes.bool, + }, + + getInitialState: function() { + return { + page: "" + }; + }, + + componentWillMount: function() { + fetch(this.props.config.home_page).then( + (response)=>{ + return response.text(); + }, + (error)=>{ + console.log(error); + this.setState({ page: "Couldn't load home page" }); + } + ).then( + (body)=>{ + this.setState({ page: body }); + } + ); + }, + + render: function() { + // const SimpleRoomHeader = sdk.getComponent('rooms.SimpleRoomHeader'); + // + + return ( +
+
+
+
+ ); + } +}); diff --git a/src/skins/vector/css/vector-web/structures/HomePage.css b/src/skins/vector/css/vector-web/structures/HomePage.css new file mode 100644 index 00000000..0e2009a6 --- /dev/null +++ b/src/skins/vector/css/vector-web/structures/HomePage.css @@ -0,0 +1,26 @@ +/* +Copyright 2016 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. +*/ + +.mx_HomePage { + max-width: 960px; + width: 100%; + margin-left: auto; + margin-right: auto; +} + +.mx_HomePage_body { + margin-left: 63px; +} \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/structures/LeftPanel.css b/src/skins/vector/css/vector-web/structures/LeftPanel.css index 4e328171..35d3e22b 100644 --- a/src/skins/vector/css/vector-web/structures/LeftPanel.css +++ b/src/skins/vector/css/vector-web/structures/LeftPanel.css @@ -79,6 +79,7 @@ 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, @@ -87,6 +88,7 @@ limitations under the License. cursor: pointer; } +.collapsed .mx_BottomLeftMenu_homePage, .collapsed .mx_BottomLeftMenu_directory, .collapsed .mx_BottomLeftMenu_createRoom, .collapsed .mx_BottomLeftMenu_people, @@ -96,6 +98,7 @@ limitations under the License. padding-bottom: 3px ! important; } +.mx_LeftPanel .mx_BottomLeftMenu_homePage, .mx_LeftPanel .mx_BottomLeftMenu_directory, .mx_LeftPanel .mx_BottomLeftMenu_createRoom, .mx_LeftPanel .mx_BottomLeftMenu_people { diff --git a/src/skins/vector/img/geektime/geektime.png b/src/skins/vector/img/geektime/geektime.png new file mode 100644 index 0000000000000000000000000000000000000000..fcb7b01af0254b72963bf34ef5530f11193596b5 GIT binary patch literal 32638 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 + + + + + 81230A28-D944-4572-B5DB-C03CAA2B1FCA + Created with sketchtool. + + + + + + + + + + + + + + + + + From efabf356137a4ca9bf3106aaf26d095d34fbfcd6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:13:32 +0000 Subject: [PATCH 0002/1951] make room previews more obvious --- .../vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css index 286a39f7..c1af5ace 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css @@ -18,6 +18,8 @@ limitations under the License. text-align: center; height: 176px; + background-color: #eeeeee; + -webkit-align-items: center; align-items: center; From 9f812b7069aa2ceb33415e17838486f10b6b2d09 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:18:31 +0000 Subject: [PATCH 0003/1951] fix img path --- home-geektime.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home-geektime.html b/home-geektime.html index cea2eac3..0f483d6c 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -1,6 +1,6 @@ From 6cf14e6a2a3a04f9cfbf46c01a3d0f835a026d9f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:19:55 +0000 Subject: [PATCH 0004/1951] fix room alias --- home-geektime.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home-geektime.html b/home-geektime.html index 0f483d6c..7bf75a10 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -13,7 +13,7 @@ To get started, please join some chat rooms!

From 616c20fc1e38fc147a22c7a3308345e6c3e07d7d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:26:54 +0000 Subject: [PATCH 0005/1951] fix img path --- home-geektime.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/home-geektime.html b/home-geektime.html index 7bf75a10..1c866178 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -17,9 +17,9 @@ To get started, please join some chat rooms!

-To explore other rooms available on Matrix, click here: Room Directory +To explore other rooms available on Matrix, click here: Room Directory

To learn more about Matrix, head over to Matrix.org - and to better understand Riot, check out the official Riot Website. -

\ No newline at end of file +

From 1fb14d5a40a40b42a05b7765b17e6cccb28f23f9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 14:32:00 +0000 Subject: [PATCH 0006/1951] plug mobile apps --- home-geektime.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/home-geektime.html b/home-geektime.html index 1c866178..4aaf2d1d 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -20,6 +20,10 @@ To get started, please join some chat rooms! To explore other rooms available on Matrix, click here: Room Directory

+

+Riot is also available on iOS and Android (Play Store and Fdroid Store) as a 100% native mobile app. +

+

To learn more about Matrix, head over to Matrix.org - and to better understand Riot, check out the official Riot Website.

From f911f0cfca1f2d2836bb39e13da7a44591149184 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 13 Nov 2016 20:18:19 +0000 Subject: [PATCH 0007/1951] geektime verbiage tweaks --- home-geektime.html | 20 ++++++++++++++---- .../views/rooms/RoomPreviewBar.css | 2 +- src/skins/vector/img/geektime/devfest.png | Bin 0 -> 92008 bytes 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 src/skins/vector/img/geektime/devfest.png diff --git a/home-geektime.html b/home-geektime.html index 4aaf2d1d..06bb603a 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -1,19 +1,29 @@

-Welcome to the GeekTime Techfest Riot! +Welcome to the Geektime Devfest 2016 Riot!

+

+This is where you can meet and chat with all the other attendees and speakers and ask them more questions following their sessions. +

+

To get started, please join some chat rooms!

@@ -25,5 +35,7 @@ Riot is also available on Matrix.org - and to better understand Riot, check out the official Riot Website. +Riot is built on Matrix - a new open protocol for interoperable and secure decentralised communication. +
+To learn more, head over to Matrix.org - and to better understand Riot, check out the official Riot Website.

diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css index c1af5ace..d4ba4997 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css @@ -18,7 +18,7 @@ limitations under the License. text-align: center; height: 176px; - background-color: #eeeeee; + background-color: #f7f7f7; -webkit-align-items: center; align-items: center; diff --git a/src/skins/vector/img/geektime/devfest.png b/src/skins/vector/img/geektime/devfest.png new file mode 100644 index 0000000000000000000000000000000000000000..1dfed09ab81717b7efd25b6757c24148b1cbc421 GIT binary patch literal 92008 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
  • sm*K7MbQ zrBORRK)%8If6*9_Zp4xArB{Fa%UEr!F7`JiM;P>Mu?+@$0 zSN Date: Mon, 14 Nov 2016 01:46:42 +0000 Subject: [PATCH 0008/1951] make logo scale --- home-geektime.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home-geektime.html b/home-geektime.html index 06bb603a..3e7d793e 100644 --- a/home-geektime.html +++ b/home-geektime.html @@ -1,6 +1,6 @@ From 3155d79a7ea557bce4e6e17c6863e9b933cf03c3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Thu, 17 Nov 2016 13:35:36 +0200 Subject: [PATCH 0009/1951] chatbot summit stuff --- home-chatbotsummit.html | 35 ++++++++++++++++++ .../vector/img/chatbotsummit/chatbot.png | Bin 0 -> 53720 bytes 2 files changed, 35 insertions(+) create mode 100644 home-chatbotsummit.html create mode 100644 src/skins/vector/img/chatbotsummit/chatbot.png diff --git a/home-chatbotsummit.html b/home-chatbotsummit.html new file mode 100644 index 00000000..a8af7a4a --- /dev/null +++ b/home-chatbotsummit.html @@ -0,0 +1,35 @@ + + +

    +Welcome to the Chatbot Summit Tel Aviv 2016 Riot! +

    + +

    +This is where you can meet and chat with all the other attendees and speakers and ask them more questions following their sessions. +

    + +

    +To get started, please join some chat rooms! +

    + + + +

    +To explore other rooms available on Matrix, click here: Room Directory +

    + +

    +Riot is also available on iOS and Android (Play Store and Fdroid Store) as a 100% native mobile app. +

    + +

    +Riot is built on Matrix - a new open protocol for interoperable and secure decentralised communication. +
    +To learn more, head over to Matrix.org - and to better understand Riot, check out the official Riot Website. +

    diff --git a/src/skins/vector/img/chatbotsummit/chatbot.png b/src/skins/vector/img/chatbotsummit/chatbot.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d5715f4cdd960a0984ae249cedceda0336d49a GIT binary patch literal 53720 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

    N>i$%YUR7Jp*<4#@~7AhL9Oh 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 literal 0 HcmV?d00001 From bba6664b1cf685d9666da63eacb4d8bbcb0ac764 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 18:58:42 +0000 Subject: [PATCH 0010/1951] bump to dep on react-sdk 0.8.3-electron --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 34df1c4e..a8197c22 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", "matrix-js-sdk": "0.7.2", - "matrix-react-sdk": "0.8.3", + "matrix-react-sdk": "0.8.3-electron", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From cbf105f2a0c448dcd27302be13de299fb43e7d4b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 18:59:41 +0000 Subject: [PATCH 0011/1951] Revert "bump to dep on react-sdk 0.8.3-electron" This reverts commit bba6664b1cf685d9666da63eacb4d8bbcb0ac764. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a8197c22..34df1c4e 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", "matrix-js-sdk": "0.7.2", - "matrix-react-sdk": "0.8.3-electron", + "matrix-react-sdk": "0.8.3", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From 078493912c97f83971ca225e962077daa0e2dd79 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 03:18:12 +0000 Subject: [PATCH 0012/1951] make electron send email validation URLs with a nextlink of riot.im rather than file:/// --- src/vector/index.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index b791783b..8231950b 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -136,11 +136,20 @@ var onNewScreen = function(screen) { // click back to the client having registered. // It's up to us to recognise if we're loaded with // this URL and tell MatrixClient to resume registration. +// +// If we're in electron, we should never pass through a file:// URL otherwise +// the identity server will try to 302 the browser to it, which breaks horribly. +// so in that instance, hardcode to use riot.im/app for now instead. var makeRegistrationUrl = function() { - return window.location.protocol + '//' + - window.location.host + - window.location.pathname + - '#/register'; + if (window.location.protocol === "file:") { + return 'https://riot.im/app/#/register'; + } + else { + return window.location.protocol + '//' + + window.location.host + + window.location.pathname + + '#/register'; + } } window.addEventListener('hashchange', onHashChange); From 8c523be6f52189ea0b731e6b0181d09f0b1f2a62 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 18:13:20 +0000 Subject: [PATCH 0013/1951] add gnu-tar to debian electron build deps --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50a8ff04..54709e08 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,7 @@ The only platform that can build packages for all three platforms is macOS: ``` brew install wine --without-x11 brew install mono +brew install gnu-tar npm install npm run build:electron ``` From 4de042bf587f42a9f91df4db22c536a6f8a6c0b6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 18:13:33 +0000 Subject: [PATCH 0014/1951] empirically fix win32 shortcut in start menu --- electron/src/squirrelhooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/electron/src/squirrelhooks.js b/electron/src/squirrelhooks.js index 10fb8d9e..295ef5cf 100644 --- a/electron/src/squirrelhooks.js +++ b/electron/src/squirrelhooks.js @@ -3,7 +3,7 @@ const spawn = require('child_process').spawn; const app = require('electron').app; function run_update_exe(args, done) { - const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); + const updateExe = path.resolve(path.dirname(process.execPath), 'Update.exe'); spawn(updateExe, args, { detached: true }).on('close', done); From 3f3772463b4c304d6d790b54a5c2d825e900facb Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 19:04:00 +0000 Subject: [PATCH 0015/1951] bump react dep --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 34df1c4e..1d2bb886 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", "matrix-js-sdk": "0.7.2", - "matrix-react-sdk": "0.8.3", + "matrix-react-sdk": "0.8.4", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From 0129da4dd6990e965a5572073c05ea4e767f7dc3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 19:07:30 +0000 Subject: [PATCH 0016/1951] Prepare changelog for v0.9.5 --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5427b8dc..265cbe80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +Changes in [0.9.5](https://github.com/vector-im/riot-web/releases/tag/v0.9.5) (2016-12-24) +========================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.4...v0.9.5) + + * make electron send email validation URLs with a nextlink of riot.im rather than file:/// + * add gnu-tar to debian electron build deps + * fix win32 shortcut in start menu + Changes in [0.9.4](https://github.com/vector-im/riot-web/releases/tag/v0.9.4) (2016-12-22) ========================================================================================== [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.3...v0.9.4) From 747408871d71c8d020672e39140725c136a3b709 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 24 Dec 2016 19:07:31 +0000 Subject: [PATCH 0017/1951] v0.9.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d2bb886..02e280a5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "riot-web", "productName": "Riot", "main": "electron/src/electron-main.js", - "version": "0.9.4", + "version": "0.9.5", "description": "A feature-rich client for Matrix.org", "author": "Vector Creations Ltd.", "repository": { From 650269d3568d2a453aab10e5e2266f34782c1558 Mon Sep 17 00:00:00 2001 From: Kevin McDonald Date: Mon, 26 Dec 2016 21:33:27 -0700 Subject: [PATCH 0018/1951] Add ISSUE_TEMPLATE --- ISSUE_TEMPLATE.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 ISSUE_TEMPLATE.md diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..8359ec67 --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,31 @@ + + +I have: +- [ ] searched open and closed issues for duplicates + +---------------------------------------- + +### Version info + +**Riot (vector-web) Version:** 0.0.0 +**Operating System:** + +### Bug description +Describe here the issue that you are experiencing. + +### Steps to reproduce +- using hyphens as bullet points +- list the steps +- that reproduce the bug + +**Actual result:** Describe here what happens after you run the steps above (i.e. the buggy behaviour) +**Expected result:** Describe here what should happen after you run the steps above (i.e. what would be the correct behaviour) + +### Screenshots + + +### debug log + From bacb284415a90811fbad2bae74b1921bf92a8fd9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 28 Dec 2016 02:01:28 +0000 Subject: [PATCH 0019/1951] basic jig for SASS-based themed CSS (one bundle per theme) --- package.json | 6 ++++-- src/skins/vector/css/{common.css => _common.scss} | 0 .../{ContextualMenu.css => _ContextualMenu.scss} | 0 .../structures/{CreateRoom.css => _CreateRoom.scss} | 0 .../structures/{FilePanel.css => _FilePanel.scss} | 0 .../structures/{MatrixChat.css => _MatrixChat.scss} | 0 .../{NotificationPanel.css => _NotificationPanel.scss} | 0 .../structures/{RoomStatusBar.css => _RoomStatusBar.scss} | 0 .../structures/{RoomView.css => _RoomView.scss} | 0 .../structures/{SearchBox.css => _SearchBox.scss} | 0 .../structures/{UploadBar.css => _UploadBar.scss} | 0 .../structures/{UserSettings.css => _UserSettings.scss} | 0 .../structures/login/{Login.css => _Login.scss} | 0 .../views/avatars/{BaseAvatar.css => _BaseAvatar.scss} | 0 .../{ChatInviteDialog.css => _ChatInviteDialog.scss} | 0 ...EncryptedEventDialog.css => _EncryptedEventDialog.scss} | 0 ...SetDisplayNameDialog.css => _SetDisplayNameDialog.scss} | 0 .../{AddressSelector.css => _AddressSelector.scss} | 0 .../views/elements/{AddressTile.css => _AddressTile.scss} | 0 .../{DirectorySearchBox.css => _DirectorySearchBox.scss} | 0 ...erEventListSummary.css => _MemberEventListSummary.scss} | 0 .../views/elements/{ProgressBar.css => _ProgressBar.scss} | 0 .../views/elements/{RichText.css => _RichText.scss} | 0 .../views/login/{ServerConfig.css => _ServerConfig.scss} | 0 .../views/messages/{MImageBody.css => _MImageBody.scss} | 0 .../views/messages/{MNoticeBody.css => _MNoticeBody.scss} | 0 .../views/messages/{MTextBody.css => _MTextBody.scss} | 0 .../messages/{TextualEvent.css => _TextualEvent.scss} | 0 .../views/messages/{UnknownBody.css => _UnknownBody.scss} | 0 .../views/rooms/{Autocomplete.css => _Autocomplete.scss} | 0 .../views/rooms/{EntityTile.css => _EntityTile.scss} | 0 .../views/rooms/{EventTile.css => _EventTile.scss} | 0 .../{LinkPreviewWidget.css => _LinkPreviewWidget.scss} | 0 .../rooms/{MemberDeviceInfo.css => _MemberDeviceInfo.scss} | 0 .../views/rooms/{MemberInfo.css => _MemberInfo.scss} | 0 .../views/rooms/{MemberList.css => _MemberList.scss} | 0 .../rooms/{MessageComposer.css => _MessageComposer.scss} | 0 .../views/rooms/{PresenceLabel.css => _PresenceLabel.scss} | 0 .../views/rooms/{RoomHeader.css => _RoomHeader.scss} | 0 .../views/rooms/{RoomList.css => _RoomList.scss} | 0 .../rooms/{RoomPreviewBar.css => _RoomPreviewBar.scss} | 0 .../views/rooms/{RoomSettings.css => _RoomSettings.scss} | 0 .../views/rooms/{RoomTile.css => _RoomTile.scss} | 0 ...SearchableEntityList.css => _SearchableEntityList.scss} | 0 .../rooms/{TabCompleteBar.css => _TabCompleteBar.scss} | 0 ...TopUnreadMessagesBar.css => _TopUnreadMessagesBar.scss} | 0 .../settings/{DevicesPanel.css => _DevicesPanel.scss} | 0 .../{IntegrationsManager.css => _IntegrationsManager.scss} | 0 .../views/voip/{CallView.css => _CallView.scss} | 0 .../voip/{IncomingCallbox.css => _IncomingCallbox.scss} | 0 .../views/voip/{VideoView.css => _VideoView.scss} | 0 src/skins/vector/css/themes/dark.scss | 7 +++++++ src/skins/vector/css/themes/light.scss | 4 ++++ src/skins/vector/css/vector-web/{fonts.css => _fonts.scss} | 0 .../{CompatibilityPage.css => _CompatibilityPage.scss} | 0 .../structures/{LeftPanel.css => _LeftPanel.scss} | 0 .../structures/{RightPanel.css => _RightPanel.scss} | 0 .../structures/{RoomDirectory.css => _RoomDirectory.scss} | 0 .../structures/{RoomSubList.css => _RoomSubList.scss} | 0 .../structures/{ViewSource.css => _ViewSource.scss} | 0 .../{MessageContextMenu.css => _MessageContextMenu.scss} | 0 ...eContextMenu.css => _NotificationStateContextMenu.scss} | 0 .../{RoomTagContextMenu.css => _RoomTagContextMenu.scss} | 0 .../dialogs/{ChangelogDialog.css => _ChangelogDialog.scss} | 0 .../{NetworkDropdown.css => _NetworkDropdown.scss} | 0 .../views/elements/{ImageView.css => _ImageView.scss} | 0 .../views/elements/{Spinner.css => _Spinner.scss} | 0 .../globals/{GuestWarningBar.css => _GuestWarningBar.scss} | 0 .../globals/{MatrixToolbar.css => _MatrixToolbar.scss} | 0 .../{MessageTimestamp.css => _MessageTimestamp.scss} | 0 .../messages/{SenderProfile.css => _SenderProfile.scss} | 0 .../rooms/{RoomDropTarget.css => _RoomDropTarget.scss} | 0 .../views/rooms/{RoomTooltip.css => _RoomTooltip.scss} | 0 .../views/rooms/{SearchBar.css => _SearchBar.scss} | 0 .../settings/{Notifications.css => _Notifications.scss} | 0 src/vector/index.js | 2 +- webpack.config.js | 1 + 77 files changed, 17 insertions(+), 3 deletions(-) rename src/skins/vector/css/{common.css => _common.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{ContextualMenu.css => _ContextualMenu.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{CreateRoom.css => _CreateRoom.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{FilePanel.css => _FilePanel.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{MatrixChat.css => _MatrixChat.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{NotificationPanel.css => _NotificationPanel.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{RoomStatusBar.css => _RoomStatusBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{RoomView.css => _RoomView.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{SearchBox.css => _SearchBox.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{UploadBar.css => _UploadBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/{UserSettings.css => _UserSettings.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/structures/login/{Login.css => _Login.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/avatars/{BaseAvatar.css => _BaseAvatar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/dialogs/{ChatInviteDialog.css => _ChatInviteDialog.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/dialogs/{EncryptedEventDialog.css => _EncryptedEventDialog.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/dialogs/{SetDisplayNameDialog.css => _SetDisplayNameDialog.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{AddressSelector.css => _AddressSelector.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{AddressTile.css => _AddressTile.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{DirectorySearchBox.css => _DirectorySearchBox.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{MemberEventListSummary.css => _MemberEventListSummary.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{ProgressBar.css => _ProgressBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/elements/{RichText.css => _RichText.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/login/{ServerConfig.css => _ServerConfig.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/messages/{MImageBody.css => _MImageBody.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/messages/{MNoticeBody.css => _MNoticeBody.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/messages/{MTextBody.css => _MTextBody.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/messages/{TextualEvent.css => _TextualEvent.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/messages/{UnknownBody.css => _UnknownBody.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{Autocomplete.css => _Autocomplete.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{EntityTile.css => _EntityTile.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{EventTile.css => _EventTile.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{LinkPreviewWidget.css => _LinkPreviewWidget.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{MemberDeviceInfo.css => _MemberDeviceInfo.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{MemberInfo.css => _MemberInfo.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{MemberList.css => _MemberList.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{MessageComposer.css => _MessageComposer.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{PresenceLabel.css => _PresenceLabel.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{RoomHeader.css => _RoomHeader.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{RoomList.css => _RoomList.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{RoomPreviewBar.css => _RoomPreviewBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{RoomSettings.css => _RoomSettings.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{RoomTile.css => _RoomTile.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{SearchableEntityList.css => _SearchableEntityList.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{TabCompleteBar.css => _TabCompleteBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/rooms/{TopUnreadMessagesBar.css => _TopUnreadMessagesBar.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/settings/{DevicesPanel.css => _DevicesPanel.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/settings/{IntegrationsManager.css => _IntegrationsManager.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/voip/{CallView.css => _CallView.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/voip/{IncomingCallbox.css => _IncomingCallbox.scss} (100%) rename src/skins/vector/css/matrix-react-sdk/views/voip/{VideoView.css => _VideoView.scss} (100%) create mode 100644 src/skins/vector/css/themes/dark.scss create mode 100644 src/skins/vector/css/themes/light.scss rename src/skins/vector/css/vector-web/{fonts.css => _fonts.scss} (100%) rename src/skins/vector/css/vector-web/structures/{CompatibilityPage.css => _CompatibilityPage.scss} (100%) rename src/skins/vector/css/vector-web/structures/{LeftPanel.css => _LeftPanel.scss} (100%) rename src/skins/vector/css/vector-web/structures/{RightPanel.css => _RightPanel.scss} (100%) rename src/skins/vector/css/vector-web/structures/{RoomDirectory.css => _RoomDirectory.scss} (100%) rename src/skins/vector/css/vector-web/structures/{RoomSubList.css => _RoomSubList.scss} (100%) rename src/skins/vector/css/vector-web/structures/{ViewSource.css => _ViewSource.scss} (100%) rename src/skins/vector/css/vector-web/views/context_menus/{MessageContextMenu.css => _MessageContextMenu.scss} (100%) rename src/skins/vector/css/vector-web/views/context_menus/{NotificationStateContextMenu.css => _NotificationStateContextMenu.scss} (100%) rename src/skins/vector/css/vector-web/views/context_menus/{RoomTagContextMenu.css => _RoomTagContextMenu.scss} (100%) rename src/skins/vector/css/vector-web/views/dialogs/{ChangelogDialog.css => _ChangelogDialog.scss} (100%) rename src/skins/vector/css/vector-web/views/directory/{NetworkDropdown.css => _NetworkDropdown.scss} (100%) rename src/skins/vector/css/vector-web/views/elements/{ImageView.css => _ImageView.scss} (100%) rename src/skins/vector/css/vector-web/views/elements/{Spinner.css => _Spinner.scss} (100%) rename src/skins/vector/css/vector-web/views/globals/{GuestWarningBar.css => _GuestWarningBar.scss} (100%) rename src/skins/vector/css/vector-web/views/globals/{MatrixToolbar.css => _MatrixToolbar.scss} (100%) rename src/skins/vector/css/vector-web/views/messages/{MessageTimestamp.css => _MessageTimestamp.scss} (100%) rename src/skins/vector/css/vector-web/views/messages/{SenderProfile.css => _SenderProfile.scss} (100%) rename src/skins/vector/css/vector-web/views/rooms/{RoomDropTarget.css => _RoomDropTarget.scss} (100%) rename src/skins/vector/css/vector-web/views/rooms/{RoomTooltip.css => _RoomTooltip.scss} (100%) rename src/skins/vector/css/vector-web/views/rooms/{SearchBar.css => _SearchBar.scss} (100%) rename src/skins/vector/css/vector-web/views/settings/{Notifications.css => _Notifications.scss} (100%) diff --git a/package.json b/package.json index 3f9f46e2..c657aad6 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "build:config": "cpx config.json webapp/", "build:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", - "build:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css --no-watch", + "build:css": "mkdirp build && node-sass --recursive --source-map true --output build \"src/skins/vector/css\"", "build:compile": "babel --source-maps -d lib src", "build:bundle": "NODE_ENV=production webpack -p --progress", "build:bundle:dev": "webpack --optimize-occurence-order --progress", @@ -44,7 +44,7 @@ "start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/ -w", "start:js": "webpack-dev-server -w --progress", "start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress", - "start:skins:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css", + "start:skins:css": "mkdirp build && node-sass --recursive --watch --source-map true --output build \"src/skins/vector/css\"", "start": "node scripts/babelcheck.js && parallelshell \"npm run start:emojione\" \"npm run start:res\" \"npm run start:config\" \"npm run start:js\" \"npm run start:skins:css\"", "start:prod": "parallelshell \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\"", "clean": "rimraf build lib webapp electron/dist", @@ -113,11 +113,13 @@ "karma-webpack": "^1.7.0", "mkdirp": "^0.5.1", "mocha": "^2.4.5", + "node-sass": "^4.1.1", "parallelshell": "^1.2.0", "phantomjs-prebuilt": "^2.1.7", "react-addons-perf": "^15.4.0", "react-addons-test-utils": "^15.4.0", "rimraf": "^2.4.3", + "sass-loader": "^4.1.1", "source-map-loader": "^0.1.5", "webpack": "^1.12.14", "webpack-dev-server": "^1.16.2" diff --git a/src/skins/vector/css/common.css b/src/skins/vector/css/_common.scss similarity index 100% rename from src/skins/vector/css/common.css rename to src/skins/vector/css/_common.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/ContextualMenu.css b/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/ContextualMenu.css rename to src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/CreateRoom.css b/src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/CreateRoom.css rename to src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/FilePanel.css b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/FilePanel.css rename to src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/MatrixChat.css b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/MatrixChat.css rename to src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/NotificationPanel.css b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/NotificationPanel.css rename to src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomStatusBar.css b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/RoomStatusBar.css rename to src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/RoomView.css rename to src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/SearchBox.css b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/SearchBox.css rename to src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/UploadBar.css b/src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/UploadBar.css rename to src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/UserSettings.css b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/UserSettings.css rename to src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss diff --git a/src/skins/vector/css/matrix-react-sdk/structures/login/Login.css b/src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/structures/login/Login.css rename to src/skins/vector/css/matrix-react-sdk/structures/login/_Login.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/avatars/BaseAvatar.css b/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/avatars/BaseAvatar.css rename to src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css rename to src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/EncryptedEventDialog.css b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/dialogs/EncryptedEventDialog.css rename to src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/SetDisplayNameDialog.css b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/dialogs/SetDisplayNameDialog.css rename to src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/AddressSelector.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/AddressSelector.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/AddressTile.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/AddressTile.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/MemberEventListSummary.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/MemberEventListSummary.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/ProgressBar.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/ProgressBar.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/RichText.css b/src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/elements/RichText.css rename to src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/login/ServerConfig.css b/src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/login/ServerConfig.css rename to src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/MImageBody.css b/src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/messages/MImageBody.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css b/src/skins/vector/css/matrix-react-sdk/views/messages/_MNoticeBody.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/messages/MNoticeBody.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/_MNoticeBody.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/MTextBody.css b/src/skins/vector/css/matrix-react-sdk/views/messages/_MTextBody.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/messages/MTextBody.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/_MTextBody.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css b/src/skins/vector/css/matrix-react-sdk/views/messages/_TextualEvent.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/messages/TextualEvent.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/_TextualEvent.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/UnknownBody.css b/src/skins/vector/css/matrix-react-sdk/views/messages/_UnknownBody.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/messages/UnknownBody.css rename to src/skins/vector/css/matrix-react-sdk/views/messages/_UnknownBody.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/Autocomplete.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/Autocomplete.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/EntityTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/EntityTile.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/LinkPreviewWidget.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/LinkPreviewWidget.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberDeviceInfo.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/MemberDeviceInfo.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberInfo.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/MemberInfo.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MemberList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/MemberList.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/MessageComposer.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/MessageComposer.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/PresenceLabel.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_PresenceLabel.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/PresenceLabel.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_PresenceLabel.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomHeader.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/RoomHeader.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/RoomList.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomList.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/RoomPreviewBar.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomSettings.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/RoomSettings.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/SearchableEntityList.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/SearchableEntityList.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/TabCompleteBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/TabCompleteBar.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/TopUnreadMessagesBar.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/rooms/TopUnreadMessagesBar.css rename to src/skins/vector/css/matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/settings/DevicesPanel.css b/src/skins/vector/css/matrix-react-sdk/views/settings/_DevicesPanel.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/settings/DevicesPanel.css rename to src/skins/vector/css/matrix-react-sdk/views/settings/_DevicesPanel.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/settings/IntegrationsManager.css b/src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/settings/IntegrationsManager.css rename to src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/CallView.css b/src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/voip/CallView.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/IncomingCallbox.css b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/voip/IncomingCallbox.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/VideoView.css b/src/skins/vector/css/matrix-react-sdk/views/voip/_VideoView.scss similarity index 100% rename from src/skins/vector/css/matrix-react-sdk/views/voip/VideoView.css rename to src/skins/vector/css/matrix-react-sdk/views/voip/_VideoView.scss diff --git a/src/skins/vector/css/themes/dark.scss b/src/skins/vector/css/themes/dark.scss new file mode 100644 index 00000000..19ab2cde --- /dev/null +++ b/src/skins/vector/css/themes/dark.scss @@ -0,0 +1,7 @@ +// typical text (dark-on-white in light skin) +$primary-fg-color: #dddddd; +$primary-bg-color: #2d2d2d; + +// button UI (white-on-green in light skin) + +@import "../_components" \ No newline at end of file diff --git a/src/skins/vector/css/themes/light.scss b/src/skins/vector/css/themes/light.scss new file mode 100644 index 00000000..e0ad84f6 --- /dev/null +++ b/src/skins/vector/css/themes/light.scss @@ -0,0 +1,4 @@ +$primary-fg-color: #454545; +$primary-bg-color: #ffffff; + +@import "../_components" \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/fonts.css b/src/skins/vector/css/vector-web/_fonts.scss similarity index 100% rename from src/skins/vector/css/vector-web/fonts.css rename to src/skins/vector/css/vector-web/_fonts.scss diff --git a/src/skins/vector/css/vector-web/structures/CompatibilityPage.css b/src/skins/vector/css/vector-web/structures/_CompatibilityPage.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/CompatibilityPage.css rename to src/skins/vector/css/vector-web/structures/_CompatibilityPage.scss diff --git a/src/skins/vector/css/vector-web/structures/LeftPanel.css b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/LeftPanel.css rename to src/skins/vector/css/vector-web/structures/_LeftPanel.scss diff --git a/src/skins/vector/css/vector-web/structures/RightPanel.css b/src/skins/vector/css/vector-web/structures/_RightPanel.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/RightPanel.css rename to src/skins/vector/css/vector-web/structures/_RightPanel.scss diff --git a/src/skins/vector/css/vector-web/structures/RoomDirectory.css b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/RoomDirectory.css rename to src/skins/vector/css/vector-web/structures/_RoomDirectory.scss diff --git a/src/skins/vector/css/vector-web/structures/RoomSubList.css b/src/skins/vector/css/vector-web/structures/_RoomSubList.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/RoomSubList.css rename to src/skins/vector/css/vector-web/structures/_RoomSubList.scss diff --git a/src/skins/vector/css/vector-web/structures/ViewSource.css b/src/skins/vector/css/vector-web/structures/_ViewSource.scss similarity index 100% rename from src/skins/vector/css/vector-web/structures/ViewSource.css rename to src/skins/vector/css/vector-web/structures/_ViewSource.scss diff --git a/src/skins/vector/css/vector-web/views/context_menus/MessageContextMenu.css b/src/skins/vector/css/vector-web/views/context_menus/_MessageContextMenu.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/context_menus/MessageContextMenu.css rename to src/skins/vector/css/vector-web/views/context_menus/_MessageContextMenu.scss diff --git a/src/skins/vector/css/vector-web/views/context_menus/NotificationStateContextMenu.css b/src/skins/vector/css/vector-web/views/context_menus/_NotificationStateContextMenu.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/context_menus/NotificationStateContextMenu.css rename to src/skins/vector/css/vector-web/views/context_menus/_NotificationStateContextMenu.scss diff --git a/src/skins/vector/css/vector-web/views/context_menus/RoomTagContextMenu.css b/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/context_menus/RoomTagContextMenu.css rename to src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss diff --git a/src/skins/vector/css/vector-web/views/dialogs/ChangelogDialog.css b/src/skins/vector/css/vector-web/views/dialogs/_ChangelogDialog.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/dialogs/ChangelogDialog.css rename to src/skins/vector/css/vector-web/views/dialogs/_ChangelogDialog.scss diff --git a/src/skins/vector/css/vector-web/views/directory/NetworkDropdown.css b/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/directory/NetworkDropdown.css rename to src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss diff --git a/src/skins/vector/css/vector-web/views/elements/ImageView.css b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/elements/ImageView.css rename to src/skins/vector/css/vector-web/views/elements/_ImageView.scss diff --git a/src/skins/vector/css/vector-web/views/elements/Spinner.css b/src/skins/vector/css/vector-web/views/elements/_Spinner.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/elements/Spinner.css rename to src/skins/vector/css/vector-web/views/elements/_Spinner.scss diff --git a/src/skins/vector/css/vector-web/views/globals/GuestWarningBar.css b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/globals/GuestWarningBar.css rename to src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss diff --git a/src/skins/vector/css/vector-web/views/globals/MatrixToolbar.css b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/globals/MatrixToolbar.css rename to src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss diff --git a/src/skins/vector/css/vector-web/views/messages/MessageTimestamp.css b/src/skins/vector/css/vector-web/views/messages/_MessageTimestamp.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/messages/MessageTimestamp.css rename to src/skins/vector/css/vector-web/views/messages/_MessageTimestamp.scss diff --git a/src/skins/vector/css/vector-web/views/messages/SenderProfile.css b/src/skins/vector/css/vector-web/views/messages/_SenderProfile.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/messages/SenderProfile.css rename to src/skins/vector/css/vector-web/views/messages/_SenderProfile.scss diff --git a/src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/rooms/RoomDropTarget.css rename to src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss diff --git a/src/skins/vector/css/vector-web/views/rooms/RoomTooltip.css b/src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/rooms/RoomTooltip.css rename to src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss diff --git a/src/skins/vector/css/vector-web/views/rooms/SearchBar.css b/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/rooms/SearchBar.css rename to src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss diff --git a/src/skins/vector/css/vector-web/views/settings/Notifications.css b/src/skins/vector/css/vector-web/views/settings/_Notifications.scss similarity index 100% rename from src/skins/vector/css/vector-web/views/settings/Notifications.css rename to src/skins/vector/css/vector-web/views/settings/_Notifications.scss diff --git a/src/vector/index.js b/src/vector/index.js index 8231950b..96c6a971 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -30,7 +30,7 @@ require('babel-polyfill'); // CSS requires: just putting them here for now as CSS is going to be // refactored "soon" anyway -require('../../build/components.css'); +require('../../build/themes/light.css'); require('gemini-scrollbar/gemini-scrollbar.css'); require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); diff --git a/webpack.config.js b/webpack.config.js index 3500fedc..1c408b70 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -25,6 +25,7 @@ module.exports = { { test: /\.js$/, loader: "babel", include: path.resolve('./src') }, // css-raw-loader loads CSS but doesn't try to treat url()s as require()s { test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, + { test: /\.scss$/, loaders: ["style-loader", "css-loader?sourceMap", "sass-loader?sourceMap"] }, ], noParse: [ // don't parse the languages within highlight.js. They cause stack From c5fa84cd28e7e0dcd389d98fd307728e0e2d896e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 28 Dec 2016 02:02:05 +0000 Subject: [PATCH 0020/1951] autogenerate the _components.scss index --- src/skins/vector/css/rethemendex.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 src/skins/vector/css/rethemendex.sh diff --git a/src/skins/vector/css/rethemendex.sh b/src/skins/vector/css/rethemendex.sh new file mode 100755 index 00000000..27c47224 --- /dev/null +++ b/src/skins/vector/css/rethemendex.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo "// autogenerated by rethemendex.sh" > _components.scss + +for i in `find */* -iname _\*.scss`; +do + echo "@import \"`dirname $i`/`basename $i .scss`\";" >> _components.scss +done From 33f0eaada0b6348305ab86b4f6557ae344dc42c2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 28 Dec 2016 02:02:23 +0000 Subject: [PATCH 0021/1951] autogen _components.css --- src/skins/vector/css/_components.scss | 72 +++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 src/skins/vector/css/_components.scss diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss new file mode 100644 index 00000000..1106f249 --- /dev/null +++ b/src/skins/vector/css/_components.scss @@ -0,0 +1,72 @@ +// autogenerated by rethemendex.sh +@import "matrix-react-sdk/structures/_ContextualMenu"; +@import "matrix-react-sdk/structures/_CreateRoom"; +@import "matrix-react-sdk/structures/_FilePanel"; +@import "matrix-react-sdk/structures/_MatrixChat"; +@import "matrix-react-sdk/structures/_NotificationPanel"; +@import "matrix-react-sdk/structures/_RoomStatusBar"; +@import "matrix-react-sdk/structures/_RoomView"; +@import "matrix-react-sdk/structures/_SearchBox"; +@import "matrix-react-sdk/structures/_UploadBar"; +@import "matrix-react-sdk/structures/_UserSettings"; +@import "matrix-react-sdk/structures/login/_Login"; +@import "matrix-react-sdk/views/avatars/_BaseAvatar"; +@import "matrix-react-sdk/views/dialogs/_ChatInviteDialog"; +@import "matrix-react-sdk/views/dialogs/_EncryptedEventDialog"; +@import "matrix-react-sdk/views/dialogs/_SetDisplayNameDialog"; +@import "matrix-react-sdk/views/elements/_AddressSelector"; +@import "matrix-react-sdk/views/elements/_AddressTile"; +@import "matrix-react-sdk/views/elements/_DirectorySearchBox"; +@import "matrix-react-sdk/views/elements/_MemberEventListSummary"; +@import "matrix-react-sdk/views/elements/_ProgressBar"; +@import "matrix-react-sdk/views/elements/_RichText"; +@import "matrix-react-sdk/views/login/_ServerConfig"; +@import "matrix-react-sdk/views/messages/_MImageBody"; +@import "matrix-react-sdk/views/messages/_MNoticeBody"; +@import "matrix-react-sdk/views/messages/_MTextBody"; +@import "matrix-react-sdk/views/messages/_TextualEvent"; +@import "matrix-react-sdk/views/messages/_UnknownBody"; +@import "matrix-react-sdk/views/rooms/_Autocomplete"; +@import "matrix-react-sdk/views/rooms/_EntityTile"; +@import "matrix-react-sdk/views/rooms/_EventTile"; +@import "matrix-react-sdk/views/rooms/_LinkPreviewWidget"; +@import "matrix-react-sdk/views/rooms/_MemberDeviceInfo"; +@import "matrix-react-sdk/views/rooms/_MemberInfo"; +@import "matrix-react-sdk/views/rooms/_MemberList"; +@import "matrix-react-sdk/views/rooms/_MessageComposer"; +@import "matrix-react-sdk/views/rooms/_PresenceLabel"; +@import "matrix-react-sdk/views/rooms/_RoomHeader"; +@import "matrix-react-sdk/views/rooms/_RoomList"; +@import "matrix-react-sdk/views/rooms/_RoomPreviewBar"; +@import "matrix-react-sdk/views/rooms/_RoomSettings"; +@import "matrix-react-sdk/views/rooms/_RoomTile"; +@import "matrix-react-sdk/views/rooms/_SearchableEntityList"; +@import "matrix-react-sdk/views/rooms/_TabCompleteBar"; +@import "matrix-react-sdk/views/rooms/_TopUnreadMessagesBar"; +@import "matrix-react-sdk/views/settings/_DevicesPanel"; +@import "matrix-react-sdk/views/settings/_IntegrationsManager"; +@import "matrix-react-sdk/views/voip/_CallView"; +@import "matrix-react-sdk/views/voip/_IncomingCallbox"; +@import "matrix-react-sdk/views/voip/_VideoView"; +@import "vector-web/_fonts"; +@import "vector-web/structures/_CompatibilityPage"; +@import "vector-web/structures/_LeftPanel"; +@import "vector-web/structures/_RightPanel"; +@import "vector-web/structures/_RoomDirectory"; +@import "vector-web/structures/_RoomSubList"; +@import "vector-web/structures/_ViewSource"; +@import "vector-web/views/context_menus/_MessageContextMenu"; +@import "vector-web/views/context_menus/_NotificationStateContextMenu"; +@import "vector-web/views/context_menus/_RoomTagContextMenu"; +@import "vector-web/views/dialogs/_ChangelogDialog"; +@import "vector-web/views/directory/_NetworkDropdown"; +@import "vector-web/views/elements/_ImageView"; +@import "vector-web/views/elements/_Spinner"; +@import "vector-web/views/globals/_GuestWarningBar"; +@import "vector-web/views/globals/_MatrixToolbar"; +@import "vector-web/views/messages/_MessageTimestamp"; +@import "vector-web/views/messages/_SenderProfile"; +@import "vector-web/views/rooms/_RoomDropTarget"; +@import "vector-web/views/rooms/_RoomTooltip"; +@import "vector-web/views/rooms/_SearchBar"; +@import "vector-web/views/settings/_Notifications"; From c6beaa16318d005f0548f19f2dc956e17c60f1de Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 28 Dec 2016 04:04:43 +0000 Subject: [PATCH 0022/1951] initial stab at an official dark theme via the magic of SASS. SVGs are all broken, and some of the more exotic colours haven't been updated. There's been no attempt to use SASS to remove duplication from the CSS yet. no attempt to switch it at runtime yet. --- src/skins/vector/css/_common.scss | 51 +++++++------- src/skins/vector/css/_components.scss | 1 + .../structures/_ContextualMenu.scss | 14 ++-- .../structures/_CreateRoom.scss | 4 +- .../structures/_FilePanel.scss | 10 +-- .../structures/_MatrixChat.scss | 4 +- .../structures/_NotificationPanel.scss | 7 +- .../structures/_RoomStatusBar.scss | 16 ++--- .../structures/_RoomView.scss | 24 +++---- .../structures/_UploadBar.scss | 6 +- .../structures/_UserSettings.scss | 6 +- .../structures/login/_Login.scss | 14 ++-- .../views/avatars/_BaseAvatar.scss | 2 +- .../views/dialogs/_ChatInviteDialog.scss | 4 +- .../views/dialogs/_EncryptedEventDialog.scss | 6 +- .../views/dialogs/_SetDisplayNameDialog.scss | 6 +- .../views/elements/_AddressSelector.scss | 16 ++--- .../views/elements/_AddressTile.scss | 8 +-- .../views/elements/_DirectorySearchBox.scss | 2 +- .../elements/_MemberEventListSummary.scss | 2 +- .../views/elements/_RichText.scss | 6 +- .../views/login/_ServerConfig.scss | 2 +- .../views/messages/_MImageBody.scss | 8 +-- .../views/rooms/_Autocomplete.scss | 12 ++-- .../views/rooms/_EntityTile.scss | 6 +- .../views/rooms/_EventTile.scss | 34 +++++----- .../views/rooms/_MemberDeviceInfo.scss | 14 ++-- .../views/rooms/_MemberInfo.scss | 4 +- .../views/rooms/_MemberList.scss | 10 +-- .../views/rooms/_MessageComposer.scss | 10 +-- .../views/rooms/_RoomHeader.scss | 16 ++--- .../views/rooms/_RoomPreviewBar.scss | 4 +- .../views/rooms/_RoomSettings.scss | 18 ++--- .../views/rooms/_RoomTile.scss | 14 ++-- .../views/rooms/_SearchableEntityList.scss | 10 +-- .../views/rooms/_TabCompleteBar.scss | 6 +- .../views/rooms/_TopUnreadMessagesBar.scss | 2 +- .../views/settings/_IntegrationsManager.scss | 2 +- .../views/voip/_CallView.scss | 4 +- .../views/voip/_IncomingCallbox.scss | 4 +- src/skins/vector/css/rethemendex.sh | 2 +- src/skins/vector/css/themes/_base.scss | 68 +++++++++++++++++++ src/skins/vector/css/themes/dark.scss | 66 +++++++++++++++++- src/skins/vector/css/themes/light.scss | 6 +- .../vector-web/structures/_RightPanel.scss | 10 +-- .../vector-web/structures/_RoomDirectory.scss | 6 +- .../vector-web/structures/_RoomSubList.scss | 36 +++++----- .../context_menus/_RoomTagContextMenu.scss | 2 +- .../views/directory/_NetworkDropdown.scss | 6 +- .../vector-web/views/elements/_ImageView.scss | 8 +-- .../views/globals/_GuestWarningBar.scss | 6 +- .../views/globals/_MatrixToolbar.scss | 6 +- .../views/rooms/_RoomDropTarget.scss | 6 +- .../vector-web/views/rooms/_RoomTooltip.scss | 12 ++-- .../vector-web/views/rooms/_SearchBar.scss | 14 ++-- .../views/settings/_Notifications.scss | 2 +- src/vector/index.js | 2 +- 57 files changed, 388 insertions(+), 259 deletions(-) create mode 100644 src/skins/vector/css/themes/_base.scss diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index bb00bbd8..e81ea2fa 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -29,7 +29,8 @@ body { Arial here. */ font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; font-size: 15px; - color: #454545; + background-color: $primary-bg-color; + color: $primary-fg-color; border: 0px; margin: 0px; /* This should render the fonts the same accross browsers */ @@ -41,7 +42,7 @@ div.error { } h2 { - color: #454545; + color: $primary-fg-color; font-weight: 400; font-size: 18px; margin-top: 16px; @@ -51,7 +52,7 @@ h2 { a:hover, a:link, a:visited { - color: #76cfa6; + color: $accent-color; } input[type=text].error, input[type=password].error { @@ -59,7 +60,7 @@ input[type=text].error, input[type=password].error { } input[type=text]:focus, textarea:focus { - border: 1px solid #76CFA6; + border: 1px solid $accent-color; outline: none; box-shadow: none; } @@ -148,8 +149,8 @@ textarea { } .mx_Dialog { - background-color: #fff; - color: #747474; + background-color: $primary-bg-color; + color: $light-fg-color; z-index: 4010; font-weight: 300; font-size: 15px; @@ -190,7 +191,7 @@ textarea { .mx_Dialog_content { margin: 24px 58px 68px 0; font-size: 14px; - color: #4a4a4a; + color: $primary-fg-color; word-wrap: break-word; } @@ -202,7 +203,7 @@ textarea { border: 0px; height: 36px; border-radius: 40px; - border: solid 1px #76cfa6; + border: solid 1px $accent-color; font-weight: 600; font-size: 14px; font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; @@ -212,26 +213,26 @@ textarea { padding-right: 1.5em; outline: none; cursor: pointer; - color: #76cfa6; - background-color: #fff; + color: $accent-color; + background-color: $primary-bg-color; /* align images in buttons (eg spinners) */ vertical-align: middle; } .mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary { - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; } .mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger { - background-color: #ff0064; - border: solid 1px #ff0064; + background-color: $warning-color; + border: solid 1px $warning-color; } .mx_Dialog button:disabled, .mx_Dialog input[type="submit"]:disabled { - background-color: #777777; - border: solid 1px #777777; + background-color: $light-fg-color; + border: solid 1px $light-fg-color; opacity: 0.7; } @@ -241,11 +242,11 @@ textarea { font-weight: bold; font-size: 22px; line-height: 1.4; - color: #454545; + color: $primary-fg-color; } .mx_Dialog_title.danger { - color: #ff0064; + color: $warning-color; } .mx_TextInputDialog_label { @@ -256,10 +257,10 @@ textarea { .mx_TextInputDialog_input { font-size: 15px; border-radius: 3px; - border: 1px solid #f0f0f0; + border: 1px solid $input-border-color; padding: 9px; - color: #454545; - background-color: #fff; + color: $primary-fg-color; + background-color: $primary-bg-color; } .mx_emojione { @@ -268,19 +269,19 @@ textarea { } ::-moz-selection { - background-color: #76CFA6; + background-color: $accent-color; color: white; } ::selection { - background-color: #76CFA6; + background-color: $accent-color; color: white; } /** green button with rounded corners */ .mx_textButton { - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; border-radius: 17px; text-align: center; padding-left: 1em; diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 1106f249..532e0a36 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -1,4 +1,5 @@ // autogenerated by rethemendex.sh +@import "_common"; @import "matrix-react-sdk/structures/_ContextualMenu"; @import "matrix-react-sdk/structures/_CreateRoom"; @import "matrix-react-sdk/structures/_FilePanel"; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss b/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss index d317363d..d3e73a9a 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss @@ -30,10 +30,10 @@ limitations under the License. } .mx_ContextualMenu { - border: solid 1px rgba(187, 187, 187, 0.5); + border: solid 1px $menu-border-color; border-radius: 4px; - background-color: #f6f6f6; - color: #4a4a4a; + background-color: $menu-bg-color; + color: $primary-fg-color; position: absolute; padding: 6px; font-size: 14px; @@ -51,7 +51,7 @@ limitations under the License. width: 0; height: 0; border-top: 8px solid transparent; - border-left: 8px solid rgba(187, 187, 187, 0.5); + border-left: 8px solid $menu-border-color; border-bottom: 8px solid transparent; } @@ -60,7 +60,7 @@ limitations under the License. width: 0; height: 0; border-top: 7px solid transparent; - border-left: 7px solid #f6f6f6; + border-left: 7px solid $menu-bg-color; border-bottom: 7px solid transparent; position:absolute; top: -7px; @@ -78,7 +78,7 @@ limitations under the License. width: 0; height: 0; border-top: 8px solid transparent; - border-right: 8px solid rgba(187, 187, 187, 0.5); + border-right: 8px solid $menu-border-color; border-bottom: 8px solid transparent; } @@ -87,7 +87,7 @@ limitations under the License. width: 0; height: 0; border-top: 7px solid transparent; - border-right: 7px solid #f6f6f6; + border-right: 7px solid $menu-bg-color; border-bottom: 7px solid transparent; position:absolute; top: -7px; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss b/src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss index 88804409..2be19352 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_CreateRoom.scss @@ -18,13 +18,13 @@ limitations under the License. width: 960px; margin-left: auto; margin-right: auto; - color: #4a4a4a; + color: $primary-fg-color; } .mx_CreateRoom input, .mx_CreateRoom textarea { border-radius: 3px; - border: 1px solid #c7c7c7; + border: 1px solid $strong-input-border-color; font-weight: 300; font-size: 13px; padding: 9px; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss index fca53b1d..2f4a00ad 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss @@ -58,12 +58,12 @@ limitations under the License. .mx_FilePanel .mx_EventTile .mx_MImageBody_download { display: flex; font-size: 14px; - color: #acacac; + color: $event-timestamp-color; } .mx_FilePanel .mx_EventTile .mx_MImageBody_downloadLink { flex: 1 1 auto; - color: #747474; + color: $light-fg-color; } .mx_FilePanel .mx_EventTile .mx_MImageBody_size { @@ -90,7 +90,7 @@ limitations under the License. padding: 0px; font-size: 11px; opacity: 1.0; - color: #acacac; + color: $event-timestamp-color; } .mx_FilePanel .mx_EventTile .mx_MessageTimestamp { @@ -100,7 +100,7 @@ limitations under the License. position: initial; font-size: 11px; opacity: 1.0; - color: #acacac; + color: $event-timestamp-color; } /* Overrides for the wrappers around the body tile */ @@ -111,7 +111,7 @@ limitations under the License. } .mx_FilePanel .mx_EventTile:hover .mx_EventTile_line { - background-color: #fff; + background-color: $primary-bg-color; } .mx_FilePanel .mx_EventTile_selected .mx_EventTile_line { diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss index f1cc7d4a..5587a609 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss @@ -91,7 +91,7 @@ limitations under the License. -webkit-order: 1; order: 1; - background-color: #eaf5f0; + background-color: $secondary-accent-color; -webkit-flex: 0 0 235px; flex: 0 0 235px; @@ -111,7 +111,7 @@ limitations under the License. padding-left: 20px; padding-right: 22px; - background-color: #fff; + background-color: $primary-bg-color; -webkit-flex: 1; flex: 1; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss index 83b0a033..06dd92f3 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss @@ -51,7 +51,7 @@ limitations under the License. } .mx_NotificationPanel .mx_EventTile_roomName a { - color: #4a4a4a; + color: $primary-fg-color; } .mx_NotificationPanel .mx_EventTile_avatar { @@ -61,8 +61,7 @@ limitations under the License. .mx_NotificationPanel .mx_EventTile .mx_SenderProfile, .mx_NotificationPanel .mx_EventTile .mx_MessageTimestamp { - color: #000; - opacity: 0.3; + color: $primary-fg-color; font-size: 12px; display: inline; padding-left: 0px; @@ -94,7 +93,7 @@ limitations under the License. } .mx_NotificationPanel .mx_EventTile:hover .mx_EventTile_line { - background-color: #fff; + background-color: $primary-bg-color; } .mx_NotificationPanel .mx_EventTile_selected .mx_EventTile_line { diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index ef0b69c4..d0ac5a60 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -36,7 +36,7 @@ limitations under the License. } .mx_RoomStatusBar_placeholderIndicator span { - color: #4a4a4a; + color: $primary-fg-color; opacity: 0.5; position: relative; top: -4px; @@ -76,7 +76,7 @@ limitations under the License. .mx_RoomStatusBar_unreadMessagesBar { padding-top: 10px; - color: #ff0064; + color: $warning-color; cursor: pointer; } @@ -93,29 +93,29 @@ limitations under the License. } .mx_RoomStatusBar_connectionLostBar_title { - color: #ff0064; + color: $warning-color; } .mx_RoomStatusBar_connectionLostBar_desc { - color: #454545; + color: $primary-fg-color; font-size: 13px; opacity: 0.5; } .mx_RoomStatusBar_resend_link { - color: #454545 ! important; + color: $primary-fg-color ! important; text-decoration: underline ! important; cursor: pointer; } .mx_RoomStatusBar_tabCompleteBar { padding-top: 10px; - color: #4a4a4a; + color: $primary-fg-color; } .mx_RoomStatusBar_typingBar { padding-top: 10px; - color: #4a4a4a; + color: $primary-fg-color; opacity: 0.5; overflow-y: hidden; display: block; @@ -135,7 +135,7 @@ limitations under the License. .mx_RoomStatusBar_tabCompleteEol { flex: 0 0 auto; -webkit-flex: 0 0 auto; - color: #76CFA6; + color: $accent-color; } .mx_RoomStatusBar_tabCompleteEol object { 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 c3f7ceed..182d690c 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -89,7 +89,7 @@ limitations under the License. margin: auto; overflow: auto; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; -webkit-flex: 0 0 auto; flex: 0 0 auto; @@ -158,7 +158,7 @@ limitations under the License. margin-bottom: 8px; margin-left: 63px; padding-bottom: 6px; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; } .mx_RoomView_invitePrompt { @@ -185,8 +185,8 @@ li.mx_RoomView_myReadMarker_container { } hr.mx_RoomView_myReadMarker { - border-top: solid 1px #76cfa6; - border-bottom: solid 1px #76cfa6; + border-top: solid 1px $accent-color; + border-bottom: solid 1px $accent-color; margin-top: 0px; position: relative; top: 5px; @@ -212,16 +212,16 @@ hr.mx_RoomView_myReadMarker { .mx_RoomView_statusAreaBox_line { margin-left: 65px; - border-top: 1px solid #e5e5e5; + border-top: 1px solid $primary-hairline-color; height: 1px; } .mx_RoomView_callStatusBar .mx_UploadBar_uploadProgressInner { - background-color: #fff; + background-color: $primary-bg-color; } .mx_RoomView_callStatusBar .mx_UploadBar_uploadFilename { - color: #fff; + color: $accent-fg-color; opacity: 1.0; } @@ -234,8 +234,8 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView_inCall .mx_RoomView_statusAreaBox { - background-color: #76CFA6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; position: relative; } @@ -272,13 +272,13 @@ hr.mx_RoomView_myReadMarker { .mx_RoomView_ongoingConfCallNotification { width: 100%; text-align: center; - background-color: #ff0064; - color: #fff; + background-color: $warning-color; + color: $accent-fg-color; font-weight: bold; padding: 6px 0; cursor: pointer; } .mx_RoomView_ongoingConfCallNotification a { - color: #fff ! important; + color: $accent-fg-color ! important; } \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss index b489e132..d76c8166 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UploadBar.scss @@ -26,7 +26,7 @@ limitations under the License. } .mx_UploadBar_uploadProgressInner { - background-color: #76cfa6; + background-color: $accent-color; height: 5px; } @@ -34,7 +34,7 @@ limitations under the License. margin-top: 5px; margin-left: 65px; opacity: 0.5; - color: #4a4a4a; + color: $primary-fg-color; } .mx_UploadBar_uploadIcon { @@ -57,5 +57,5 @@ limitations under the License. float: right; margin-top: 5px; margin-right: 30px; - color: #76cfa6; + color: $accent-color; } 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 427369a5..6ba1002b 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -84,8 +84,8 @@ limitations under the License. border-radius: 36px; font-weight: 400; font-size: 16px; - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; width: auto; margin: auto; padding: 7px; @@ -95,7 +95,7 @@ limitations under the License. } .mx_UserSettings_button.danger { - background-color: #ff0064; + background-color: $warning-color; } .mx_UserSettings_section { 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 0f610b25..5f4164e8 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 @@ -32,7 +32,7 @@ limitations under the License. } .mx_Login h2 { - color: #4a4a4a; + color: $primary-fg-color; font-weight: 300; margin-top: 32px; margin-bottom: 20px; @@ -53,7 +53,7 @@ limitations under the License. .mx_Login_field { width: 100%; border-radius: 3px; - border: 1px solid #c7c7c7; + border: 1px solid $strong-input-border-color; font-weight: 300; font-size: 13px; padding: 9px; @@ -75,9 +75,9 @@ limitations under the License. border-radius: 40px; height: 40px; border: 0px; - background-color: #76cfa6; + background-color: $accent-color; font-size: 15px; - color: #fff; + color: $accent-fg-color; } .mx_Login_label { @@ -99,7 +99,7 @@ limitations under the License. } .mx_Login_create:link { - color: #4a4a4a; + color: $primary-fg-color; } .mx_Login_links { @@ -112,7 +112,7 @@ limitations under the License. } .mx_Login_links a:link { - color: #4a4a4a; + color: $primary-fg-color; } .mx_Login_prompt { @@ -127,7 +127,7 @@ limitations under the License. } .mx_Login_forgot:link { - color: #4a4a4a; + color: $primary-fg-color; } .mx_Login_loader { diff --git a/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss b/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss index 901a2959..d481e5c1 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss @@ -21,7 +21,7 @@ limitations under the License. .mx_BaseAvatar_initial { position: absolute; z-index: 1; - color: #fff; + color: $avatar-initial-color; text-align: center; speak: none; pointer-events: none; diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss index aa1dced8..6bf9adce 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss @@ -36,7 +36,7 @@ limitations under the License. .mx_ChatInviteDialog_inputContainer { border-radius: 3px; - border: solid 1px #f0f0f0; + border: solid 1px $input-border-color; line-height: 36px; padding-left: 4px; padding-right: 4px; @@ -49,7 +49,7 @@ limitations under the License. .mx_ChatInviteDialog_error { position: absolute; - color: #ff0064; + color: $warning-color; line-height: 36px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss index 5e72d7f8..cbc0195c 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss @@ -24,7 +24,7 @@ limitations under the License. border: 0px; height: 36px; border-radius: 40px; - border: solid 1px #76cfa6; + border: solid 1px $accent-color; font-weight: 600; font-size: 14px; font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; @@ -34,6 +34,6 @@ limitations under the License. padding-right: 1.5em; outline: none; cursor: pointer; - color: #76cfa6; - background-color: #fff; + color: $accent-color; + background-color: $primary-bg-color; } \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss index 68409a80..2f0750ad 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss @@ -16,9 +16,9 @@ limitations under the License. .mx_SetDisplayNameDialog_input { border-radius: 3px; - border: 1px solid #f0f0f0; + border: 1px solid $input-border-color; padding: 9px; - color: #454545; - background-color: #fff; + color: $primary-fg-color; + background-color: $primary-bg-color; font-size: 15px; } \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss index aa0f8c6a..9871a7e8 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressSelector.scss @@ -16,13 +16,13 @@ limitations under the License. .mx_AddressSelector { position: absolute; - background-color: #fff; + background-color: $primary-bg-color; width: 485px; max-height: 116px; overflow-y: auto; border-radius: 3px; - background-color: #fff; - border: solid 1px #76cfa6; + background-color: $primary-bg-color; + border: solid 1px $accent-color; cursor: pointer; } @@ -31,15 +31,15 @@ limitations under the License. } .mx_AddressSelector_addressListElement .mx_AddressTile { - background-color: #fff; - border: solid 1px #fff; + background-color: $primary-bg-color; + border: solid 1px $primary-bg-color; } .mx_AddressSelector_addressListElement.mx_AddressSelector_selected { - background-color: #eaf5f0; /* selected colour */ + background-color: $selected-color; } .mx_AddressSelector_addressListElement.mx_AddressSelector_selected .mx_AddressTile { - background-color: #eaf5f0; /* selected colour */ - border: solid 1px #eaf5f0; /* selected colour */ + background-color: $selected-color; + border: solid 1px $selected-color; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss index 76c0e603..59c0b68d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_AddressTile.scss @@ -18,9 +18,9 @@ limitations under the License. display: inline-block; border-radius: 3px; background-color: rgba(74, 73, 74, 0.1); - border: solid 1px #f0f0f0; + border: solid 1px $input-border-color; line-height: 26px; - color: #454545; + color: $primary-fg-color; font-size: 14px; font-weight: normal; margin-right: 4px; @@ -28,8 +28,8 @@ limitations under the License. .mx_AddressTile.mx_AddressTile_error { background-color: rgba(255, 0, 100, 0.1); - color: #ff0064; - border-color: #ff0064; + color: $warning-color; + border-color: $warning-color; } .mx_AddressTile_network { diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss index 3a0922bc..dd953ab9 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_DirectorySearchBox { position: relative; border-radius: 3px; - border: 1px solid #c7c7c7; + border: 1px solid $strong-input-border-color; } .mx_DirectorySearchBox_container { diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss index b8197805..7d1b059b 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_MemberEventListSummary.scss @@ -31,6 +31,6 @@ limitations under the License. } .mx_MemberEventListSummary_toggle { - color:#76cfa6; + color:$accent-color; cursor:pointer; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss index f0b3c046..72a4c0b7 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_RichText.scss @@ -1,14 +1,14 @@ .mx_UserPill { color: white; - background-color: #76cfa6; + background-color: $accent-color; padding: 2px 8px; border-radius: 16px; } .mx_RoomPill { background-color: white; - color: #76cfa6; - border: 1px solid #76cfa6; + color: $accent-color; + border: 1px solid $accent-color; padding: 2px 8px; border-radius: 16px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss b/src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss index 75cd4170..1ad195de 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/login/_ServerConfig.scss @@ -27,5 +27,5 @@ limitations under the License. opacity: 0.8; font-size: 13px; font-weight: 300; - color: #4a4a4a; + color: $primary-fg-color; } \ No newline at end of file diff --git a/src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss b/src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss index 003ef103..83ae0616 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/messages/_MImageBody.scss @@ -22,18 +22,18 @@ limitations under the License. .mx_MImageBody_thumbnail { max-width: 100%; /* - background-color: #fff; - border: 2px solid #fff; + background-color: $primary-bg-color; + border: 2px solid $primary-bg-color; border-radius: 1px; */ } .mx_MImageBody_download { - color: #76cfa6; + color: $accent-color; } .mx_MImageBody_download a { - color: #76cfa6; + color: $accent-color; text-decoration: none; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss index 6d611b5e..9265d4dc 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss @@ -3,7 +3,7 @@ bottom: 0; z-index: 1000; width: 100%; - border: 1px solid #e5e5e5; + border: 1px solid $primary-hairline-color; background: white; border-bottom: none; border-radius: 4px 4px 0 0; @@ -12,7 +12,7 @@ } .mx_Autocomplete_ProviderSection { - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; } .mx_Autocomplete_Completion_container_pill { @@ -28,7 +28,7 @@ user-select: none; cursor: pointer; align-items: center; - color: #4a4a4a; + color: $primary-fg-color; } .mx_Autocomplete_Completion_block * { @@ -42,7 +42,7 @@ user-select: none; cursor: pointer; align-items: center; - color: #4a4a4a; + color: $primary-fg-color; } .mx_Autocomplete_Completion_pill * { @@ -57,13 +57,13 @@ } .mx_Autocomplete_Completion.selected { - background: #f6f6f6; + background: $menu-bg-color; outline: none; } .mx_Autocomplete_provider_name { margin: 12px; - color: #454545; + color: $primary-fg-color; font-weight: 400; opacity: 0.4; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss index e52ece77..2511f07d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss @@ -17,7 +17,7 @@ limitations under the License. .mx_EntityTile { display: table-row; position: relative; - color: #454545; + color: $primary-fg-color; cursor: pointer; } @@ -77,12 +77,12 @@ limitations under the License. .mx_EntityTile_ellipsis .mx_EntityTile_name { font-style: italic; - font-color: #454545; + font-color: $primary-fg-color; } .mx_EntityTile_invitePlaceholder .mx_EntityTile_name { font-style: italic; - font-color: #454545; + font-color: $primary-fg-color; } .mx_EntityTile_unavailable .mx_EntityTile_avatar, diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss index fd3f486b..a6b10e24 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss @@ -44,7 +44,7 @@ limitations under the License. } .mx_EventTile .mx_SenderProfile { - color: #454545; + color: $primary-fg-color; opacity: 0.5; font-size: 14px; display: block; /* anti-zalgo, with overflow hidden */ @@ -61,7 +61,7 @@ limitations under the License. display: block; visibility: hidden; white-space: nowrap; - color: #acacac; + color: $event-timestamp-color; font-size: 11px; left: 8px; position: absolute; @@ -93,20 +93,20 @@ limitations under the License. * TODO: ultimately we probably want some transition on here. */ .mx_EventTile_selected .mx_EventTile_line { - border-left: #76cfa6 5px solid; + border-left: $accent-color 5px solid; padding-left: 60px; - background-color: #f7f7f7; + background-color: $event-selected-color; } .mx_EventTile:hover .mx_EventTile_line, .mx_EventTile.menu .mx_EventTile_line { - background-color: #f7f7f7; + background-color: $event-selected-color; } .mx_EventTile_searchHighlight { - background-color: #76cfa6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; border-radius: 5px; padding-left: 2px; padding-right: 2px; @@ -114,26 +114,26 @@ limitations under the License. } .mx_EventTile_searchHighlight a { - background-color: #76cfa6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; } .mx_EventTile_encrypting { - color: #abddbc ! important; + color: $event-encrypting-color ! important; } .mx_EventTile_sending { - color: #ddd; + color: $event-sending-color; } .mx_EventTile_notSent { - color: #f44; + color: $event-notsent-color; } .mx_EventTile_highlight, .mx_EventTile_highlight .markdown-body { - color: #FF0064; + color: $warning-color; } .mx_EventTile_contextual { @@ -204,7 +204,7 @@ limitations under the License. } .mx_EventTile_readAvatarRemainder { - color: #acacac; + color: $event-timestamp-color; font-size: 11px; position: absolute; } @@ -244,10 +244,10 @@ limitations under the License. } .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line { - border-left: #76cfa5 5px solid; + border-left: $e2e-verified-color 5px solid; } .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line { - border-left: #e8bf37 5px solid; + border-left: $e2e-unverified-color 5px solid; } .mx_EventTile:hover.mx_EventTile_verified .mx_MessageTimestamp, @@ -297,7 +297,7 @@ limitations under the License. } .mx_EventTile_content .markdown-body a { - color: #76cfa6; + color: $accent-color; } .mx_EventTile_content .markdown-body .hljs { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss index 53379ac4..f1584d4e 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss @@ -20,8 +20,8 @@ limitations under the License. .mx_MemberDeviceInfo_textButton { - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; border-radius: 17px; text-align: center; padding-left: 1em; @@ -50,13 +50,13 @@ limitations under the License. } /* "Unblacklist" is too long for a regular button: make it wider and - reduce the padding. */ + reduce the padding. */ .mx_EncryptedEventDialog .mx_MemberDeviceInfo_blacklist, .mx_EncryptedEventDialog .mx_MemberDeviceInfo_unblacklist { width: 8em; padding-left: 1em; padding-right: 1em; -} +} .mx_MemberDeviceInfo div.mx_MemberDeviceInfo_verified, .mx_MemberDeviceInfo div.mx_MemberDeviceInfo_unverified, @@ -66,13 +66,13 @@ limitations under the License. } .mx_MemberDeviceInfo div.mx_MemberDeviceInfo_verified { - color: #76cfa5; + color: $e2e-verified-color; } .mx_MemberDeviceInfo div.mx_MemberDeviceInfo_unverified { - color: #e8bf37; + color: $e2e-unverified-color; } .mx_MemberDeviceInfo div.mx_MemberDeviceInfo_blacklisted { - color: #ba6363; + color: $e2e-warning-color; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss index 40b1524c..3b4b653e 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss @@ -72,7 +72,7 @@ limitations under the License. font-color: #999999; font-size: 13px; position: relative; - background-color: #fff; + background-color: $primary-bg-color; } .mx_MemberInfo_buttons { @@ -82,7 +82,7 @@ limitations under the License. .mx_MemberInfo_field { cursor: pointer; font-size: 13px; - color: #76cfa6; + color: $accent-color; margin-left: 8px; line-height: 23px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss index 7d383606..3c7e3ec5 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss @@ -55,10 +55,10 @@ limitations under the License. .mx_MemberList_query { font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; border-radius: 3px; - border: 1px solid #f0f0f0; + border: 1px solid $input-border-color; padding: 9px; - color: #454545; - background-color: #fff; + color: $primary-fg-color; + background-color: $primary-bg-color; margin-left: 3px; font-size: 14px; margin-bottom: 8px; @@ -66,13 +66,13 @@ limitations under the License. } .mx_MemberList_query::-moz-placeholder { - color: #454545; + color: $primary-fg-color; opacity: 0.5; font-size: 14px; } .mx_MemberList_query::-webkit-input-placeholder { - color: #454545; + color: $primary-fg-color; opacity: 0.5; font-size: 14px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss index 842f8db6..e5ffd220 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss @@ -18,7 +18,7 @@ limitations under the License. max-width: 960px; vertical-align: middle; margin: auto; - border-top: 1px solid #e5e5e5; + border-top: 1px solid $primary-hairline-color; position: relative; } @@ -108,8 +108,8 @@ limitations under the License. -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; - color: #454545; - background-color: #fff; + color: $primary-fg-color; + background-color: $primary-bg-color; font-size: 14px; max-height: 120px; overflow: auto; @@ -120,11 +120,11 @@ limitations under the License. /* hack for FF as vertical alignment of custom placeholder text is broken */ .mx_MessageComposer_input textarea::-moz-placeholder { line-height: 100%; - color: #76cfa6; + color: $accent-color; opacity: 1.0; } .mx_MessageComposer_input textarea::-webkit-input-placeholder { - color: #76cfa6; + color: $accent-color; } .mx_MessageComposer_upload, diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss index 056fa879..a26f12ee 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss @@ -63,10 +63,10 @@ limitations under the License. .mx_RoomHeader_textButton { height: 36px; - background-color: #76cfa6; + background-color: $accent-color; border-radius: 36px; margin-right: 8px; - color: #fff; + color: $accent-fg-color; line-height: 34px; margin-top: -2px; text-align: center; @@ -102,7 +102,7 @@ limitations under the License. .mx_RoomHeader_rightRow { margin-top: 4px; - background-color: #fff; + background-color: $primary-bg-color; display: flex; align-items: center; -webkit-box-ordinal-group: 3; @@ -120,7 +120,7 @@ limitations under the License. .mx_RoomHeader_simpleHeader { line-height: 70px; - color: #454545; + color: $primary-fg-color; font-size: 22px; font-weight: bold; overflow: hidden; @@ -138,7 +138,7 @@ limitations under the License. width: 100%; height: 31px; overflow: hidden; - color: #454545; + color: $primary-fg-color; font-weight: bold; font-size: 22px; padding-left: 19px; @@ -174,7 +174,7 @@ limitations under the License. } .mx_RoomHeader_name:hover div:not(.mx_RoomHeader_editable) { - color: #76cfa6; + color: $accent-color; } .mx_RoomHeader_placeholder { @@ -182,13 +182,13 @@ limitations under the License. } .mx_RoomHeader_editable { - border-bottom: 1px solid #c7c7c7 ! important; + border-bottom: 1px solid $strong-input-border-color ! important; min-width: 150px; cursor: text; } .mx_RoomHeader_editable:focus { - border-bottom: 1px solid #76CFA6 ! important; + border-bottom: 1px solid $accent-color ! important; outline: none; box-shadow: none; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss index 286a39f7..6b71f96d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss @@ -38,11 +38,11 @@ limitations under the License. } .mx_RoomPreviewBar_invite_text { - color: #454545; + color: $primary-fg-color; } .mx_RoomPreviewBar_join_text { - color: #ff0064; + color: $warning-color; } .mx_RoomPreviewBar_preview_text { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss index 6a3b1ac8..132c9792 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss @@ -24,10 +24,10 @@ limitations under the License. .mx_RoomSettings_integrationsButton_error { position: relative; height: 36px; - background-color: #76cfa6; + background-color: $accent-color; border-radius: 36px; margin-right: 8px; - color: #fff; + color: $accent-fg-color; line-height: 34px; text-align: center; float: right; @@ -47,8 +47,8 @@ limitations under the License. font-size: 10pt; line-height: 1.5em; border-radius: 5px; - background-color: #000; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; } .mx_RoomSettings_e2eIcon { @@ -174,7 +174,7 @@ limitations under the License. } .mx_RoomSettings_warning { - color: #ff0064; + color: $warning-color; font-weight: bold; margin-top: 8px; margin-bottom: 8px; @@ -182,13 +182,13 @@ limitations under the License. .mx_RoomSettings_editable { border: 0px; - border-bottom: 1px solid #c7c7c7; + border-bottom: 1px solid $strong-input-border-color; padding: 0px; min-width: 240px; } .mx_RoomSettings_editable:focus { - border-bottom: 1px solid #76CFA6; + border-bottom: 1px solid $accent-color; outline: none; box-shadow: none; } @@ -220,8 +220,8 @@ limitations under the License. border-radius: 36px; font-weight: 400; font-size: 15px; - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; width: auto; margin: auto; padding: 6px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss index 2822d82e..22364043 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss @@ -78,7 +78,7 @@ limitations under the License. position: absolute; content: ""; border-radius: 40px; - background: #4A4A4A; + background: $primary-fg-color; bottom: 0; width: 24px; height: 24px; @@ -103,7 +103,7 @@ limitations under the License. padding-right: 6px; padding-top: 2px; padding-bottom: 3px; - color: rgba(69, 69, 69, 0.8); + color: $roomtile-name-color; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -142,7 +142,7 @@ limitations under the License. width: 0; height: 0; margin-left: 5px; - border-top: 5px solid #ff0064; + border-top: 5px solid $warning-color; border-right: 7px solid transparent; } @@ -154,7 +154,7 @@ limitations under the License. right: 8px; /*gutter */ top: 9px; border-radius: 8px; - color: #fff; + color: $accent-fg-color; font-weight: 600; font-size: 10px; text-align: center; @@ -175,11 +175,11 @@ limitations under the License. } .mx_RoomTile_unreadNotify .mx_RoomTile_badge { - background-color: #76cfa6; + background-color: $accent-color; } .mx_RoomTile_highlight .mx_RoomTile_badge { - background-color: #ff0064; + background-color: $warning-color; } .mx_RoomTile_unread, .mx_RoomTile_highlight { @@ -187,7 +187,7 @@ limitations under the License. } .mx_RoomTile_selected { - background-color: rgba(255, 255, 255, 0.8); + background-color: $roomtile-selected-bg-color; } .mx_RoomTile .mx_RoomTile_name.mx_RoomTile_badgeShown { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss index 76d9e216..9a24868d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss @@ -25,10 +25,10 @@ limitations under the License. .mx_SearchableEntityList_query { font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; border-radius: 3px; - border: 1px solid #f0f0f0; + border: 1px solid $input-border-color; padding: 9px; - color: #454545; - background-color: #fff; + color: $primary-fg-color; + background-color: $primary-bg-color; margin-left: 3px; font-size: 15px; margin-bottom: 8px; @@ -36,13 +36,13 @@ limitations under the License. } .mx_SearchableEntityList_query::-moz-placeholder { - color: #454545; + color: $primary-fg-color; opacity: 0.5; font-size: 12px; } .mx_SearchableEntityList_query::-webkit-input-placeholder { - color: #454545; + color: $primary-fg-color; opacity: 0.5; font-size: 12px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss index 1f8a3450..5dcbd21a 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_TabCompleteBar.scss @@ -27,7 +27,7 @@ limitations under the License. .mx_TabCompleteBar_command { margin-right: 8px; - background-color: #76CFA6; + background-color: $accent-color; padding-left: 8px; padding-right: 8px; padding-top: 2px; @@ -41,7 +41,7 @@ limitations under the License. .mx_TabCompleteBar_command .mx_TabCompleteBar_text { opacity: 1.0; vertical-align: initial; - color: #fff; + color: $accent-fg-color; } .mx_TabCompleteBar_item img { @@ -50,7 +50,7 @@ limitations under the License. } .mx_TabCompleteBar_text { - color: #4a4a4a; + color: $primary-fg-color; vertical-align: middle; opacity: 0.5; } 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 77184d42..7bbafcbe 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 @@ -19,7 +19,7 @@ limitations under the License. max-width: 960px; padding-top: 5px; padding-bottom: 5px; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; } .mx_TopUnreadMessagesBar_scrollUp { diff --git a/src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss b/src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss index 93ee0e20..13fc9b22 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/settings/_IntegrationsManager.scss @@ -24,7 +24,7 @@ limitations under the License. } .mx_IntegrationsManager iframe { - background-color: #fff; + background-color: $primary-bg-color; border: 0px; width: 100%; height: 100%; diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss b/src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss index 8051b4d0..deb89a83 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/_CallView.scss @@ -15,8 +15,8 @@ limitations under the License. */ .mx_CallView_voice { - background-color: #76cfa6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; cursor: pointer; text-align: center; padding: 6px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss index 5cf62091..97a82a03 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss @@ -18,7 +18,7 @@ limitations under the License. text-align: center; border: 1px solid #a4a4a4; border-radius: 8px; - background-color: #fff; + background-color: $primary-bg-color; position: fixed; z-index: 1000; padding: 6px; @@ -58,7 +58,7 @@ limitations under the License. height: 36px; line-height: 36px; border-radius: 36px; - color: #fff; + color: $accent-fg-color; margin: auto; } diff --git a/src/skins/vector/css/rethemendex.sh b/src/skins/vector/css/rethemendex.sh index 27c47224..29e34382 100755 --- a/src/skins/vector/css/rethemendex.sh +++ b/src/skins/vector/css/rethemendex.sh @@ -2,7 +2,7 @@ echo "// autogenerated by rethemendex.sh" > _components.scss -for i in `find */* -iname _\*.scss`; +for i in `find . -iname _\*.scss | fgrep -v _components.scss`; do echo "@import \"`dirname $i`/`basename $i .scss`\";" >> _components.scss done diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss new file mode 100644 index 00000000..0fb25a6a --- /dev/null +++ b/src/skins/vector/css/themes/_base.scss @@ -0,0 +1,68 @@ + + +// typical text (dark-on-white in light skin) +$primary-fg-color: #454545; +$primary-bg-color: #ffffff; + +// used for dialog box text +$light-fg-color: #747474; + +// button UI (white-on-green in light skin) +$accent-fg-color: #ffffff; +$accent-color: #76CFA6; + +// red warning colour +$warning-color: #ff0064; + +// left-panel style muted accent color +$secondary-accent-color: #eaf5f0; + +// used by AddressSelector +$selected-color: #eaf5f0; + +// selected for hoverover & selected event tiles +$event-selected-color: #f7f7f7; + +// used for the hairline dividers in RoomView +$primary-hairline-color: #e5e5e5; + +// used for the border of input text fields +$input-border-color: #f0f0f0; + +// apart from login forms, which have stronger border +$strong-input-border-color: #c7c7c7; + +// context menus +$menu-border-color: rgba(187, 187, 187, 0.5); +$menu-bg-color: #f6f6f6; + +$avatar-initial-color: #ffffff; + +// ******************** + +$roomtile-name-color: rgba(69, 69, 69, 0.8); +$roomtile-selected-bg-color: rgba(255, 255, 255, 0.8); + +$roomsublist-label-fg-color: #3d3b39; +$roomsublist-label-bg-color: #d3efe1; + +// ******************** + +// event tile lifecycle +$event-encrypting-color: #abddbc; +$event-sending-color: #ddd; +$event-notsent-color: #f44; + +// event timestamp +$event-timestamp-color: #acacac; + +// e2e +$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color +$e2e-unverified-color: #e8bf37; +$e2e-warning-color: #ba6363; + +// ******************** + +$lightbox-bg-color: #454545; +$lightbox-fg-color: #ffffff; +$lightbox-border-color: #ffffff; \ No newline at end of file diff --git a/src/skins/vector/css/themes/dark.scss b/src/skins/vector/css/themes/dark.scss index 19ab2cde..e5602402 100644 --- a/src/skins/vector/css/themes/dark.scss +++ b/src/skins/vector/css/themes/dark.scss @@ -1,7 +1,69 @@ +@import "_base"; + // typical text (dark-on-white in light skin) $primary-fg-color: #dddddd; $primary-bg-color: #2d2d2d; -// button UI (white-on-green in light skin) +// used for dialog box text +$light-fg-color: #747474; -@import "../_components" \ No newline at end of file +// button UI (white-on-green in light skin) +$accent-fg-color: $primary-bg-color; +$accent-color: #76CFA6; + +// red warning colour +$warning-color: #ff0064; + +// left-panel style muted accent color +$secondary-accent-color: $primary-bg-color; + +// used by AddressSelector +$selected-color: #eaf5f0; + +// selected for hoverover & selected event tiles +$event-selected-color: #353535; + +// used for the hairline dividers in RoomView +$primary-hairline-color: #474747; + +// used for the border of input text fields +$input-border-color: #3a3a3a; + +// apart from login forms, which have stronger border +$strong-input-border-color: #656565; + +// context menus +$menu-border-color: rgba(187, 187, 187, 0.5); +$menu-bg-color: #373737; + +$avatar-initial-color: #2d2d2d; + +// ******************** + +$roomtile-name-color: rgba(186, 186, 186, 0.8); +$roomtile-selected-bg-color: rgba(0, 0, 0, 0.8); + +$roomsublist-label-fg-color: $primary-fg-color; +$roomsublist-label-bg-color: #454545; + +// ******************** + +// event tile lifecycle +$event-encrypting-color: #abddbc; +$event-sending-color: #ddd; +$event-notsent-color: #f44; + +// event timestamp +$event-timestamp-color: #acacac; + +// e2e +$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color +$e2e-unverified-color: #e8bf37; +$e2e-warning-color: #ba6363; + +/*** ImageView ***/ +$lightbox-bg-color: #454545; +$lightbox-fg-color: #ffffff; +$lightbox-border-color: #ffffff; + +@import "../_components"; \ No newline at end of file diff --git a/src/skins/vector/css/themes/light.scss b/src/skins/vector/css/themes/light.scss index e0ad84f6..8b951e27 100644 --- a/src/skins/vector/css/themes/light.scss +++ b/src/skins/vector/css/themes/light.scss @@ -1,4 +1,2 @@ -$primary-fg-color: #454545; -$primary-bg-color: #ffffff; - -@import "../_components" \ No newline at end of file +@import "_base"; +@import "../_components"; \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/structures/_RightPanel.scss b/src/skins/vector/css/vector-web/structures/_RightPanel.scss index 5da4c4ab..b9dbf1cd 100644 --- a/src/skins/vector/css/vector-web/structures/_RightPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_RightPanel.scss @@ -33,7 +33,7 @@ limitations under the License. -webkit-order: 1; order: 1; - border-bottom: 1px solid #e5e5e5; + border-bottom: 1px solid $primary-hairline-color; margin-right: 20px; -webkit-flex: 0 0 70px; @@ -45,7 +45,7 @@ limitations under the License. .mx_RightPanel_headerButtonGroup { margin-top: 6px; float: left; - background-color: #fff; + background-color: $primary-bg-color; margin-left: 0px; } @@ -73,7 +73,7 @@ limitations under the License. .mx_RightPanel_headerButton_badge { font-size: 11px; - color: #76cfa6; + color: $accent-color; font-weight: bold; padding-bottom: 2px; } @@ -97,7 +97,7 @@ limitations under the License. -webkit-order: 3; order: 3; - border-top: 1px solid #e5e5e5; + border-top: 1px solid $primary-hairline-color; margin-right: 20px; -webkit-flex: 0 0 60px; @@ -107,7 +107,7 @@ limitations under the License. .mx_RightPanel_footer .mx_RightPanel_invite { line-height: 35px; font-size: 14px; - color: #4A4A4A; + color: $primary-fg-color; padding-top: 13px; padding-left: 5px; cursor: pointer; diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index 563b1772..e5df8741 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -20,7 +20,7 @@ limitations under the License. margin-left: auto; margin-right: auto; margin-bottom: 12px; - color: #4a4a4a; + color: $primary-fg-color; display: -webkit-box; display: -moz-box; @@ -78,7 +78,7 @@ limitations under the License. .mx_RoomDirectory_table { font-size: 14px; - color: #4a4a4a; + color: $primary-fg-color; width: 100%; text-align: left; table-layout: fixed; @@ -110,7 +110,7 @@ limitations under the License. padding-right: 5px; height: 15px; border-radius: 11px; - background-color: #eaf5f0; + background-color: $secondary-accent-color; text-transform: uppercase; font-weight: 600; font-size: 11px; diff --git a/src/skins/vector/css/vector-web/structures/_RoomSubList.scss b/src/skins/vector/css/vector-web/structures/_RoomSubList.scss index e6e81aef..0d56d6c3 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomSubList.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomSubList.scss @@ -29,7 +29,7 @@ limitations under the License. .mx_RoomSubList_label { position: relative; text-transform: uppercase; - color: #3d3b39; + color: $roomsublist-label-fg-color; font-weight: 600; font-size: 12px; width: 203px; /* padding + width = LHS Panel width */ @@ -39,8 +39,8 @@ limitations under the License. padding-top: 6px; padding-bottom: 6px; cursor: pointer; - background-color: #d3efe1; - border-top: solid 2px #eaf5f0; + background-color: $roomsublist-label-bg-color; + border-top: solid 2px $secondary-accent-color; } .mx_RoomSubList_label.mx_RoomSubList_fixed { @@ -63,7 +63,7 @@ limitations under the License. display: inline-block; font-size: 12px; font-weight: normal; - color: #76cfa6; + color: $accent-color; padding-left: 5px; text-transform: none; } @@ -80,14 +80,14 @@ limitations under the License. right: 8px; /*gutter */ top: 7px; border-radius: 8px; - color: #fff; + color: $accent-fg-color; font-weight: 600; font-size: 10px; text-align: center; padding-top: 1px; padding-left: 4px; padding-right: 4px; - background-color: #76cfa6; + background-color: $accent-color; } /* @@ -97,7 +97,7 @@ limitations under the License. */ .mx_RoomSubList_badgeHighlight { - background-color: #ff0064; + background-color: $warning-color; } /* This is the bottom of the speech bubble */ @@ -108,7 +108,7 @@ limitations under the License. width: 0; height: 0; margin-left: 5px; - border-top: 5px solid #ff0064; + border-top: 5px solid $warning-color; border-right: 7px solid transparent; } @@ -129,7 +129,7 @@ limitations under the License. height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; - border-top: 6px solid #76cfa6; + border-top: 6px solid $accent-color; } .mx_RoomSubList_chevronUp { @@ -137,14 +137,14 @@ limitations under the License. height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; - border-bottom: 6px solid #76cfa6; + border-bottom: 6px solid $accent-color; } .mx_RoomSubList_chevronRight { width: 0; height: 0; border-top: 5px solid transparent; - border-left: 6px solid #76cfa6; + border-left: 6px solid $accent-color; border-bottom: 5px solid transparent; } @@ -165,7 +165,7 @@ limitations under the License. .mx_RoomSubList_line { display: inline-block; width: 159px; - border-top: dotted 2px #76cfa6; + border-top: dotted 2px $accent-color; vertical-align: middle; } @@ -179,7 +179,7 @@ limitations under the License. font-size: 10px; font-weight: 600; text-align: left; - color: #76cfa6; + color: $accent-color; padding-left: 7px; padding-right: 7px; padding-left: 7px; @@ -198,20 +198,20 @@ limitations under the License. right: 8px; /*gutter */ top: -2px; border-radius: 8px; - border: solid 1px #76cfa6; - color: #fff; + border: solid 1px $accent-color; + color: $accent-fg-color; font-weight: 600; font-size: 10px; text-align: center; padding-top: 1px; padding-left: 3px; padding-right: 3px; - background-color: #fff; + background-color: $primary-bg-color; vertical-align: middle; } .mx_RoomSubList_moreBadge.mx_RoomSubList_moreBadgeNotify { - background-color: #76cfa6; + background-color: $accent-color; border: 0; padding-top: 3px; padding-left: 4px; @@ -219,7 +219,7 @@ limitations under the License. } .mx_RoomSubList_moreBadge.mx_RoomSubList_moreBadgeHighlight { - background-color: #ff0064; + background-color: $warning-color; border: 0; padding-top: 3px; padding-left: 4px; diff --git a/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss b/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss index 947fd480..0a2e7605 100644 --- a/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss +++ b/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss @@ -31,7 +31,7 @@ limitations under the License. .mx_RoomTagContextMenu_field:last-child { padding-bottom: 4px; - color: #ff0064; + color: $warning-color; } .mx_RoomTagContextMenu_field.mx_RoomTagContextMenu_fieldSet { diff --git a/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss b/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss index 8b4c4459..9147fdb7 100644 --- a/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss +++ b/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss @@ -21,14 +21,14 @@ limitations under the License. .mx_NetworkDropdown_input { position: relative; border-radius: 3px; - border: 1px solid #c7c7c7; + border: 1px solid $strong-input-border-color; font-weight: 300; font-size: 13px; user-select: none; } .mx_NetworkDropdown_arrow { - border-color: #4a4a4a transparent transparent; + border-color: $primary-fg-color transparent transparent; border-style: solid; border-width: 5px 5px 0; display: block; @@ -67,7 +67,7 @@ input.mx_NetworkDropdown_networkoption, input.mx_NetworkDropdown_networkoption:f margin: 0; padding: 0px; border-radius: 3px; - border: 1px solid #76cfa6; + border: 1px solid $accent-color; background-color: white; } diff --git a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss index 03223f25..d66d9c7d 100644 --- a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss +++ b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss @@ -87,7 +87,7 @@ limitations under the License. padding-right: 30px; min-height: 100%; max-width: 240px; - color: #fff; + color: $lightbox-fg-color; } .mx_ImageView_cancel { @@ -114,10 +114,10 @@ limitations under the License. margin-top: 24px; margin-bottom: 6px; border-radius: 5px; - background-color: #454545; + background-color: $lightbox-bg-color; font-size: 14px; padding: 9px; - border: 1px solid #fff; + border: 1px solid $lightbox-border-color; } .mx_ImageView_size { @@ -125,7 +125,7 @@ limitations under the License. } .mx_ImageView_link { - color: #fff ! important; + color: $lightbox-fg-color ! important; text-decoration: none ! important; } diff --git a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss index 717d75af..e8b5aebb 100644 --- a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss @@ -15,8 +15,8 @@ limitations under the License. */ .mx_GuestWarningBar { - background-color: #76cfa6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; display: -webkit-box; display: -moz-box; @@ -34,7 +34,7 @@ limitations under the License. } .mx_GuestWarningBar a { - color: #fff ! important; + color: $accent-fg-color ! important; text-decoration: underline ! important; cursor: pointer; } diff --git a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss index 4e214e11..9bd70bb9 100644 --- a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss @@ -15,8 +15,8 @@ limitations under the License. */ .mx_MatrixToolbar { - background-color: #76cfa6; - color: #fff; + background-color: $accent-color; + color: $accent-fg-color; display: -webkit-box; display: -moz-box; @@ -40,7 +40,7 @@ limitations under the License. .mx_MatrixToolbar_link { - color: #fff ! important; + color: $accent-fg-color ! important; text-decoration: underline ! important; cursor: pointer; } diff --git a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss index 003215af..2fe16ca9 100644 --- a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss +++ b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss @@ -22,8 +22,8 @@ limitations under the License. margin-bottom: 7px; padding-top: 5px; padding-bottom: 5px; - border: 1px dashed #76cfa6; - color: #454545; + border: 1px dashed $accent-color; + color: $primary-fg-color; background-color: rgba(255,255,255,0.5); border-radius: 4px; } @@ -39,7 +39,7 @@ limitations under the License. } .mx_RoomDropTarget_avatar { - background-color: #fff; + background-color: $primary-bg-color; border-radius: 24px; width: 24px; height: 24px; diff --git a/src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss b/src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss index 192fa77b..5469a9e6 100644 --- a/src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss +++ b/src/skins/vector/css/vector-web/views/rooms/_RoomTooltip.scss @@ -21,16 +21,16 @@ limitations under the License. width: 0; height: 0; border-top: 8px solid transparent; - border-right: 8px solid rgba(187, 187, 187, 0.5); + border-right: 8px solid $menu-border-color; border-bottom: 8px solid transparent; } -.mx_RoomTooltip_chevron:after{ +.mx_RoomTooltip_chevron:after { content:''; width: 0; height: 0; border-top: 7px solid transparent; - border-right: 7px solid #fff; + border-right: 7px solid $primary-bg-color; border-bottom: 7px solid transparent; position:absolute; top: -7px; @@ -40,14 +40,14 @@ limitations under the License. .mx_RoomTooltip { display: none; position: fixed; - border: 1px solid rgba(187, 187, 187, 0.5); + border: 1px solid $menu-border-color; border-radius: 5px; - background-color: #fff; + background-color: $primary-bg-color; z-index: 2000; padding: 5px; pointer-events: none; line-height: 14px; font-size: 13px; - color: rgba(0, 0, 0, 0.7); + color: $primary-fg-color; } diff --git a/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss b/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss index 7ec1a17a..5d195322 100644 --- a/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss +++ b/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss @@ -26,7 +26,7 @@ limitations under the License. .mx_SearchBar_input { display: inline block; border-radius: 3px 0px 0px 3px; - border: 1px solid #f0f0f0; + border: 1px solid $input-border-color; font-size: 15px; padding: 9px; padding-left: 11px; @@ -41,7 +41,7 @@ limitations under the License. width: 37px; height: 37px; border-radius: 0px 3px 3px 0px; - background-color: #76CFA6; + background-color: $accent-color; } @keyframes pulsate { @@ -61,8 +61,8 @@ limitations under the License. border-radius: 36px; font-weight: 400; font-size: 15px; - color: #fff; - background-color: #76cfa6; + color: $accent-fg-color; + background-color: $accent-color; width: auto; margin: auto; margin-left: 7px; @@ -74,9 +74,9 @@ limitations under the License. } .mx_SearchBar_unselected { - background-color: #fff; - color: #76CFA6; - border: #76CFA6 1px solid; + background-color: $primary-bg-color; + color: $accent-color; + border: $accent-color 1px solid; } .mx_SearchBar_cancel { diff --git a/src/skins/vector/css/vector-web/views/settings/_Notifications.scss b/src/skins/vector/css/vector-web/views/settings/_Notifications.scss index 7a93f3f7..4c88e449 100644 --- a/src/skins/vector/css/vector-web/views/settings/_Notifications.scss +++ b/src/skins/vector/css/vector-web/views/settings/_Notifications.scss @@ -58,7 +58,7 @@ limitations under the License. .mx_UserNotifSettings_keywords { cursor: pointer; - color: #76cfa6; + color: $accent-color; } .mx_UserSettings_devicesTable td { diff --git a/src/vector/index.js b/src/vector/index.js index 96c6a971..47509887 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -30,7 +30,7 @@ require('babel-polyfill'); // CSS requires: just putting them here for now as CSS is going to be // refactored "soon" anyway -require('../../build/themes/light.css'); +require('../../build/themes/dark.css'); require('gemini-scrollbar/gemini-scrollbar.css'); require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); From d1a9695a353aec38762fcde749e852cfc3a5c3b5 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Wed, 28 Dec 2016 20:57:31 -0500 Subject: [PATCH 0023/1951] update README to point to new names/locations --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 41f307d3..f82572fb 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ https://riot.im/develop for those who like living dangerously. To host your own copy of Riot, the quickest bet is to use a pre-built released version of Riot: -1. Download the latest version from https://github.com/vector-im/vector-web/releases +1. Download the latest version from https://github.com/vector-im/riot-web/releases 1. Untar the tarball on your web server 1. Move (or symlink) the vector-x.x.x directory to an appropriate name 1. If desired, copy `config.sample.json` to `config.json` and edit it @@ -36,7 +36,7 @@ access to Riot (or other apps) due to sharing the same domain. We have put some coarse mitigations into place to try to protect against this situation, but it's still not good practice to do it in the first place. See -https://github.com/vector-im/vector-web/issues/1977 for more details. +https://github.com/vector-im/riot-web/issues/1977 for more details. Building From Source ==================== @@ -45,7 +45,7 @@ Riot is a modular webapp built with modern ES6 and requires a npm build system to build. 1. Install or update `node.js` so that your `npm` is at least at version `2.0.0` -1. Clone the repo: `git clone https://github.com/vector-im/vector-web.git` +1. Clone the repo: `git clone https://github.com/vector-im/riot-web.git` 1. Switch to the vector-web directory: `cd vector-web` 1. Install the prerequisites: `npm install` 1. If you are using the `develop` branch of vector-web, you will probably need @@ -97,7 +97,7 @@ Running as a Desktop app ======================== Riot can also be run as a desktop app, wrapped in electron. You can download a -pre-built version from https://riot.im/download/desktop/ or, if you prefer, +pre-built version from https://riot.im/desktop.html or, if you prefer, built it yourself. To run as a desktop app: @@ -167,13 +167,13 @@ the `component-index.js` for the app (used in future for skinning) development on Riot forcing `matrix-react-sdk` to move fast at the expense of maintaining a clear abstraction between the two.** Hacking on Riot inevitably means hacking equally on `matrix-react-sdk`, and there are bits of -`matrix-react-sdk` behaviour incorrectly residing in the `vector-web` project +`matrix-react-sdk` behaviour incorrectly residing in the `riot-web` project (e.g. matrix-react-sdk specific CSS), and a bunch of Riot specific behaviour in the `matrix-react-sdk` (grep for `vector` / `riot`). This separation problem will be solved asap once development on Riot (and thus matrix-react-sdk) has stabilised. Until then, the two projects should basically be considered as a single unit. In particular, `matrix-react-sdk` issues are currently filed -against `vector-web` in github. +against `riot-web` in github. Please note that Riot is intended to run correctly without access to the public internet. So please don't depend on resources (JS libs, CSS, images, fonts) @@ -208,8 +208,8 @@ Then similarly with `matrix-react-sdk`: Finally, build and start Riot itself: -1. `git clone git@github.com:vector-im/vector-web.git` -1. `cd vector-web` +1. `git clone git@github.com:vector-im/riot-web.git` +1. `cd riot-web` 1. `git checkout develop` 1. `npm install` 1. `rm -r node_modules/matrix-js-sdk; ln -s ../../matrix-js-sdk node_modules/` From 35b301338643dcabafb43653e6da17de9c9fdcd2 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sat, 31 Dec 2016 14:27:44 +0000 Subject: [PATCH 0024/1951] fix up a few more colours --- src/skins/vector/css/_common.scss | 7 ++++++- src/skins/vector/css/themes/_base.scss | 3 +++ src/skins/vector/css/themes/dark.scss | 3 +++ .../css/vector-web/views/directory/_NetworkDropdown.scss | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index e81ea2fa..ed96b48c 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -55,8 +55,13 @@ a:visited { color: $accent-color; } +input[type=text] { + background-color: $primary-bg-color; + color: $primary-fg-color; +} + input[type=text].error, input[type=password].error { - border: 1px solid red; + border: 1px solid $warning-color; } input[type=text]:focus, textarea:focus { diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index 0fb25a6a..b9fd2d12 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -7,6 +7,9 @@ $primary-bg-color: #ffffff; // used for dialog box text $light-fg-color: #747474; +// used for focusing form controls +$focus-bg-color: #dddddd; + // button UI (white-on-green in light skin) $accent-fg-color: #ffffff; $accent-color: #76CFA6; diff --git a/src/skins/vector/css/themes/dark.scss b/src/skins/vector/css/themes/dark.scss index e5602402..5449b90e 100644 --- a/src/skins/vector/css/themes/dark.scss +++ b/src/skins/vector/css/themes/dark.scss @@ -4,6 +4,9 @@ $primary-fg-color: #dddddd; $primary-bg-color: #2d2d2d; +// used for focusing form controls +$focus-bg-color: #101010; + // used for dialog box text $light-fg-color: #747474; diff --git a/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss b/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss index 9147fdb7..84aa896a 100644 --- a/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss +++ b/src/skins/vector/css/vector-web/views/directory/_NetworkDropdown.scss @@ -68,11 +68,11 @@ input.mx_NetworkDropdown_networkoption, input.mx_NetworkDropdown_networkoption:f padding: 0px; border-radius: 3px; border: 1px solid $accent-color; - background-color: white; + background-color: $primary-bg-color; } .mx_NetworkDropdown_menu .mx_NetworkDropdown_networkoption:hover { - background-color: #ddd; + background-color: $focus-bg-color; } .mx_NetworkDropdown_menu_network { From 85040a2e6d77c2a33262df92f8cf2cbfa5cc01e3 Mon Sep 17 00:00:00 2001 From: rubo77 Date: Thu, 5 Jan 2017 23:18:56 +0100 Subject: [PATCH 0025/1951] Hide Options button from copy to clipboard --- .../vector/css/matrix-react-sdk/views/rooms/EventTile.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css index fd3f486b..cbae8643 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/EventTile.css @@ -172,6 +172,10 @@ limitations under the License. cursor: pointer; top: 6px; right: 6px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .mx_EventTile:hover .mx_EventTile_editButton, From 6a1af891b77f1b0c63bd1df32d0ac8c87b59dff7 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 6 Jan 2017 10:43:13 +0000 Subject: [PATCH 0026/1951] Use a custom script to copy resources (#2893) All of those cpx invocations were getting unwieldy, and I suspect the exotic quoting needed to run them under parallelshell was breaking things on windows. Replace the unwieldy cpx invocations with an unwieldy custom script. --- package.json | 20 +++++------ scripts/copy-res.js | 81 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 11 deletions(-) create mode 100755 scripts/copy-res.js diff --git a/package.json b/package.json index 3f9f46e2..52cc41be 100644 --- a/package.json +++ b/package.json @@ -27,26 +27,22 @@ "matrix-react-parent": "matrix-react-sdk", "scripts": { "reskindex": "reskindex -h src/header", - "build:res": "cpx \"{src/skins/vector/fonts,src/skins/vector/img}/**\" webapp/ && cpx \"{res/media,res/vector-icons}/**\" webapp/", - "build:config": "cpx config.json webapp/", - "build:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/", + "build:res": "node scripts/copy-res.js", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", "build:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css --no-watch", "build:compile": "babel --source-maps -d lib src", "build:bundle": "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:config && npm run build:emojione && npm run build:css && npm run build:bundle", - "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:config && npm run build:emojione && npm run build:css && npm run build:bundle:dev", + "build": "node scripts/babelcheck.js && npm run build:res && npm run build:css && npm run build:bundle", + "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:css && npm run build:bundle:dev", "dist": "scripts/package.sh", - "start:res": "parallelshell \"cpx -w \\\"{src/skins/vector/fonts,src/skins/vector/img}/**\\\" webapp/\" \"cpx -w \\\"{res/media,res/vector-icons}/**\\\" webapp/\"", - "start:config": "cpx -w config.json webapp/", - "start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/ -w", + "start:res": "node scripts/copy-res.js -w", "start:js": "webpack-dev-server -w --progress", "start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress", - "start:skins:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css", - "start": "node scripts/babelcheck.js && parallelshell \"npm run start:emojione\" \"npm run start:res\" \"npm run start:config\" \"npm run start:js\" \"npm run start:skins:css\"", - "start:prod": "parallelshell \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\"", + "start:css": "mkdirp build && catw \"src/skins/vector/css/**/*.css\" -o build/components.css", + "start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\" \"npm run start:css\"", + "start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\" \"npm run start:css\"", "clean": "rimraf build lib webapp electron/dist", "prepublish": "npm run build:compile", "test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false", @@ -95,6 +91,7 @@ "babel-preset-react": "^6.16.0", "babel-preset-stage-2": "^6.17.0", "catw": "^1.0.1", + "chokidar": "^1.6.1", "cpx": "^1.3.2", "css-raw-loader": "^0.1.1", "electron-builder": "^10.4.1", @@ -111,6 +108,7 @@ "karma-phantomjs-launcher": "^1.0.0", "karma-sourcemap-loader": "^0.3.7", "karma-webpack": "^1.7.0", + "minimist": "^1.2.0", "mkdirp": "^0.5.1", "mocha": "^2.4.5", "parallelshell": "^1.2.0", diff --git a/scripts/copy-res.js b/scripts/copy-res.js new file mode 100755 index 00000000..826d9a96 --- /dev/null +++ b/scripts/copy-res.js @@ -0,0 +1,81 @@ +#!/usr/bin/env node + +// copies the resources into the webapp directory. +// + +// cpx includes globbed parts of the filename in the destination, but excludes +// common parents. Hence, "res/{a,b}/**": the output will be "dest/a/..." and +// "dest/b/...". +const COPY_LIST = [ + ["res/{media,vector-icons}/**", "webapp"], + ["src/skins/vector/{fonts,img}/**", "webapp"], + ["node_modules/emojione/assets/svg/*", "webapp/emojione/svg/"], + ["./config.json", "webapp", {directwatch: 1}], +]; + +const parseArgs = require('minimist'); +const Cpx = require('cpx'); +const chokidar = require('chokidar'); + +const argv = parseArgs( + process.argv.slice(2), {} +); + +var watch = argv.w; +var verbose = argv.v; + +function errCheck(err) { + if (err) { + console.error(err.message); + process.exit(1); + } +} + +function next(i, err) { + errCheck(err); + + if (i >= COPY_LIST.length) { + return; + } + + const ent = COPY_LIST[i]; + const source = ent[0]; + const dest = ent[1]; + const opts = ent[2] || {}; + + const cpx = new Cpx.Cpx(source, dest); + + if (verbose) { + cpx.on("copy", (event) => { + console.log(`Copied: ${event.srcPath} --> ${event.dstPath}`); + }); + cpx.on("remove", (event) => { + console.log(`Removed: ${event.path}`); + }); + } + + const cb = (err) => {next(i+1, err)}; + + if (watch) { + if (opts.directwatch) { + // cpx -w creates a watcher for the parent of any files specified, + // which in the case of config.json is '.', which inevitably takes + // ages to crawl. So we create our own watcher on the files + // instead. + const copy = () => {cpx.copy(errCheck)}; + chokidar.watch(source) + .on('add', copy) + .on('change', copy) + .on('ready', cb) + .on('error', errCheck); + } else { + cpx.on('watch-ready', cb); + cpx.on("watch-error", cb); + cpx.watch(); + } + } else { + cpx.copy(cb); + } +} + +next(0); From 691fe611d6141f6c43f9e966ab135a721f248482 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 10 Jan 2017 11:06:09 +0000 Subject: [PATCH 0027/1951] experimental postcss --- package.json | 14 ++- src/skins/vector/css/_components.scss | 145 +++++++++++++------------ src/skins/vector/css/rethemendex.sh | 2 +- src/skins/vector/css/themes/dark.scss | 76 +------------ src/skins/vector/css/themes/light.scss | 4 +- src/vector/index.js | 2 +- webpack.config.js | 4 +- 7 files changed, 94 insertions(+), 153 deletions(-) diff --git a/package.json b/package.json index c657aad6..8eb562d3 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "build:config": "cpx config.json webapp/", "build:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", - "build:css": "mkdirp build && node-sass --recursive --source-map true --output build \"src/skins/vector/css\"", + "build:css": "mkdirp build && postcss -c postcss.config.json", "build:compile": "babel --source-maps -d lib src", "build:bundle": "NODE_ENV=production webpack -p --progress", "build:bundle:dev": "webpack --optimize-occurence-order --progress", @@ -44,7 +44,7 @@ "start:emojione": "cpx \"node_modules/emojione/assets/svg/*\" webapp/emojione/svg/ -w", "start:js": "webpack-dev-server -w --progress", "start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress", - "start:skins:css": "mkdirp build && node-sass --recursive --watch --source-map true --output build \"src/skins/vector/css\"", + "start:skins:css": "mkdirp build && postcss -c postcss.config.json -w", "start": "node scripts/babelcheck.js && parallelshell \"npm run start:emojione\" \"npm run start:res\" \"npm run start:config\" \"npm run start:js\" \"npm run start:skins:css\"", "start:prod": "parallelshell \"npm run start:emojione\" \"npm run start:js:prod\" \"npm run start:skins:css\"", "clean": "rimraf build lib webapp electron/dist", @@ -80,6 +80,7 @@ "url": "^0.11.0" }, "devDependencies": { + "autoprefixer": "^6.6.0", "babel-cli": "^6.5.2", "babel-core": "^6.14.0", "babel-eslint": "^6.1.0", @@ -116,10 +117,17 @@ "node-sass": "^4.1.1", "parallelshell": "^1.2.0", "phantomjs-prebuilt": "^2.1.7", + "postcss-cli": "^2.6.0", + "postcss-extend": "^1.0.5", + "postcss-import": "^9.0.0", + "postcss-mixins": "^5.4.1", + "postcss-nested": "^1.0.0", + "postcss-scss": "^0.4.0", + "postcss-simple-vars": "^3.0.0", + "postcss-strip-inline-comments": "^0.1.5", "react-addons-perf": "^15.4.0", "react-addons-test-utils": "^15.4.0", "rimraf": "^2.4.3", - "sass-loader": "^4.1.1", "source-map-loader": "^0.1.5", "webpack": "^1.12.14", "webpack-dev-server": "^1.16.2" diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 532e0a36..88435236 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -1,73 +1,74 @@ // autogenerated by rethemendex.sh -@import "_common"; -@import "matrix-react-sdk/structures/_ContextualMenu"; -@import "matrix-react-sdk/structures/_CreateRoom"; -@import "matrix-react-sdk/structures/_FilePanel"; -@import "matrix-react-sdk/structures/_MatrixChat"; -@import "matrix-react-sdk/structures/_NotificationPanel"; -@import "matrix-react-sdk/structures/_RoomStatusBar"; -@import "matrix-react-sdk/structures/_RoomView"; -@import "matrix-react-sdk/structures/_SearchBox"; -@import "matrix-react-sdk/structures/_UploadBar"; -@import "matrix-react-sdk/structures/_UserSettings"; -@import "matrix-react-sdk/structures/login/_Login"; -@import "matrix-react-sdk/views/avatars/_BaseAvatar"; -@import "matrix-react-sdk/views/dialogs/_ChatInviteDialog"; -@import "matrix-react-sdk/views/dialogs/_EncryptedEventDialog"; -@import "matrix-react-sdk/views/dialogs/_SetDisplayNameDialog"; -@import "matrix-react-sdk/views/elements/_AddressSelector"; -@import "matrix-react-sdk/views/elements/_AddressTile"; -@import "matrix-react-sdk/views/elements/_DirectorySearchBox"; -@import "matrix-react-sdk/views/elements/_MemberEventListSummary"; -@import "matrix-react-sdk/views/elements/_ProgressBar"; -@import "matrix-react-sdk/views/elements/_RichText"; -@import "matrix-react-sdk/views/login/_ServerConfig"; -@import "matrix-react-sdk/views/messages/_MImageBody"; -@import "matrix-react-sdk/views/messages/_MNoticeBody"; -@import "matrix-react-sdk/views/messages/_MTextBody"; -@import "matrix-react-sdk/views/messages/_TextualEvent"; -@import "matrix-react-sdk/views/messages/_UnknownBody"; -@import "matrix-react-sdk/views/rooms/_Autocomplete"; -@import "matrix-react-sdk/views/rooms/_EntityTile"; -@import "matrix-react-sdk/views/rooms/_EventTile"; -@import "matrix-react-sdk/views/rooms/_LinkPreviewWidget"; -@import "matrix-react-sdk/views/rooms/_MemberDeviceInfo"; -@import "matrix-react-sdk/views/rooms/_MemberInfo"; -@import "matrix-react-sdk/views/rooms/_MemberList"; -@import "matrix-react-sdk/views/rooms/_MessageComposer"; -@import "matrix-react-sdk/views/rooms/_PresenceLabel"; -@import "matrix-react-sdk/views/rooms/_RoomHeader"; -@import "matrix-react-sdk/views/rooms/_RoomList"; -@import "matrix-react-sdk/views/rooms/_RoomPreviewBar"; -@import "matrix-react-sdk/views/rooms/_RoomSettings"; -@import "matrix-react-sdk/views/rooms/_RoomTile"; -@import "matrix-react-sdk/views/rooms/_SearchableEntityList"; -@import "matrix-react-sdk/views/rooms/_TabCompleteBar"; -@import "matrix-react-sdk/views/rooms/_TopUnreadMessagesBar"; -@import "matrix-react-sdk/views/settings/_DevicesPanel"; -@import "matrix-react-sdk/views/settings/_IntegrationsManager"; -@import "matrix-react-sdk/views/voip/_CallView"; -@import "matrix-react-sdk/views/voip/_IncomingCallbox"; -@import "matrix-react-sdk/views/voip/_VideoView"; -@import "vector-web/_fonts"; -@import "vector-web/structures/_CompatibilityPage"; -@import "vector-web/structures/_LeftPanel"; -@import "vector-web/structures/_RightPanel"; -@import "vector-web/structures/_RoomDirectory"; -@import "vector-web/structures/_RoomSubList"; -@import "vector-web/structures/_ViewSource"; -@import "vector-web/views/context_menus/_MessageContextMenu"; -@import "vector-web/views/context_menus/_NotificationStateContextMenu"; -@import "vector-web/views/context_menus/_RoomTagContextMenu"; -@import "vector-web/views/dialogs/_ChangelogDialog"; -@import "vector-web/views/directory/_NetworkDropdown"; -@import "vector-web/views/elements/_ImageView"; -@import "vector-web/views/elements/_Spinner"; -@import "vector-web/views/globals/_GuestWarningBar"; -@import "vector-web/views/globals/_MatrixToolbar"; -@import "vector-web/views/messages/_MessageTimestamp"; -@import "vector-web/views/messages/_SenderProfile"; -@import "vector-web/views/rooms/_RoomDropTarget"; -@import "vector-web/views/rooms/_RoomTooltip"; -@import "vector-web/views/rooms/_SearchBar"; -@import "vector-web/views/settings/_Notifications"; +@import "./_common.scss"; +@import "./matrix-react-sdk/structures/_ContextualMenu.scss"; +@import "./matrix-react-sdk/structures/_CreateRoom.scss"; +@import "./matrix-react-sdk/structures/_FilePanel.scss"; +@import "./matrix-react-sdk/structures/_MatrixChat.scss"; +@import "./matrix-react-sdk/structures/_NotificationPanel.scss"; +@import "./matrix-react-sdk/structures/_RoomStatusBar.scss"; +@import "./matrix-react-sdk/structures/_RoomView.scss"; +@import "./matrix-react-sdk/structures/_SearchBox.scss"; +@import "./matrix-react-sdk/structures/_UploadBar.scss"; +@import "./matrix-react-sdk/structures/_UserSettings.scss"; +@import "./matrix-react-sdk/structures/login/_Login.scss"; +@import "./matrix-react-sdk/views/avatars/_BaseAvatar.scss"; +@import "./matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss"; +@import "./matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss"; +@import "./matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.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"; +@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/login/_ServerConfig.scss"; +@import "./matrix-react-sdk/views/messages/_MImageBody.scss"; +@import "./matrix-react-sdk/views/messages/_MNoticeBody.scss"; +@import "./matrix-react-sdk/views/messages/_MTextBody.scss"; +@import "./matrix-react-sdk/views/messages/_TextualEvent.scss"; +@import "./matrix-react-sdk/views/messages/_UnknownBody.scss"; +@import "./matrix-react-sdk/views/rooms/_Autocomplete.scss"; +@import "./matrix-react-sdk/views/rooms/_EntityTile.scss"; +@import "./matrix-react-sdk/views/rooms/_EventTile.scss"; +@import "./matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss"; +@import "./matrix-react-sdk/views/rooms/_MemberDeviceInfo.scss"; +@import "./matrix-react-sdk/views/rooms/_MemberInfo.scss"; +@import "./matrix-react-sdk/views/rooms/_MemberList.scss"; +@import "./matrix-react-sdk/views/rooms/_MessageComposer.scss"; +@import "./matrix-react-sdk/views/rooms/_PresenceLabel.scss"; +@import "./matrix-react-sdk/views/rooms/_RoomHeader.scss"; +@import "./matrix-react-sdk/views/rooms/_RoomList.scss"; +@import "./matrix-react-sdk/views/rooms/_RoomPreviewBar.scss"; +@import "./matrix-react-sdk/views/rooms/_RoomSettings.scss"; +@import "./matrix-react-sdk/views/rooms/_RoomTile.scss"; +@import "./matrix-react-sdk/views/rooms/_SearchableEntityList.scss"; +@import "./matrix-react-sdk/views/rooms/_TabCompleteBar.scss"; +@import "./matrix-react-sdk/views/rooms/_TopUnreadMessagesBar.scss"; +@import "./matrix-react-sdk/views/settings/_DevicesPanel.scss"; +@import "./matrix-react-sdk/views/settings/_IntegrationsManager.scss"; +@import "./matrix-react-sdk/views/voip/_CallView.scss"; +@import "./matrix-react-sdk/views/voip/_IncomingCallbox.scss"; +@import "./matrix-react-sdk/views/voip/_VideoView.scss"; +@import "./themes/_base.scss"; +@import "./vector-web/_fonts.scss"; +@import "./vector-web/structures/_CompatibilityPage.scss"; +@import "./vector-web/structures/_LeftPanel.scss"; +@import "./vector-web/structures/_RightPanel.scss"; +@import "./vector-web/structures/_RoomDirectory.scss"; +@import "./vector-web/structures/_RoomSubList.scss"; +@import "./vector-web/structures/_ViewSource.scss"; +@import "./vector-web/views/context_menus/_MessageContextMenu.scss"; +@import "./vector-web/views/context_menus/_NotificationStateContextMenu.scss"; +@import "./vector-web/views/context_menus/_RoomTagContextMenu.scss"; +@import "./vector-web/views/dialogs/_ChangelogDialog.scss"; +@import "./vector-web/views/directory/_NetworkDropdown.scss"; +@import "./vector-web/views/elements/_ImageView.scss"; +@import "./vector-web/views/elements/_Spinner.scss"; +@import "./vector-web/views/globals/_GuestWarningBar.scss"; +@import "./vector-web/views/globals/_MatrixToolbar.scss"; +@import "./vector-web/views/messages/_MessageTimestamp.scss"; +@import "./vector-web/views/messages/_SenderProfile.scss"; +@import "./vector-web/views/rooms/_RoomDropTarget.scss"; +@import "./vector-web/views/rooms/_RoomTooltip.scss"; +@import "./vector-web/views/rooms/_SearchBar.scss"; +@import "./vector-web/views/settings/_Notifications.scss"; diff --git a/src/skins/vector/css/rethemendex.sh b/src/skins/vector/css/rethemendex.sh index 29e34382..915b235d 100755 --- a/src/skins/vector/css/rethemendex.sh +++ b/src/skins/vector/css/rethemendex.sh @@ -4,5 +4,5 @@ echo "// autogenerated by rethemendex.sh" > _components.scss for i in `find . -iname _\*.scss | fgrep -v _components.scss`; do - echo "@import \"`dirname $i`/`basename $i .scss`\";" >> _components.scss + echo "@import \"$i\";" >> _components.scss done diff --git a/src/skins/vector/css/themes/dark.scss b/src/skins/vector/css/themes/dark.scss index 5449b90e..0c51486b 100644 --- a/src/skins/vector/css/themes/dark.scss +++ b/src/skins/vector/css/themes/dark.scss @@ -1,72 +1,4 @@ -@import "_base"; - -// typical text (dark-on-white in light skin) -$primary-fg-color: #dddddd; -$primary-bg-color: #2d2d2d; - -// used for focusing form controls -$focus-bg-color: #101010; - -// used for dialog box text -$light-fg-color: #747474; - -// button UI (white-on-green in light skin) -$accent-fg-color: $primary-bg-color; -$accent-color: #76CFA6; - -// red warning colour -$warning-color: #ff0064; - -// left-panel style muted accent color -$secondary-accent-color: $primary-bg-color; - -// used by AddressSelector -$selected-color: #eaf5f0; - -// selected for hoverover & selected event tiles -$event-selected-color: #353535; - -// used for the hairline dividers in RoomView -$primary-hairline-color: #474747; - -// used for the border of input text fields -$input-border-color: #3a3a3a; - -// apart from login forms, which have stronger border -$strong-input-border-color: #656565; - -// context menus -$menu-border-color: rgba(187, 187, 187, 0.5); -$menu-bg-color: #373737; - -$avatar-initial-color: #2d2d2d; - -// ******************** - -$roomtile-name-color: rgba(186, 186, 186, 0.8); -$roomtile-selected-bg-color: rgba(0, 0, 0, 0.8); - -$roomsublist-label-fg-color: $primary-fg-color; -$roomsublist-label-bg-color: #454545; - -// ******************** - -// event tile lifecycle -$event-encrypting-color: #abddbc; -$event-sending-color: #ddd; -$event-notsent-color: #f44; - -// event timestamp -$event-timestamp-color: #acacac; - -// e2e -$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color -$e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; - -/*** ImageView ***/ -$lightbox-bg-color: #454545; -$lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; - -@import "../_components"; \ No newline at end of file +@import "_base.scss"; +@import "_dark.scss"; +@import "../_components.scss"; +// moofleasdadsasdadsa \ No newline at end of file diff --git a/src/skins/vector/css/themes/light.scss b/src/skins/vector/css/themes/light.scss index 8b951e27..ea0f93d5 100644 --- a/src/skins/vector/css/themes/light.scss +++ b/src/skins/vector/css/themes/light.scss @@ -1,2 +1,2 @@ -@import "_base"; -@import "../_components"; \ No newline at end of file +@import "_base.scss"; +@import "../_components.scss"; \ No newline at end of file diff --git a/src/vector/index.js b/src/vector/index.js index 47509887..64b87698 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -30,7 +30,7 @@ require('babel-polyfill'); // CSS requires: just putting them here for now as CSS is going to be // refactored "soon" anyway -require('../../build/themes/dark.css'); +require('../../build/dark.scss'); require('gemini-scrollbar/gemini-scrollbar.css'); require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); diff --git a/webpack.config.js b/webpack.config.js index 1c408b70..1f2a8a60 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -24,8 +24,8 @@ module.exports = { { test: /\.json$/, loader: "json" }, { test: /\.js$/, loader: "babel", include: path.resolve('./src') }, // css-raw-loader loads CSS but doesn't try to treat url()s as require()s - { test: /\.css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, - { test: /\.scss$/, loaders: ["style-loader", "css-loader?sourceMap", "sass-loader?sourceMap"] }, + // we're assuming that postcss has already preprocessed SCSS by this point + { test: /\.s?css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, ], noParse: [ // don't parse the languages within highlight.js. They cause stack From c1d4a0dd2826b8ec7dad6fcbd946c2ef4cd985b6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 10 Jan 2017 11:06:45 +0000 Subject: [PATCH 0028/1951] postcss experiment --- postcss.config.json | 18 +++++++ src/skins/vector/css/themes/_dark.scss | 69 ++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 postcss.config.json create mode 100644 src/skins/vector/css/themes/_dark.scss diff --git a/postcss.config.json b/postcss.config.json new file mode 100644 index 00000000..be3585cf --- /dev/null +++ b/postcss.config.json @@ -0,0 +1,18 @@ +{ + "use": [ + "autoprefixer", + "postcss-import", + "postcss-simple-vars", + "postcss-extend", + "postcss-nested", + "postcss-mixins", + "postcss-strip-inline-comments" + ], + "parser": "postcss-scss", + "input": "src/skins/vector/css/themes/[^_]*.scss", + "dir": "build", + "local-plugins": true, + "autoprefixer": { + "browsers": "> 5%" + } +} \ No newline at end of file diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss new file mode 100644 index 00000000..e2c33e3e --- /dev/null +++ b/src/skins/vector/css/themes/_dark.scss @@ -0,0 +1,69 @@ + +// typical text (dark-on-white in light skin) +$primary-fg-color: #dddddd; +$primary-bg-color: #2d2d2d; + +// used for focusing form controls +$focus-bg-color: #101010; + +// used for dialog box text +$light-fg-color: #747474; + +// button UI (white-on-green in light skin) +$accent-fg-color: $primary-bg-color; +$accent-color: #76CFA6; + +// red warning colour +$warning-color: #ff0064; + +// left-panel style muted accent color +$secondary-accent-color: $primary-bg-color; + +// used by AddressSelector +$selected-color: #eaf5f0; + +// selected for hoverover & selected event tiles +$event-selected-color: #353535; + +// used for the hairline dividers in RoomView +$primary-hairline-color: #474747; + +// used for the border of input text fields +$input-border-color: #3a3a3a; + +// apart from login forms, which have stronger border +$strong-input-border-color: #656565; + +// context menus +$menu-border-color: rgba(187, 187, 187, 0.5); +$menu-bg-color: #373737; + +$avatar-initial-color: #2d2d2d; + +// ******************** + +$roomtile-name-color: rgba(186, 186, 186, 0.8); +$roomtile-selected-bg-color: rgba(0, 0, 0, 0.8); + +$roomsublist-label-fg-color: $primary-fg-color; +$roomsublist-label-bg-color: #454545; + +// ******************** + +// event tile lifecycle +$event-encrypting-color: #abddbc; +$event-sending-color: #ddd; +$event-notsent-color: #f44; + +// event timestamp +$event-timestamp-color: #acacac; + +// e2e +$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color +$e2e-unverified-color: #e8bf37; +$e2e-warning-color: #ba6363; + +/*** ImageView ***/ +$lightbox-bg-color: #454545; +$lightbox-fg-color: #ffffff; +$lightbox-border-color: #ffffff; From c5459a2f19607a5d87a783b75cabcdfa4c0abd1d Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 10 Jan 2017 18:39:21 +0000 Subject: [PATCH 0029/1951] Enable screen sharing easter-egg in desktop app --- electron/src/electron-main.js | 2 ++ src/vector/platform/ElectronPlatform.js | 4 ++++ src/vector/platform/WebPlatform.js | 8 ++++++++ 3 files changed, 14 insertions(+) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 2b135a03..675640a5 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -148,6 +148,8 @@ process.on('uncaughtException', function (error) { electron.ipcMain.on('install_update', installUpdate); +electron.app.commandLine.appendSwitch('--enable-usermedia-screen-capturing'); + electron.app.on('ready', () => { if (vectorConfig.update_base_url) { console.log("Starting auto update with base URL: " + vectorConfig.update_base_url); diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index 3b41822b..68df88b0 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -127,4 +127,8 @@ export default class ElectronPlatform extends VectorBasePlatform { getDefaultDeviceDisplayName() { return "Riot Desktop on " + platformFriendlyName(); } + + screenCaptureErrorString() { + return null; + } } diff --git a/src/vector/platform/WebPlatform.js b/src/vector/platform/WebPlatform.js index c4ed4d55..2029822b 100644 --- a/src/vector/platform/WebPlatform.js +++ b/src/vector/platform/WebPlatform.js @@ -196,4 +196,12 @@ export default class WebPlatform extends VectorBasePlatform { return app_name + " via " + ua.getBrowser().name + " on " + ua.getOS().name; } + + screenCaptureErrorString() { + // 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; + } } From b71f96e4e5de520f1bbc7166b2bbd0ba1ffae4af Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 10:28:04 +0000 Subject: [PATCH 0030/1951] Use the role for 'toggle dev tools' Let electron handle it rather than doing it ourselves --- electron/src/vectormenu.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/electron/src/vectormenu.js b/electron/src/vectormenu.js index f4d55c15..70ed3ac3 100644 --- a/electron/src/vectormenu.js +++ b/electron/src/vectormenu.js @@ -72,11 +72,7 @@ const template = [ role: 'togglefullscreen' }, { - label: 'Toggle Developer Tools', - accelerator: process.platform == 'darwin' ? 'Alt+Command+I' : 'Ctrl+Shift+I', - click: function(item, focusedWindow) { - if (focusedWindow) focusedWindow.toggleDevTools(); - } + role: 'toggledevtools' } ] }, From 06718c580e49e4f506f01a1cf938889aaa6b371d Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 16:27:40 +0000 Subject: [PATCH 0031/1951] Hopefully fix Windows shortcuts * Revert https://github.com/vector-im/riot-web/commit/79d164309f7926cb1c24008e464a516d7a872325 as it seems to break shortcuts altogether * Update electron-builder (and add the squirrel windows package that the newer version now requires: it's been split out). This uses a newer version of squirrel which has some fixes for shortcuts. I'm unsure exactly what was going wrong originally in https://github.com/vector-im/riot-web/issues/2775 but #79d1643 seems to break shortcut creation as far as I can see. --- electron/src/squirrelhooks.js | 7 ++++++- package.json | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/electron/src/squirrelhooks.js b/electron/src/squirrelhooks.js index 295ef5cf..ca0983b6 100644 --- a/electron/src/squirrelhooks.js +++ b/electron/src/squirrelhooks.js @@ -3,7 +3,12 @@ const spawn = require('child_process').spawn; const app = require('electron').app; function run_update_exe(args, done) { - const updateExe = path.resolve(path.dirname(process.execPath), 'Update.exe'); + // Invokes Squirrel's Update.exe which will do things for us like create shortcuts + // Note that there's an Update.exe in the app-x.x.x directory and one in the parent + // directory: we need to run the one in the parent directory, because it discovers + // information about the app by inspecting the directory it's run from. + const updateExe = path.resolve(path.dirname(process.execPath), '..', 'Update.exe'); + console.log('Spawning `%s` with args `%s`', updateExe, args); spawn(updateExe, args, { detached: true }).on('close', done); diff --git a/package.json b/package.json index 52cc41be..b110b2af 100644 --- a/package.json +++ b/package.json @@ -94,7 +94,8 @@ "chokidar": "^1.6.1", "cpx": "^1.3.2", "css-raw-loader": "^0.1.1", - "electron-builder": "^10.4.1", + "electron-builder": "^11.2.4", + "electron-builder-squirrel-windows": "^11.2.1", "emojione": "^2.2.3", "expect": "^1.16.0", "fs-extra": "^0.30.0", From 97fc92b1bb01758294f304bc5ef687b307eec2ca Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 16:50:09 +0000 Subject: [PATCH 0032/1951] Update to electron 1.4.14 This pulls in the chrome change that broke geotrust ssl when the build hit 10 weeks old, so let's get this one out before we hit more disasters. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 52cc41be..59dfe1e8 100644 --- a/package.json +++ b/package.json @@ -126,7 +126,7 @@ "build": { "appId": "im.riot.app", "category": "Network", - "electronVersion": "1.4.11", + "electronVersion": "1.4.14", "//asar=false": "https://github.com/electron-userland/electron-builder/issues/675", "asar": false, "dereference": true, From 10f0631487589f7c0841cf049be719024150b43e Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 17:20:27 +0000 Subject: [PATCH 0033/1951] Build the js-sdk in the CI script now it's transpiled --- scripts/jenkins.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/jenkins.sh b/scripts/jenkins.sh index be8d8dee..bd27d6e3 100755 --- a/scripts/jenkins.sh +++ b/scripts/jenkins.sh @@ -19,7 +19,8 @@ tar -C olm -xz < olm/olm-*.tgz rm -r node_modules/olm cp -r olm/package node_modules/olm -# we may be using a dev branch of react-sdk, in which case we need to build it +# we may be using dev branches of js-sdk and react-sdk, in which case we need to build them +(cd node_modules/matrix-js-sdk && npm run build) (cd node_modules/matrix-react-sdk && npm run build) # run the mocha tests From 5258bf9c8455e97ce59e82150c51e31c0c681a44 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 17:25:58 +0000 Subject: [PATCH 0034/1951] Update README now the js-sdk has a transpile step --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f7754348..57cfd5cb 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,11 @@ to build. 1. Install the prerequisites: `npm install` 1. If you are using the `develop` branch of vector-web, you will probably need to rebuild one of the dependencies, due to - https://github.com/npm/npm/issues/3055: `(cd node_modules/matrix-react-sdk - && npm install)` + https://github.com/npm/npm/issues/3055: + ``` + (cd node_modules/matrix-js-sdk && npm install) + (cd node_modules/matrix-react-sdk && npm install) + ``` 1. Configure the app by copying `config.sample.json` to `config.json` and modifying it (see below for details) 1. `npm run dist` to build a tarball to deploy. Untaring this file will give @@ -241,10 +244,10 @@ Finally, build and start Riot itself: disables caching, so do NOT use it in production. 1. Open http://127.0.0.1:8080/ in your browser to see your newly built Riot. -When you make changes to `matrix-react-sdk`, you will need to run `npm run -build` in the relevant directory. You can do this automatically by instead -running `npm start` in the directory, to start a development builder which -will watch for changes to the files and rebuild automatically. +When you make changes to `matrix-react-sdk` or `matrix-js-sdk`, you will need +to run `npm run build` in the relevant directory. You can do this automatically +by instead running `npm start` in the directory, to start a development builder +which will watch for changes to the files and rebuild automatically. If you add or remove any components from the Riot skin, you will need to rebuild the skin's index by running, `npm run reskindex`. From 27e311f7e8dccb0f8e3774d77b612df3beb80529 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 18:15:06 +0000 Subject: [PATCH 0035/1951] Address PR comments --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 57cfd5cb..ba59ea26 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,9 @@ to build. 1. Switch to the vector-web directory: `cd vector-web` 1. Install the prerequisites: `npm install` 1. If you are using the `develop` branch of vector-web, you will probably need - to rebuild one of the dependencies, due to + to rebuild some of the dependencies, due to https://github.com/npm/npm/issues/3055: + ``` (cd node_modules/matrix-js-sdk && npm install) (cd node_modules/matrix-react-sdk && npm install) From f17f103d1287ce4390505a31abc13fd1484abf42 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 18:19:44 +0000 Subject: [PATCH 0036/1951] s/build/compile/ for js sdk --- scripts/jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins.sh b/scripts/jenkins.sh index bd27d6e3..6ba384e1 100755 --- a/scripts/jenkins.sh +++ b/scripts/jenkins.sh @@ -20,7 +20,7 @@ rm -r node_modules/olm cp -r olm/package node_modules/olm # we may be using dev branches of js-sdk and react-sdk, in which case we need to build them -(cd node_modules/matrix-js-sdk && npm run build) +(cd node_modules/matrix-js-sdk && npm run compile) (cd node_modules/matrix-react-sdk && npm run build) # run the mocha tests From 70f48343ee905f09be867207f5d8eed53f3d896d Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 11 Jan 2017 19:03:52 +0000 Subject: [PATCH 0037/1951] It's now just build --- scripts/jenkins.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins.sh b/scripts/jenkins.sh index 6ba384e1..bd27d6e3 100755 --- a/scripts/jenkins.sh +++ b/scripts/jenkins.sh @@ -20,7 +20,7 @@ rm -r node_modules/olm cp -r olm/package node_modules/olm # we may be using dev branches of js-sdk and react-sdk, in which case we need to build them -(cd node_modules/matrix-js-sdk && npm run compile) +(cd node_modules/matrix-js-sdk && npm run build) (cd node_modules/matrix-react-sdk && npm run build) # run the mocha tests From 0d05e607f067ccc0d6d6a6740bb004262a1ff727 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 12 Jan 2017 17:24:28 +0000 Subject: [PATCH 0038/1951] install js-sdk in travis too --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1fbc4dcc..af738bb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,5 @@ node_js: - 6 # node v6, to match jenkins install: - npm install + - (cd node_modules/matrix-js-sdk && npm install) - (cd node_modules/matrix-react-sdk && npm run build) From 40545bd48e45fa8f0b40841b89e6f20c1afa9599 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Jan 2017 11:15:55 +0000 Subject: [PATCH 0039/1951] Released react-sdk & js-sdk --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fd7c3b12..823dd04d 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,8 @@ "gfm.css": "^1.1.1", "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", - "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", - "matrix-react-sdk": "matrix-org/matrix-react-sdk#develop", + "matrix-js-sdk": "0.7.4-rc.1", + "matrix-react-sdk": "0.8.5-rc.1", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From 9471c30f89ded93656ac5bd3f29a296b77a8d894 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Jan 2017 11:21:08 +0000 Subject: [PATCH 0040/1951] Prepare changelog for v0.9.6-rc.1 --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 265cbe80..00772055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +Changes in [0.9.6-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.6-rc.1) (2017-01-13) +==================================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.5...v0.9.6-rc.1) + + * Build the js-sdk in the CI script + [\#2920](https://github.com/vector-im/riot-web/pull/2920) + * Hopefully fix Windows shortcuts + [\#2917](https://github.com/vector-im/riot-web/pull/2917) + * Update README now the js-sdk has a transpile step + [\#2921](https://github.com/vector-im/riot-web/pull/2921) + * Use the role for 'toggle dev tools' + [\#2915](https://github.com/vector-im/riot-web/pull/2915) + * Enable screen sharing easter-egg in desktop app + [\#2909](https://github.com/vector-im/riot-web/pull/2909) + * make electron send email validation URLs with a nextlink of riot.im + [\#2808](https://github.com/vector-im/riot-web/pull/2808) + * add Debian Stretch install steps to readme + [\#2809](https://github.com/vector-im/riot-web/pull/2809) + * Update desktop build instructions fixes #2792 + [\#2793](https://github.com/vector-im/riot-web/pull/2793) + * CSS for the delete threepid button + [\#2784](https://github.com/vector-im/riot-web/pull/2784) + Changes in [0.9.5](https://github.com/vector-im/riot-web/releases/tag/v0.9.5) (2016-12-24) ========================================================================================== [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.4...v0.9.5) From e1c1937855967e0a1da57d00728b091d9dbfdec0 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 13 Jan 2017 11:21:08 +0000 Subject: [PATCH 0041/1951] v0.9.6-rc.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 823dd04d..ab9e36c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "riot-web", "productName": "Riot", "main": "electron/src/electron-main.js", - "version": "0.9.5", + "version": "0.9.6-rc.1", "description": "A feature-rich client for Matrix.org", "author": "Vector Creations Ltd.", "repository": { From ccf7db7cc227b4d5a8996d485493b0b8f0c06cc2 Mon Sep 17 00:00:00 2001 From: Hubert Chathi Date: Fri, 13 Jan 2017 15:06:29 -0500 Subject: [PATCH 0042/1951] Fix a couple more references to vector-web in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f82572fb..1368589e 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,9 @@ to build. 1. Install or update `node.js` so that your `npm` is at least at version `2.0.0` 1. Clone the repo: `git clone https://github.com/vector-im/riot-web.git` -1. Switch to the vector-web directory: `cd vector-web` +1. Switch to the riot-web directory: `cd riot-web` 1. Install the prerequisites: `npm install` -1. If you are using the `develop` branch of vector-web, you will probably need +1. If you are using the `develop` branch of riot-web, you will probably need to rebuild one of the dependencies, due to https://github.com/npm/npm/issues/3055: `(cd node_modules/matrix-react-sdk && npm install)` From 5edb5f6487404b89da45f13f3d8444dd1f533d0f Mon Sep 17 00:00:00 2001 From: Jani Mustonen Date: Fri, 6 Jan 2017 01:37:12 +0200 Subject: [PATCH 0043/1951] Turned divs to button-likes to comply with MDN's recommendations --- src/components/structures/BottomLeftMenu.js | 17 ++++---- src/components/structures/RightPanel.js | 17 +++++--- src/components/structures/RoomSubList.js | 13 +++--- src/components/structures/SearchBox.js | 43 +++++++++++++++---- src/components/views/elements/ImageView.js | 3 +- src/components/views/globals/MatrixToolbar.js | 3 +- src/components/views/rooms/SearchBar.js | 11 ++--- 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/src/components/structures/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js index 0ea35b4e..1e352fa1 100644 --- a/src/components/structures/BottomLeftMenu.js +++ b/src/components/structures/BottomLeftMenu.js @@ -20,6 +20,7 @@ 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'); module.exports = React.createClass({ displayName: 'BottomLeftMenu', @@ -101,22 +102,22 @@ module.exports = React.createClass({ return (

    -
    + { this.getLabel("Start chat", this.state.peopleHover) } -
    -
    + + { this.getLabel("Room directory", this.state.directoryHover) } -
    -
    + + { this.getLabel("Create new room", this.state.roomsHover) } -
    -
    + + { this.getLabel("Settings", this.state.settingsHover) } -
    +
    ); diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index d1622e5f..7bd5d3b9 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -23,6 +23,7 @@ var dis = require('matrix-react-sdk/lib/dispatcher'); var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg"); var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc'); var Modal = require('matrix-react-sdk/lib/Modal'); +var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); module.exports = React.createClass({ displayName: 'RightPanel', @@ -207,12 +208,12 @@ module.exports = React.createClass({ if (user_is_in_room) { inviteGroup = -
    +
    Invite to this room
    -
    ; + ; } } @@ -220,20 +221,26 @@ module.exports = React.createClass({ if (this.props.roomId) { buttonGroup =
    -
    +
    +
    { membersBadge ? membersBadge :  }
    { membersHighlight } +
    -
    +
    +
     
    { filesHighlight } +
    -
    +
    +
     
    { notificationsHighlight } +
    ; } diff --git a/src/components/structures/RoomSubList.js b/src/components/structures/RoomSubList.js index e87f3f8a..af8500df 100644 --- a/src/components/structures/RoomSubList.js +++ b/src/components/structures/RoomSubList.js @@ -26,6 +26,7 @@ var Unread = require('matrix-react-sdk/lib/Unread'); 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'); // turn this on for drop & drag console debugging galore var debug = false; @@ -417,15 +418,17 @@ var RoomSubList = React.createClass({ } } + var tabindex = this.props.searchFilter === "" ? "0" : "-1"; + return (
    -
    + { this.props.collapsed ? '' : this.props.label }
    { roomCount }
    { badge } { incomingCall } -
    +
    ); }, @@ -447,11 +450,11 @@ var RoomSubList = React.createClass({ }); return ( -
    +
    more
    -
    { content }
    -
    +
    { content }
    + ); }, diff --git a/src/components/structures/SearchBox.js b/src/components/structures/SearchBox.js index 7fc51000..036ff3f1 100644 --- a/src/components/structures/SearchBox.js +++ b/src/components/structures/SearchBox.js @@ -20,6 +20,7 @@ var React = require('react'); 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'); module.exports = React.createClass({ displayName: 'SearchBox', @@ -35,6 +36,25 @@ module.exports = React.createClass({ }; }, + componentDidMount: function() { + this.dispatcherRef = dis.register(this.onAction); + }, + + componentWillUnmount: function() { + dis.unregister(this.dispatcherRef); + }, + + onAction: function(payload) { + switch (payload.action) { + // Clear up the text field when a room is selected. + case 'view_room': + if (this.refs.search) { + this._clearSearch(); + } + break; + } + }, + onChange: function() { if (!this.refs.search) return; this.setState({ searchTerm: this.refs.search.value }); @@ -61,35 +81,42 @@ module.exports = React.createClass({ } }, + _clearSearch: function() { + this.refs.search.value = ""; + this.onChange(); + }, + render: function() { var TintableSvg = sdk.getComponent('elements.TintableSvg'); + var collapseTabIndex = this.refs.search && this.refs.search.value !== "" ? "-1" : "0"; + var toggleCollapse; if (this.props.collapsed) { toggleCollapse = -
    + -
    + } else { toggleCollapse = -
    + -
    + } var searchControls; if (!this.props.collapsed) { searchControls = [ this.state.searchTerm.length > 0 ? -
    { this.refs.search.value = ""; this.onChange(); } }> + { this._clearSearch(); } }> -
    + :
    - Close + Close
    diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js index a22e15ff..dbe4196a 100644 --- a/src/components/views/globals/MatrixToolbar.js +++ b/src/components/views/globals/MatrixToolbar.js @@ -19,6 +19,7 @@ limitations under the License. var React = require('react'); var Notifier = require("matrix-react-sdk/lib/Notifier"); var sdk = require('matrix-react-sdk') +var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); module.exports = React.createClass({ displayName: 'MatrixToolbar', @@ -38,7 +39,7 @@ module.exports = React.createClass({
    You are not receiving desktop notifications. Enable them now
    -
    +
    ); } diff --git a/src/components/views/rooms/SearchBar.js b/src/components/views/rooms/SearchBar.js index 99e77064..1653f269 100644 --- a/src/components/views/rooms/SearchBar.js +++ b/src/components/views/rooms/SearchBar.js @@ -20,6 +20,7 @@ var React = require('react'); var MatrixClientPeg = require('matrix-react-sdk/lib/MatrixClientPeg'); var sdk = require('matrix-react-sdk'); var classNames = require('classnames'); +var AccessibleButton = require('matrix-react-sdk/lib/components/views/elements/AccessibleButton'); module.exports = React.createClass({ displayName: 'SearchBar', @@ -57,12 +58,12 @@ module.exports = React.createClass({ var allRoomsClasses = classNames({ mx_SearchBar_button : true, mx_SearchBar_unselected : this.state.scope !== 'All' }); return ( -
    +
    -
    Search
    -
    This Room
    -
    All Rooms
    - + Search + This Room + All Rooms +
    ); } From 002339fb86ffcd128ba90a0508d85e22a94255fa Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 02:10:45 +0000 Subject: [PATCH 0044/1951] factor out some more colours --- src/skins/vector/css/_common.scss | 4 +-- .../structures/_UserSettings.scss | 6 ++--- .../structures/login/_Login.scss | 2 +- .../views/elements/_DirectorySearchBox.scss | 2 +- .../views/elements/_ProgressBar.scss | 4 +-- .../views/rooms/_MemberInfo.scss | 2 +- .../views/rooms/_MemberList.scss | 2 +- .../views/rooms/_RoomSettings.scss | 2 +- src/skins/vector/css/themes/_base.scss | 26 +++++++++++++++---- src/skins/vector/css/themes/_dark.scss | 20 +++++++++++++- .../vector-web/structures/_RoomDirectory.scss | 2 +- .../views/rooms/_RoomDropTarget.scss | 2 +- 12 files changed, 54 insertions(+), 20 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index ed96b48c..9e2ac1f4 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -174,13 +174,13 @@ textarea { left: 0; width: 100%; height: 100%; - background-color: #e9e9e9; + background-color: $dialog-background-bg-color; opacity: 0.8; } .mx_Dialog_lightbox .mx_Dialog_background { opacity: 0.85; - background-color: #000; + background-color: $lightbox-background-bg-color; } .mx_Dialog_lightbox .mx_Dialog { 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 6ba1002b..1379063d 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -58,7 +58,7 @@ limitations under the License. clear: both; margin-left: 63px; text-transform: uppercase; - color: #3d3b39; + color: $h3-color; font-weight: 600; font-size: 13px; margin-top: 26px; @@ -166,10 +166,10 @@ limitations under the License. { display: inline-block; border: 0px; - border-bottom: 1px solid rgba(151, 151, 151, 0.5); + border-bottom: 1px solid $input-underline-color; padding: 0px; width: 240px; - color: rgba(74, 74, 74, 0.9); + color: $input-fg-color; font-family: 'Open Sans', Helvetica, Arial, Sans-Serif; font-size: 16px; } 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 5f4164e8..75dc7180 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 @@ -147,7 +147,7 @@ limitations under the License. } .mx_Login_error { - color: #ff2020; + color: $warning-color; font-weight: bold; text-align: center; /* diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss index dd953ab9..66063733 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss @@ -44,7 +44,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { padding: 3px; padding-left: 10px; padding-right: 10px; - background-color: #efefef; + background-color: $plinth-bg-color; border-radius: 3px; background-image: url('img/icon-return.svg'); background-position: 8px 70%; diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss index 7b5e0c74..a3fee232 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_ProgressBar.scss @@ -16,10 +16,10 @@ limitations under the License. .mx_ProgressBar { height: 5px; - border: 1px solid black; + border: 1px solid $progressbar-color; } .mx_ProgressBar_fill { height: 100%; - background-color: #000; + background-color: $progressbar-color; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss index 3b4b653e..970c2496 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss @@ -61,7 +61,7 @@ limitations under the License. .mx_MemberInfo h3 { text-transform: uppercase; - color: #3d3b39; + color: $h3-color; font-weight: 600; font-size: 13px; margin-top: 16px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss index 3c7e3ec5..8a6f1172 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss @@ -96,7 +96,7 @@ limitations under the License. .mx_MemberList_invited h2 { text-transform: uppercase; - color: #3d3b39; + color: $h3-color; font-weight: 600; font-size: 13px; padding-left: 3px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss index 132c9792..d9de0e8a 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss @@ -81,7 +81,7 @@ limitations under the License. .mx_RoomSettings h3 { text-transform: uppercase; - color: #3d3b39; + color: $h3-color; font-weight: 600; font-size: 13px; margin-top: 36px; diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index b9fd2d12..e623372a 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -1,5 +1,4 @@ - // typical text (dark-on-white in light skin) $primary-fg-color: #454545; $primary-bg-color: #ffffff; @@ -20,6 +19,12 @@ $warning-color: #ff0064; // left-panel style muted accent color $secondary-accent-color: #eaf5f0; +// used by RoomDirectory permissions +$plinth-bg-color: $secondary-accent-color; + +// used by RoomDropTarget +$droptarget-bg-color: rgba(255,255,255,0.5); + // used by AddressSelector $selected-color: #eaf5f0; @@ -35,18 +40,27 @@ $input-border-color: #f0f0f0; // apart from login forms, which have stronger border $strong-input-border-color: #c7c7c7; +// used for UserSettings EditableText +$input-underline-color: rgba(151, 151, 151, 0.5); +$input-fg-color: rgba(74, 74, 74, 0.9); + // context menus $menu-border-color: rgba(187, 187, 187, 0.5); $menu-bg-color: #f6f6f6; $avatar-initial-color: #ffffff; +$h3-color: #3d3b39; + +$dialog-background-bg-color: #e9e9e9; +$lightbox-background-bg-color: #000; + // ******************** $roomtile-name-color: rgba(69, 69, 69, 0.8); $roomtile-selected-bg-color: rgba(255, 255, 255, 0.8); -$roomsublist-label-fg-color: #3d3b39; +$roomsublist-label-fg-color: $h3-color; $roomsublist-label-bg-color: #d3efe1; // ******************** @@ -64,8 +78,10 @@ $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-unverified-color: #e8bf37; $e2e-warning-color: #ba6363; -// ******************** - +/*** ImageView ***/ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; \ No newline at end of file +$lightbox-border-color: #ffffff; + +// unused? +$progressbar-color: #000; \ No newline at end of file diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index e2c33e3e..1d93c1a4 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -19,6 +19,12 @@ $warning-color: #ff0064; // left-panel style muted accent color $secondary-accent-color: $primary-bg-color; +// used by RoomDirectory permissions +$plinth-bg-color: #474747; + +// used by RoomDropTarget +$droptarget-bg-color: rgba(45,45,45,0.5); + // used by AddressSelector $selected-color: #eaf5f0; @@ -34,18 +40,27 @@ $input-border-color: #3a3a3a; // apart from login forms, which have stronger border $strong-input-border-color: #656565; +// used for UserSettings EditableText +$input-underline-color: $primary-fg-color; +$input-fg-color: $primary-fg-color; + // context menus $menu-border-color: rgba(187, 187, 187, 0.5); $menu-bg-color: #373737; $avatar-initial-color: #2d2d2d; +$h3-color: $primary-fg-color; + +$dialog-background-bg-color: #000; +$lightbox-background-bg-color: #000; + // ******************** $roomtile-name-color: rgba(186, 186, 186, 0.8); $roomtile-selected-bg-color: rgba(0, 0, 0, 0.8); -$roomsublist-label-fg-color: $primary-fg-color; +$roomsublist-label-fg-color: $h3-color; $roomsublist-label-bg-color: #454545; // ******************** @@ -67,3 +82,6 @@ $e2e-warning-color: #ba6363; $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; + +// unused? +$progressbar-color: #000; \ No newline at end of file diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index e5df8741..3dd885ba 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -110,7 +110,7 @@ limitations under the License. padding-right: 5px; height: 15px; border-radius: 11px; - background-color: $secondary-accent-color; + background-color: $plinth-bg-color; text-transform: uppercase; font-weight: 600; font-size: 11px; diff --git a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss index 2fe16ca9..e91658e8 100644 --- a/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss +++ b/src/skins/vector/css/vector-web/views/rooms/_RoomDropTarget.scss @@ -24,7 +24,7 @@ limitations under the License. padding-bottom: 5px; border: 1px dashed $accent-color; color: $primary-fg-color; - background-color: rgba(255,255,255,0.5); + background-color: $droptarget-bg-color; border-radius: 4px; } From 231306ea7ce629fe0e58cab62f038d2b6e271dc7 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Jan 2017 13:24:44 +0000 Subject: [PATCH 0045/1951] Update js-sdk & react-sdk --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ab9e36c5..7b2916e0 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,8 @@ "gfm.css": "^1.1.1", "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", - "matrix-js-sdk": "0.7.4-rc.1", - "matrix-react-sdk": "0.8.5-rc.1", + "matrix-js-sdk": "0.7.4", + "matrix-react-sdk": "0.8.5", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From 3f3a31e475598237d0a44bd53746a06a48965bda Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Jan 2017 13:26:21 +0000 Subject: [PATCH 0046/1951] Prepare changelog for v0.9.6 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00772055..ee745baa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [0.9.6](https://github.com/vector-im/riot-web/releases/tag/v0.9.6) (2017-01-16) +========================================================================================== +[Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.6-rc.1...v0.9.6) + + * Update to matrix-js-sdk 0.9.6 for video calling fix + Changes in [0.9.6-rc.1](https://github.com/vector-im/riot-web/releases/tag/v0.9.6-rc.1) (2017-01-13) ==================================================================================================== [Full Changelog](https://github.com/vector-im/riot-web/compare/v0.9.5...v0.9.6-rc.1) From 22060ac5a44b9d4fa66b8ffd74690588ca252297 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Jan 2017 13:26:22 +0000 Subject: [PATCH 0047/1951] v0.9.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b2916e0..f169cf9b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "riot-web", "productName": "Riot", "main": "electron/src/electron-main.js", - "version": "0.9.6-rc.1", + "version": "0.9.6", "description": "A feature-rich client for Matrix.org", "author": "Vector Creations Ltd.", "repository": { From ae7820e8f28552ca1a19fe7612a6d1c02c5b633f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 16 Jan 2017 13:37:18 +0000 Subject: [PATCH 0048/1951] Fix merge --- package.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/package.json b/package.json index 054dedf9..f169cf9b 100644 --- a/package.json +++ b/package.json @@ -62,13 +62,8 @@ "gfm.css": "^1.1.1", "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", -<<<<<<< HEAD - "matrix-js-sdk": "0.7.2", - "matrix-react-sdk": "0.8.4", -======= "matrix-js-sdk": "0.7.4", "matrix-react-sdk": "0.8.5", ->>>>>>> release-v0.9.6 "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From b3bff92cc2cbe0f8818f2ae1563335671d0a2733 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 16 Jan 2017 18:01:25 +0000 Subject: [PATCH 0049/1951] Put parent build hash in webpack output filenames In order to better support a world where old build artifacts are available (which is necessary to support bundle.js splitting), collect all the webpack artifacts for the build into a single directory. Then we'll be able to clear out old builds after a few weeks, knowing they won't be in use any more. --- webpack.config.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 3500fedc..9c0c6bbb 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -40,7 +40,8 @@ module.exports = { }, output: { path: path.join(__dirname, "webapp"), - filename: "[name].[chunkhash].js", + filename: "[hash]/[name].js", + chunkFilename: "[hash]/[name].js", devtoolModuleFilenameTemplate: function(info) { // Reading input source maps gives only relative paths here for // everything. Until I figure out how to fix this, this is a @@ -79,7 +80,7 @@ module.exports = { }), new ExtractTextPlugin( - "[name].[contenthash].css", + "[hash]/[name].css", { allChunks: true } From 906d42688eb153a10e0e8f96701af1ebf4897ee0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 18:40:29 +0000 Subject: [PATCH 0050/1951] make autoprefixer work by reordering it --- postcss.config.json | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/postcss.config.json b/postcss.config.json index be3585cf..7ed32cda 100644 --- a/postcss.config.json +++ b/postcss.config.json @@ -1,7 +1,7 @@ { "use": [ - "autoprefixer", "postcss-import", + "autoprefixer", "postcss-simple-vars", "postcss-extend", "postcss-nested", @@ -11,8 +11,5 @@ "parser": "postcss-scss", "input": "src/skins/vector/css/themes/[^_]*.scss", "dir": "build", - "local-plugins": true, - "autoprefixer": { - "browsers": "> 5%" - } -} \ No newline at end of file + "local-plugins": true +} From dc1563d916900a1ad66af358edc26f02d25a1c8f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 18:55:29 +0000 Subject: [PATCH 0051/1951] strip out unneeded webkit prefixes now we're using autoprefixer --- src/skins/vector/css/_common.scss | 5 ---- .../structures/_FilePanel.scss | 3 -- .../structures/_MatrixChat.scss | 25 ---------------- .../structures/_NotificationPanel.scss | 3 -- .../structures/_RoomStatusBar.scss | 3 -- .../structures/_RoomView.scss | 29 ------------------- .../structures/_SearchBox.scss | 2 -- .../structures/_UserSettings.scss | 9 ------ .../structures/login/_Login.scss | 4 --- .../views/elements/_DirectorySearchBox.scss | 2 -- .../views/rooms/_LinkPreviewWidget.scss | 4 --- .../views/rooms/_MemberList.scss | 8 ----- .../views/rooms/_MessageComposer.scss | 1 - .../views/rooms/_RoomHeader.scss | 15 ---------- .../views/rooms/_RoomPreviewBar.scss | 7 ----- .../views/rooms/_SearchableEntityList.scss | 4 --- .../views/voip/_IncomingCallbox.scss | 2 -- .../css/vector-web/structures/_LeftPanel.scss | 9 ------ .../vector-web/structures/_RightPanel.scss | 12 -------- .../vector-web/structures/_RoomDirectory.scss | 9 ------ .../vector-web/views/elements/_ImageView.scss | 14 --------- .../vector-web/views/elements/_Spinner.scss | 4 --- .../views/globals/_GuestWarningBar.scss | 3 -- .../views/globals/_MatrixToolbar.scss | 4 --- .../vector-web/views/rooms/_SearchBar.scss | 3 -- 25 files changed, 184 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 9e2ac1f4..6a623855 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -83,7 +83,6 @@ textarea { /* applied to side-panels and messagepanel when in RoomSettings */ .mx_fadable { opacity: 1; - -webkit-transition: opacity 0.2s ease-in-out; -moz-transition: opacity 0.2s ease-in-out; -ms-transition: opacity 0.2s ease-in-out; -o-transition: opacity 0.2s ease-in-out; @@ -128,14 +127,10 @@ textarea { width: 100%; height: 100%; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; - -webkit-justify-content: center; justify-content: center; } diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss index 2f4a00ad..6c769a3b 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss @@ -15,13 +15,10 @@ limitations under the License. */ .mx_FilePanel { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; - -webkit-flex: 1 1 0; flex: 1 1 0; width: 100%; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss index 5587a609..61eb0170 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss @@ -27,34 +27,27 @@ limitations under the License. } .mx_MatrixChat_wrapper { - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; width: 100%; height: 100%; } .mx_MatrixToolbar { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; height: 40px; } .mx_GuestWarningBar { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; height: 40px; @@ -68,52 +61,40 @@ limitations under the License. width: 100%; height: 100%; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; - -webkit-flex: 1; flex: 1; } .mx_MatrixChat .mx_LeftPanel { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; background-color: $secondary-accent-color; - -webkit-flex: 0 0 235px; flex: 0 0 235px; } .mx_MatrixChat .mx_LeftPanel.collapsed { - -webkit-flex: 0 0 60px; flex: 0 0 60px; } .mx_MatrixChat .mx_MatrixChat_middlePanel { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; padding-left: 20px; padding-right: 22px; background-color: $primary-bg-color; - -webkit-flex: 1; flex: 1; /* Experimental fix for https://github.com/vector-im/vector-web/issues/947 @@ -132,25 +113,19 @@ limitations under the License. * point, but instead we fudge it and make the middlePanel * flex itself. */ - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; } .mx_MatrixChat .mx_RightPanel { - -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; - -webkit-order: 3; order: 3; - -webkit-flex: 0 0 235px; flex: 0 0 235px; } .mx_MatrixChat .mx_RightPanel.collapsed { - -webkit-flex: 0 0 122px; flex: 0 0 122px; } diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss index 06dd92f3..ed818497 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss @@ -15,13 +15,10 @@ limitations under the License. */ .mx_NotificationPanel { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; - -webkit-flex: 1 1 0; flex: 1 1 0; width: 100%; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index d0ac5a60..5daac88f 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -123,18 +123,15 @@ limitations under the License. .mx_RoomStatusBar_tabCompleteWrapper { display: flex; - display: -webkit-flex; height: 26px; } .mx_RoomStatusBar_tabCompleteWrapper .mx_TabCompleteBar { flex: 1 1 auto; - -webkit-flex: 1 1 auto; } .mx_RoomStatusBar_tabCompleteEol { flex: 0 0 auto; - -webkit-flex: 0 0 auto; 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 182d690c..dc668236 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -18,25 +18,19 @@ limitations under the License. word-wrap: break-word; position: relative; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; width: 100%; flex-direction: column; - -webkit-flex-direction: column; } .mx_RoomView .mx_RoomHeader { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; - -webkit-flex: 0 0 70px; flex: 0 0 70px; } @@ -53,8 +47,6 @@ limitations under the License. padding-right: 12px; margin-left: -12px; - -webkit-border-top-left-radius: 10px; - -webkit-border-top-right-radius: 10px; -moz-border-radius-topleft: 10px; -moz-border-radius-topright: 10px; border-top-left-radius: 10px; @@ -77,10 +69,8 @@ limitations under the License. } .mx_RoomView_auxPanel { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; min-width: 0px; @@ -91,26 +81,20 @@ limitations under the License. overflow: auto; border-bottom: 1px solid $primary-hairline-color; - -webkit-flex: 0 0 auto; flex: 0 0 auto; } .mx_RoomView_topUnreadMessagesBar { - -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; - -webkit-order: 3; order: 3; } .mx_RoomView_messagePanel { - -webkit-box-ordinal-group: 4; -moz-box-ordinal-group: 4; -ms-flex-order: 4; - -webkit-order: 4; order: 4; - -webkit-flex: 1 1 0; flex: 1 1 0; width: 100%; @@ -124,22 +108,17 @@ limitations under the License. min-height: 100%; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; justify-content: flex-end; - -webkit-justify-content: flex-end; } .mx_RoomView_searchResultsPanel .mx_RoomView_messageListWrapper { justify-content: flex-start; - -webkit-justify-content: flex-start; } .mx_RoomView_MessageList { @@ -162,10 +141,8 @@ limitations under the License. } .mx_RoomView_invitePrompt { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; min-width: 0px; @@ -193,14 +170,11 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView_statusArea { - -webkit-box-ordinal-group: 5; -moz-box-ordinal-group: 5; -ms-flex-order: 5; - -webkit-order: 5; order: 5; width: 100%; - -webkit-flex: 0 0 auto; flex: 0 0 auto; } @@ -257,14 +231,11 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView .mx_MessageComposer { - -webkit-box-ordinal-group: 6; -moz-box-ordinal-group: 6; -ms-flex-order: 6; - -webkit-order: 6; order: 6; width: 100%; - -webkit-flex: 0 0 auto; flex: 0 0 auto; margin-right: 2px; } diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss index 0b536259..bd335f60 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss @@ -22,7 +22,6 @@ limitations under the License. padding-bottom: 22px; display: flex; - display: -webkit-flex; } .mx_SearchBox_searchButton { @@ -38,7 +37,6 @@ limitations under the License. .mx_SearchBox_search { flex: 1 1 auto; - -webkit-flex: 1 1 auto; width: 0px; font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; font-size: 12px; 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 1379063d..2c62a4ec 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -20,34 +20,25 @@ limitations under the License. margin-left: auto; margin-right: auto; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_UserSettings .mx_RoomHeader { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; - -webkit-flex: 0 0 70px; flex: 0 0 70px; } .mx_UserSettings_body { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; - -webkit-flex: 1 1 0; flex: 1 1 0; margin-top: -20px; 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 75dc7180..332f313f 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 @@ -18,14 +18,10 @@ limitations under the License. width: 100%; height: 100%; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; - -webkit-justify-content: center; justify-content: center; overflow: auto; diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss index 66063733..8824c659 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/_DirectorySearchBox.scss @@ -22,14 +22,12 @@ limitations under the License. .mx_DirectorySearchBox_container { display: flex; - display: -webkit-flex; padding-left: 9px; padding-right: 9px; } .mx_DirectorySearchBox_input { flex-grow: 1; - -webkit-flex-grow: 1; border: 0; padding: 0; font-weight: 300; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss index 2e2d9f80..0e911541 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss @@ -18,14 +18,12 @@ limitations under the License. margin-top: 15px; margin-right: 15px; margin-bottom: 15px; - display: -webkit-flex; display: flex; border-left: 4px solid #ddd; color: #888; } .mx_LinkPreviewWidget_image { - -webkit-flex: 0 0 100px; flex: 0 0 100px; margin-left: 15px; text-align: center; @@ -34,7 +32,6 @@ limitations under the License. .mx_LinkPreviewWidget_caption { margin-left: 15px; - -webkit-flex: 1 1 auto; flex: 1 1 auto; } @@ -56,7 +53,6 @@ limitations under the License. .mx_LinkPreviewWidget_cancel { visibility: hidden; cursor: pointer; - -webkit-flex: 0 0 40px; flex: 0 0 40px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss index 8a6f1172..403de7f9 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss @@ -20,22 +20,17 @@ limitations under the License. margin-top: 12px; margin-right: 20px; - -webkit-flex: 1; flex: 1; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_MemberList .mx_Spinner { flex: 0; - -webkit-flex: 0; } .mx_MemberList_chevron { @@ -48,7 +43,6 @@ limitations under the License. overflow-y: auto; order: 1; - -webkit-flex: 1 1 0; flex: 1 1 0px; } @@ -80,7 +74,6 @@ limitations under the License. .mx_MemberList_joined { order: 2; flex: 1 0 0; - -webkit-flex: 1 0 0; overflow-y: auto; } @@ -89,7 +82,6 @@ limitations under the License. .mx_MemberList_invited { order: 3; flex: 0 0 100px; - -webkit-flex: 0 0 100px; overflow-y: auto; } */ diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss index e5ffd220..a4549c0f 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss @@ -105,7 +105,6 @@ limitations under the License. border: 0px; resize: none; outline: none; - -webkit-box-shadow: none; -moz-box-shadow: none; box-shadow: none; color: $primary-fg-color; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss index a26f12ee..73327ea6 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss @@ -25,36 +25,28 @@ limitations under the License. margin: auto; height: 70px; - -webkit-align-items: center; align-items: center; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; } .mx_RoomHeader_leftRow { margin-left: -2px; - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; - -webkit-flex: 1; flex: 1; } .mx_RoomHeader_spinner { height: 36px; - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; padding-left: 12px; @@ -71,16 +63,13 @@ limitations under the License. margin-top: -2px; text-align: center; - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; cursor: pointer; /* - -webkit-flex: 0 0 90px; flex: 0 0 90px; */ padding-left: 12px; @@ -88,10 +77,8 @@ limitations under the License. } .mx_RoomHeader_cancelButton { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; cursor: pointer; @@ -105,10 +92,8 @@ limitations under the License. background-color: $primary-bg-color; display: flex; align-items: center; - -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; - -webkit-order: 3; order: 3; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss index 6b71f96d..0d030ad7 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss @@ -18,19 +18,14 @@ limitations under the License. text-align: center; height: 176px; - -webkit-align-items: center; align-items: center; flex-direction: column; - -webkit-flex-direction: column; justify-content: center; - -webkit-justify-content: center; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; } @@ -56,9 +51,7 @@ limitations under the License. } .mx_RoomPreviewBar_warning { - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; padding: 8px; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss index 9a24868d..b404da02 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss @@ -16,10 +16,8 @@ limitations under the License. .mx_SearchableEntityList { display: flex; - display: -webkit-flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_SearchableEntityList_query { @@ -49,7 +47,6 @@ limitations under the License. .mx_SearchableEntityList_listWrapper { flex: 1; - -webkit-flex: 1; overflow-y: auto; } @@ -67,7 +64,6 @@ limitations under the License. .mx_SearchableEntityList_hrWrapper { width: 100%; flex: 0 0 auto; - -webkit-flex: 0 0 auto; } .mx_SearchableEntityList hr { diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss index 97a82a03..d45c4a58 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss @@ -41,14 +41,12 @@ limitations under the License. .mx_IncomingCallBox_buttons { display: flex; - display: -webkit-flex; } .mx_IncomingCallBox_buttons_cell { vertical-align: middle; padding: 6px; flex: 1; - -webkit-flex: 1; } .mx_IncomingCallBox_buttons_decline, diff --git a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss index 4e328171..36a1e298 100644 --- a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss @@ -17,13 +17,10 @@ limitations under the License. .mx_LeftPanel { position: relative; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_LeftPanel_hideButton { @@ -39,13 +36,10 @@ limitations under the License. } .mx_LeftPanel .mx_RoomList_scrollbar { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; - -webkit-flex: 1 1 0; flex: 1 1 0; overflow-y: auto; @@ -57,16 +51,13 @@ limitations under the License. } .mx_LeftPanel .mx_BottomLeftMenu { - -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; - -webkit-order: 3; order: 3; border-top: 1px solid rgba(118, 207, 166, 0.2); margin-left: 16px; /* gutter */ margin-right: 16px; /* gutter */ - -webkit-flex: 0 0 60px; flex: 0 0 60px; z-index: 1; } diff --git a/src/skins/vector/css/vector-web/structures/_RightPanel.scss b/src/skins/vector/css/vector-web/structures/_RightPanel.scss index b9dbf1cd..000d36b3 100644 --- a/src/skins/vector/css/vector-web/structures/_RightPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_RightPanel.scss @@ -17,26 +17,20 @@ limitations under the License. .mx_RightPanel { position: relative; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_RightPanel_header { - -webkit-box-ordinal-group: 1; -moz-box-ordinal-group: 1; -ms-flex-order: 1; - -webkit-order: 1; order: 1; border-bottom: 1px solid $primary-hairline-color; margin-right: 20px; - -webkit-flex: 0 0 70px; flex: 0 0 70px; } @@ -81,26 +75,20 @@ limitations under the License. .mx_RightPanel .mx_MemberList, .mx_RightPanel .mx_MemberInfo, .mx_RightPanel_blank { - -webkit-box-ordinal-group: 2; -moz-box-ordinal-group: 2; -ms-flex-order: 2; - -webkit-order: 2; order: 2; flex: 1 1 0; - -webkit-flex: 1 1 0; } .mx_RightPanel_footer { - -webkit-box-ordinal-group: 3; -moz-box-ordinal-group: 3; -ms-flex-order: 3; - -webkit-order: 3; order: 3; border-top: 1px solid $primary-hairline-color; margin-right: 20px; - -webkit-flex: 0 0 60px; flex: 0 0 60px; } diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index 3dd885ba..c80a8d8f 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -22,33 +22,25 @@ limitations under the License. margin-bottom: 12px; color: $primary-fg-color; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_RoomDirectory_list { - -webkit-flex: 1; flex: 1; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; flex-direction: column; - -webkit-flex-direction: column; } .mx_RoomDirectory_list .mx_RoomView_messageListWrapper { justify-content: flex-start; - -webkit-justify-content: flex-start; } .mx_RoomDirectory_listheader { @@ -72,7 +64,6 @@ limitations under the License. .mx_RoomDirectory_tableWrapper { overflow-y: auto; - -webkit-flex: 1 1 0; flex: 1 1 0; } diff --git a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss index d66d9c7d..d31a9d27 100644 --- a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss +++ b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss @@ -19,18 +19,14 @@ limitations under the License. */ .mx_ImageView { - display: -webkit-flex; display: flex; width: 100%; height: 100%; - -webkit-align-items: center; align-items: center; } .mx_ImageView_lhs { - -webkit-box-ordinal-group: 1; order: 1; - -webkit-flex: 1; flex: 1 1 10%; min-width: 60px; /* @@ -40,18 +36,13 @@ limitations under the License. } .mx_ImageView_content { - -webkit-box-ordinal-group: 2; order: 2; /* min-width hack needed for FF */ min-width: 0px; height: 90%; - -webkit-flex: 15; flex: 15 15 0; - display: -webkit-flex; display: flex; - -webkit-align-items: center; - -webkit-justify-content: center; align-items: center; justify-content: center; } @@ -78,11 +69,8 @@ limitations under the License. .mx_ImageView_label { text-align: left; display: flex; - display: -webkit-flex; justify-content: center; - -webkit-justify-content: center; flex-direction: column; - -webkit-flex-direction: column; padding-left: 30px; padding-right: 30px; min-height: 100%; @@ -141,9 +129,7 @@ limitations under the License. } .mx_ImageView_rhs { - -webkit-box-ordinal-group: 3; order: 3; - -webkit-flex: 1; flex: 1 1 10%; min-width: 300px; /* diff --git a/src/skins/vector/css/vector-web/views/elements/_Spinner.scss b/src/skins/vector/css/vector-web/views/elements/_Spinner.scss index 3831cc4c..aea57379 100644 --- a/src/skins/vector/css/vector-web/views/elements/_Spinner.scss +++ b/src/skins/vector/css/vector-web/views/elements/_Spinner.scss @@ -15,16 +15,12 @@ limitations under the License. */ .mx_Spinner { - display: -webkit-flex; display: flex; - -webkit-align-items: center; - -webkit-justify-content: center; align-items: center; justify-content: center; width: 100%; height: 100%; flex: 1; - -webkit-flex: 1; } .mx_MatrixChat_middlePanel .mx_Spinner { diff --git a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss index e8b5aebb..2141f3a8 100644 --- a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss @@ -18,12 +18,9 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; } diff --git a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss index 9bd70bb9..9e21bcb1 100644 --- a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss @@ -18,12 +18,9 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -webkit-box; display: -moz-box; display: -ms-flexbox; - display: -webkit-flex; display: flex; - -webkit-align-items: center; align-items: center; } @@ -34,7 +31,6 @@ limitations under the License. } .mx_MatrixToolbar_content { - -webkit-flex: 1; flex: 1; } diff --git a/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss b/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss index 5d195322..079ea16c 100644 --- a/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss +++ b/src/skins/vector/css/vector-web/views/rooms/_SearchBar.scss @@ -18,9 +18,7 @@ limitations under the License. padding-top: 5px; padding-bottom: 5px; display: flex; - display: -webkit-flex; align-items: center; - -webkit-align-items: center; } .mx_SearchBar_input { @@ -32,7 +30,6 @@ limitations under the License. padding-left: 11px; width: auto; flex: 1 1 0; - -webkit-flex: 1 1 0; } .mx_SearchBar_searchButton { From c305b72b918c08661d0b068121011e4ab7f5ae8f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 18:58:02 +0000 Subject: [PATCH 0052/1951] strip out unneeded -moz- prefixes --- src/skins/vector/css/_common.scss | 2 -- .../css/matrix-react-sdk/structures/_FilePanel.scss | 1 - .../css/matrix-react-sdk/structures/_MatrixChat.scss | 9 --------- .../structures/_NotificationPanel.scss | 1 - .../css/matrix-react-sdk/structures/_RoomView.scss | 11 ----------- .../matrix-react-sdk/structures/_UserSettings.scss | 3 --- .../css/matrix-react-sdk/structures/login/_Login.scss | 1 - .../css/matrix-react-sdk/views/rooms/_MemberList.scss | 1 - .../views/rooms/_MessageComposer.scss | 1 - .../css/matrix-react-sdk/views/rooms/_RoomHeader.scss | 6 ------ .../matrix-react-sdk/views/rooms/_RoomPreviewBar.scss | 1 - .../vector/css/vector-web/structures/_LeftPanel.scss | 3 --- .../vector/css/vector-web/structures/_RightPanel.scss | 4 ---- .../css/vector-web/structures/_RoomDirectory.scss | 2 -- .../vector-web/views/globals/_GuestWarningBar.scss | 1 - .../css/vector-web/views/globals/_MatrixToolbar.scss | 1 - 16 files changed, 48 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 6a623855..5e0b902a 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -83,7 +83,6 @@ textarea { /* applied to side-panels and messagepanel when in RoomSettings */ .mx_fadable { opacity: 1; - -moz-transition: opacity 0.2s ease-in-out; -ms-transition: opacity 0.2s ease-in-out; -o-transition: opacity 0.2s ease-in-out; } @@ -127,7 +126,6 @@ textarea { width: 100%; height: 100%; - display: -moz-box; display: -ms-flexbox; display: flex; align-items: center; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss index 6c769a3b..2fb1131d 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss @@ -15,7 +15,6 @@ limitations under the License. */ .mx_FilePanel { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss index 61eb0170..72f1feff 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss @@ -27,7 +27,6 @@ limitations under the License. } .mx_MatrixChat_wrapper { - display: -moz-box; display: -ms-flexbox; display: flex; @@ -38,7 +37,6 @@ limitations under the License. } .mx_MatrixToolbar { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -46,7 +44,6 @@ limitations under the License. } .mx_GuestWarningBar { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -61,11 +58,9 @@ limitations under the License. width: 100%; height: 100%; - display: -moz-box; display: -ms-flexbox; display: flex; - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -73,7 +68,6 @@ limitations under the License. } .mx_MatrixChat .mx_LeftPanel { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -87,7 +81,6 @@ limitations under the License. } .mx_MatrixChat .mx_MatrixChat_middlePanel { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -113,13 +106,11 @@ limitations under the License. * point, but instead we fudge it and make the middlePanel * flex itself. */ - display: -moz-box; display: -ms-flexbox; display: flex; } .mx_MatrixChat .mx_RightPanel { - -moz-box-ordinal-group: 3; -ms-flex-order: 3; order: 3; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss index ed818497..b3f724d8 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss @@ -15,7 +15,6 @@ limitations under the License. */ .mx_NotificationPanel { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; 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 dc668236..6a5db95a 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -18,7 +18,6 @@ limitations under the License. word-wrap: break-word; position: relative; - display: -moz-box; display: -ms-flexbox; display: flex; width: 100%; @@ -27,7 +26,6 @@ limitations under the License. } .mx_RoomView .mx_RoomHeader { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -47,8 +45,6 @@ limitations under the License. padding-right: 12px; margin-left: -12px; - -moz-border-radius-topleft: 10px; - -moz-border-radius-topright: 10px; border-top-left-radius: 10px; border-top-right-radius: 10px; @@ -69,7 +65,6 @@ limitations under the License. } .mx_RoomView_auxPanel { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -85,13 +80,11 @@ limitations under the License. } .mx_RoomView_topUnreadMessagesBar { - -moz-box-ordinal-group: 3; -ms-flex-order: 3; order: 3; } .mx_RoomView_messagePanel { - -moz-box-ordinal-group: 4; -ms-flex-order: 4; order: 4; @@ -108,7 +101,6 @@ limitations under the License. min-height: 100%; - display: -moz-box; display: -ms-flexbox; display: flex; @@ -141,7 +133,6 @@ limitations under the License. } .mx_RoomView_invitePrompt { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -170,7 +161,6 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView_statusArea { - -moz-box-ordinal-group: 5; -ms-flex-order: 5; order: 5; @@ -231,7 +221,6 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView .mx_MessageComposer { - -moz-box-ordinal-group: 6; -ms-flex-order: 6; order: 6; 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 2c62a4ec..d190e32c 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -20,14 +20,12 @@ limitations under the License. margin-left: auto; margin-right: auto; - display: -moz-box; display: -ms-flexbox; display: flex; flex-direction: column; } .mx_UserSettings .mx_RoomHeader { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -35,7 +33,6 @@ limitations under the License. } .mx_UserSettings_body { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; 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 332f313f..90dd2c1e 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 @@ -18,7 +18,6 @@ limitations under the License. width: 100%; height: 100%; - display: -moz-box; display: -ms-flexbox; display: flex; align-items: center; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss index 403de7f9..9c1daa95 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss @@ -22,7 +22,6 @@ limitations under the License. flex: 1; - display: -moz-box; display: -ms-flexbox; display: flex; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss index a4549c0f..5ecb3441 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss @@ -105,7 +105,6 @@ limitations under the License. border: 0px; resize: none; outline: none; - -moz-box-shadow: none; box-shadow: none; color: $primary-fg-color; background-color: $primary-bg-color; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss index 73327ea6..297a3f16 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss @@ -27,7 +27,6 @@ limitations under the License. align-items: center; - display: -moz-box; display: -ms-flexbox; display: flex; } @@ -35,7 +34,6 @@ limitations under the License. .mx_RoomHeader_leftRow { margin-left: -2px; - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -45,7 +43,6 @@ limitations under the License. .mx_RoomHeader_spinner { height: 36px; - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -63,7 +60,6 @@ limitations under the License. margin-top: -2px; text-align: center; - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -77,7 +73,6 @@ limitations under the License. } .mx_RoomHeader_cancelButton { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; @@ -92,7 +87,6 @@ limitations under the License. background-color: $primary-bg-color; display: flex; align-items: center; - -moz-box-ordinal-group: 3; -ms-flex-order: 3; order: 3; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss index 0d030ad7..2114e01a 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss @@ -24,7 +24,6 @@ limitations under the License. justify-content: center; - display: -moz-box; display: -ms-flexbox; display: flex; } diff --git a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss index 36a1e298..030c5367 100644 --- a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss @@ -17,7 +17,6 @@ limitations under the License. .mx_LeftPanel { position: relative; - display: -moz-box; display: -ms-flexbox; display: flex; flex-direction: column; @@ -36,7 +35,6 @@ limitations under the License. } .mx_LeftPanel .mx_RoomList_scrollbar { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -51,7 +49,6 @@ limitations under the License. } .mx_LeftPanel .mx_BottomLeftMenu { - -moz-box-ordinal-group: 3; -ms-flex-order: 3; order: 3; diff --git a/src/skins/vector/css/vector-web/structures/_RightPanel.scss b/src/skins/vector/css/vector-web/structures/_RightPanel.scss index 000d36b3..a76e5020 100644 --- a/src/skins/vector/css/vector-web/structures/_RightPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_RightPanel.scss @@ -17,14 +17,12 @@ limitations under the License. .mx_RightPanel { position: relative; - display: -moz-box; display: -ms-flexbox; display: flex; flex-direction: column; } .mx_RightPanel_header { - -moz-box-ordinal-group: 1; -ms-flex-order: 1; order: 1; @@ -75,14 +73,12 @@ limitations under the License. .mx_RightPanel .mx_MemberList, .mx_RightPanel .mx_MemberInfo, .mx_RightPanel_blank { - -moz-box-ordinal-group: 2; -ms-flex-order: 2; order: 2; flex: 1 1 0; } .mx_RightPanel_footer { - -moz-box-ordinal-group: 3; -ms-flex-order: 3; order: 3; diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index c80a8d8f..e2b65e68 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -22,7 +22,6 @@ limitations under the License. margin-bottom: 12px; color: $primary-fg-color; - display: -moz-box; display: -ms-flexbox; display: flex; @@ -32,7 +31,6 @@ limitations under the License. .mx_RoomDirectory_list { flex: 1; - display: -moz-box; display: -ms-flexbox; display: flex; diff --git a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss index 2141f3a8..d9d3cb19 100644 --- a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss @@ -18,7 +18,6 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -moz-box; display: -ms-flexbox; display: flex; align-items: center; diff --git a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss index 9e21bcb1..a425d8cd 100644 --- a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss @@ -18,7 +18,6 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -moz-box; display: -ms-flexbox; display: flex; align-items: center; From d74dfc9ee7c0affc4bc3ca7e7bf8c6befc7a46e6 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 18:58:37 +0000 Subject: [PATCH 0053/1951] strip out unneeded -ms- prefixes --- src/skins/vector/css/_common.scss | 2 -- .../css/matrix-react-sdk/structures/_FilePanel.scss | 1 - .../css/matrix-react-sdk/structures/_MatrixChat.scss | 9 --------- .../matrix-react-sdk/structures/_NotificationPanel.scss | 1 - .../css/matrix-react-sdk/structures/_RoomView.scss | 9 --------- .../css/matrix-react-sdk/structures/_UserSettings.scss | 3 --- .../css/matrix-react-sdk/structures/login/_Login.scss | 1 - .../css/matrix-react-sdk/views/rooms/_MemberList.scss | 1 - .../css/matrix-react-sdk/views/rooms/_RoomHeader.scss | 6 ------ .../matrix-react-sdk/views/rooms/_RoomPreviewBar.scss | 1 - .../vector/css/vector-web/structures/_LeftPanel.scss | 3 --- .../vector/css/vector-web/structures/_RightPanel.scss | 4 ---- .../vector/css/vector-web/structures/_RoomDirectory.scss | 2 -- .../css/vector-web/views/globals/_GuestWarningBar.scss | 1 - .../css/vector-web/views/globals/_MatrixToolbar.scss | 1 - 15 files changed, 45 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 5e0b902a..a2b7399e 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -83,7 +83,6 @@ textarea { /* applied to side-panels and messagepanel when in RoomSettings */ .mx_fadable { opacity: 1; - -ms-transition: opacity 0.2s ease-in-out; -o-transition: opacity 0.2s ease-in-out; } @@ -126,7 +125,6 @@ textarea { width: 100%; height: 100%; - display: -ms-flexbox; display: flex; align-items: center; justify-content: center; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss index 2fb1131d..872085b6 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_FilePanel.scss @@ -15,7 +15,6 @@ limitations under the License. */ .mx_FilePanel { - -ms-flex-order: 2; order: 2; flex: 1 1 0; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss index 72f1feff..05a39ea7 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_MatrixChat.scss @@ -27,7 +27,6 @@ limitations under the License. } .mx_MatrixChat_wrapper { - display: -ms-flexbox; display: flex; flex-direction: column; @@ -37,14 +36,12 @@ limitations under the License. } .mx_MatrixToolbar { - -ms-flex-order: 1; order: 1; height: 40px; } .mx_GuestWarningBar { - -ms-flex-order: 1; order: 1; height: 40px; @@ -58,17 +55,14 @@ limitations under the License. width: 100%; height: 100%; - display: -ms-flexbox; display: flex; - -ms-flex-order: 2; order: 2; flex: 1; } .mx_MatrixChat .mx_LeftPanel { - -ms-flex-order: 1; order: 1; background-color: $secondary-accent-color; @@ -81,7 +75,6 @@ limitations under the License. } .mx_MatrixChat .mx_MatrixChat_middlePanel { - -ms-flex-order: 2; order: 2; padding-left: 20px; @@ -106,12 +99,10 @@ limitations under the License. * point, but instead we fudge it and make the middlePanel * flex itself. */ - display: -ms-flexbox; display: flex; } .mx_MatrixChat .mx_RightPanel { - -ms-flex-order: 3; order: 3; flex: 0 0 235px; diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss index b3f724d8..ef75678d 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_NotificationPanel.scss @@ -15,7 +15,6 @@ limitations under the License. */ .mx_NotificationPanel { - -ms-flex-order: 2; order: 2; flex: 1 1 0; 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 6a5db95a..919b9f9b 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -18,7 +18,6 @@ limitations under the License. word-wrap: break-word; position: relative; - display: -ms-flexbox; display: flex; width: 100%; @@ -26,7 +25,6 @@ limitations under the License. } .mx_RoomView .mx_RoomHeader { - -ms-flex-order: 1; order: 1; flex: 0 0 70px; @@ -65,7 +63,6 @@ limitations under the License. } .mx_RoomView_auxPanel { - -ms-flex-order: 2; order: 2; min-width: 0px; @@ -80,12 +77,10 @@ limitations under the License. } .mx_RoomView_topUnreadMessagesBar { - -ms-flex-order: 3; order: 3; } .mx_RoomView_messagePanel { - -ms-flex-order: 4; order: 4; flex: 1 1 0; @@ -101,7 +96,6 @@ limitations under the License. min-height: 100%; - display: -ms-flexbox; display: flex; flex-direction: column; @@ -133,7 +127,6 @@ limitations under the License. } .mx_RoomView_invitePrompt { - -ms-flex-order: 2; order: 2; min-width: 0px; @@ -161,7 +154,6 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView_statusArea { - -ms-flex-order: 5; order: 5; width: 100%; @@ -221,7 +213,6 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView .mx_MessageComposer { - -ms-flex-order: 6; order: 6; width: 100%; 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 d190e32c..5c1b4c5c 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_UserSettings.scss @@ -20,20 +20,17 @@ limitations under the License. margin-left: auto; margin-right: auto; - display: -ms-flexbox; display: flex; flex-direction: column; } .mx_UserSettings .mx_RoomHeader { - -ms-flex-order: 1; order: 1; flex: 0 0 70px; } .mx_UserSettings_body { - -ms-flex-order: 2; order: 2; flex: 1 1 0; 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 90dd2c1e..30231b43 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 @@ -18,7 +18,6 @@ limitations under the License. width: 100%; height: 100%; - display: -ms-flexbox; display: flex; align-items: center; justify-content: center; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss index 9c1daa95..d87bced4 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberList.scss @@ -22,7 +22,6 @@ limitations under the License. flex: 1; - display: -ms-flexbox; display: flex; flex-direction: column; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss index 297a3f16..688b8a84 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss @@ -27,14 +27,12 @@ limitations under the License. align-items: center; - display: -ms-flexbox; display: flex; } .mx_RoomHeader_leftRow { margin-left: -2px; - -ms-flex-order: 1; order: 1; flex: 1; @@ -43,7 +41,6 @@ limitations under the License. .mx_RoomHeader_spinner { height: 36px; - -ms-flex-order: 2; order: 2; padding-left: 12px; @@ -60,7 +57,6 @@ limitations under the License. margin-top: -2px; text-align: center; - -ms-flex-order: 2; order: 2; cursor: pointer; @@ -73,7 +69,6 @@ limitations under the License. } .mx_RoomHeader_cancelButton { - -ms-flex-order: 2; order: 2; cursor: pointer; @@ -87,7 +82,6 @@ limitations under the License. background-color: $primary-bg-color; display: flex; align-items: center; - -ms-flex-order: 3; order: 3; } diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss index 2114e01a..3814f9c0 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss @@ -24,7 +24,6 @@ limitations under the License. justify-content: center; - display: -ms-flexbox; display: flex; } diff --git a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss index 030c5367..1ce10e51 100644 --- a/src/skins/vector/css/vector-web/structures/_LeftPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_LeftPanel.scss @@ -17,7 +17,6 @@ limitations under the License. .mx_LeftPanel { position: relative; - display: -ms-flexbox; display: flex; flex-direction: column; } @@ -35,7 +34,6 @@ limitations under the License. } .mx_LeftPanel .mx_RoomList_scrollbar { - -ms-flex-order: 1; order: 1; flex: 1 1 0; @@ -49,7 +47,6 @@ limitations under the License. } .mx_LeftPanel .mx_BottomLeftMenu { - -ms-flex-order: 3; order: 3; border-top: 1px solid rgba(118, 207, 166, 0.2); diff --git a/src/skins/vector/css/vector-web/structures/_RightPanel.scss b/src/skins/vector/css/vector-web/structures/_RightPanel.scss index a76e5020..96e8698b 100644 --- a/src/skins/vector/css/vector-web/structures/_RightPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_RightPanel.scss @@ -17,13 +17,11 @@ limitations under the License. .mx_RightPanel { position: relative; - display: -ms-flexbox; display: flex; flex-direction: column; } .mx_RightPanel_header { - -ms-flex-order: 1; order: 1; border-bottom: 1px solid $primary-hairline-color; @@ -73,13 +71,11 @@ limitations under the License. .mx_RightPanel .mx_MemberList, .mx_RightPanel .mx_MemberInfo, .mx_RightPanel_blank { - -ms-flex-order: 2; order: 2; flex: 1 1 0; } .mx_RightPanel_footer { - -ms-flex-order: 3; order: 3; border-top: 1px solid $primary-hairline-color; diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index e2b65e68..f0d4c860 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -22,7 +22,6 @@ limitations under the License. margin-bottom: 12px; color: $primary-fg-color; - display: -ms-flexbox; display: flex; flex-direction: column; @@ -31,7 +30,6 @@ limitations under the License. .mx_RoomDirectory_list { flex: 1; - display: -ms-flexbox; display: flex; flex-direction: column; diff --git a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss index d9d3cb19..f5bdbaf9 100644 --- a/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_GuestWarningBar.scss @@ -18,7 +18,6 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -ms-flexbox; display: flex; align-items: center; } diff --git a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss index a425d8cd..5a0b23ae 100644 --- a/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss +++ b/src/skins/vector/css/vector-web/views/globals/_MatrixToolbar.scss @@ -18,7 +18,6 @@ limitations under the License. background-color: $accent-color; color: $accent-fg-color; - display: -ms-flexbox; display: flex; align-items: center; } From cab5bf8849395dab0afad1439ca3ae2a1a22b25b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 19:00:44 +0000 Subject: [PATCH 0054/1951] fix a lone lost opera vendor prefix --- src/skins/vector/css/_common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index a2b7399e..09f3fcfb 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -83,7 +83,7 @@ textarea { /* applied to side-panels and messagepanel when in RoomSettings */ .mx_fadable { opacity: 1; - -o-transition: opacity 0.2s ease-in-out; + transition: opacity 0.2s ease-in-out; } /* XXX: critical hack to GeminiScrollbar to allow them to work in FF 42 and Chrome 48. From 87fd136e218000c64ed9b7369ce176dc18d1fdc0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 16 Jan 2017 23:13:47 +0000 Subject: [PATCH 0055/1951] factor out remaining # colours --- .../views/rooms/_EntityTile.scss | 4 ++-- .../views/rooms/_LinkPreviewWidget.scss | 4 ++-- .../views/rooms/_MemberInfo.scss | 1 - .../views/rooms/_MessageComposer.scss | 10 +++++----- .../views/rooms/_RoomHeader.scss | 17 +++-------------- .../views/rooms/_RoomPreviewBar.scss | 6 +----- .../views/rooms/_RoomSettings.scss | 2 +- .../views/rooms/_SearchableEntityList.scss | 4 ++-- .../views/voip/_IncomingCallbox.scss | 4 ++-- src/skins/vector/css/themes/_base.scss | 13 +++++++++++++ src/skins/vector/css/themes/_dark.scss | 13 +++++++++++++ .../vector-web/structures/_RoomDirectory.scss | 4 ++-- .../context_menus/_RoomTagContextMenu.scss | 3 +-- .../vector-web/views/elements/_ImageView.scss | 13 ++++--------- 14 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss index 2511f07d..3f360e79 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EntityTile.scss @@ -77,12 +77,12 @@ limitations under the License. .mx_EntityTile_ellipsis .mx_EntityTile_name { font-style: italic; - font-color: $primary-fg-color; + color: $primary-fg-color; } .mx_EntityTile_invitePlaceholder .mx_EntityTile_name { font-style: italic; - font-color: $primary-fg-color; + color: $primary-fg-color; } .mx_EntityTile_unavailable .mx_EntityTile_avatar, diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss index 0e911541..33f283e0 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_LinkPreviewWidget.scss @@ -19,8 +19,8 @@ limitations under the License. margin-right: 15px; margin-bottom: 15px; display: flex; - border-left: 4px solid #ddd; - color: #888; + border-left: 4px solid $preview-widget-bar-color; + color: $preview-widget-fg-color; } .mx_LinkPreviewWidget_image { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss index 970c2496..d6fb5a19 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MemberInfo.scss @@ -69,7 +69,6 @@ limitations under the License. } .mx_MemberInfo_profileField { - font-color: #999999; font-size: 13px; position: relative; background-color: $primary-bg-color; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss index 5ecb3441..525cc1f6 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_MessageComposer.scss @@ -57,7 +57,7 @@ limitations under the License. height: 60px; text-align: center; font-style: italic; - color: #888; + color: $greyed-fg-color; } .mx_MessageComposer_input_wrapper { @@ -90,10 +90,10 @@ limitations under the License. } .mx_MessageComposer_input blockquote { - color: rgb(119, 119, 119); + color: $blockquote-fg-color; margin: 0 0 16px; padding: 0 15px; - border-left: 4px solid rgb(221, 221, 221); + border-left: 4px solid $blockquote-bar-color; } .mx_MessageComposer_input textarea { @@ -151,7 +151,7 @@ limitations under the License. .mx_MessageComposer_formatbar_wrapper { width: 100%; - background-color: #f7f7f7; + background-color: $menu-bg-color; box-shadow: inset 0 1px 0 0 rgba(0, 0, 0, 0.08); } @@ -168,7 +168,7 @@ limitations under the License. flex-direction: row; align-items: center; font-size: 10px; - color: #888; + color: $greyed-fg-color; } .mx_MessageComposer_formatbar * { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss index 688b8a84..4affc994 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomHeader.scss @@ -16,7 +16,6 @@ limitations under the License. /* add 20px to the height of the header when editing */ .mx_RoomHeader_editing { - -webit-flex: 0 0 93px ! important; flex: 0 0 93px ! important; } @@ -24,25 +23,19 @@ limitations under the License. max-width: 960px; margin: auto; height: 70px; - align-items: center; - display: flex; } .mx_RoomHeader_leftRow { margin-left: -2px; - order: 1; - flex: 1; } .mx_RoomHeader_spinner { height: 36px; - order: 2; - padding-left: 12px; padding-right: 12px; } @@ -56,9 +49,7 @@ limitations under the License. line-height: 34px; margin-top: -2px; text-align: center; - order: 2; - cursor: pointer; /* @@ -70,9 +61,7 @@ limitations under the License. .mx_RoomHeader_cancelButton { order: 2; - cursor: pointer; - padding-left: 12px; padding-right: 12px; } @@ -126,7 +115,7 @@ limitations under the License. } .mx_RoomHeader_settingsHint { - color: #a2a2a2 ! important; + color: $settings-grey-fg-color ! important; } .mx_RoomHeader_searchStatus { @@ -151,7 +140,7 @@ limitations under the License. } .mx_RoomHeader_placeholder { - color: #a2a2a2 ! important; + color: $settings-grey-fg-color ! important; } .mx_RoomHeader_editable { @@ -170,7 +159,7 @@ limitations under the License. vertical-align: bottom; float: left; max-height: 42px; - color: #A2A2A2; + color: $settings-grey-fg-color; font-weight: 300; font-size: 13px; margin-left: 19px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss index 3814f9c0..34ff3a86 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomPreviewBar.scss @@ -17,13 +17,9 @@ limitations under the License. .mx_RoomPreviewBar { text-align: center; height: 176px; - align-items: center; - flex-direction: column; - justify-content: center; - display: flex; } @@ -40,7 +36,7 @@ limitations under the License. .mx_RoomPreviewBar_preview_text { margin-top: 25px; - color: #a4a4a4; + color: $settings-grey-fg-color; } .mx_RoomPreviewBar_join_text a { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss index d9de0e8a..ef115f6e 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomSettings.scss @@ -205,7 +205,7 @@ limitations under the License. } .mx_RoomSettings_aliasPlaceholder { - color: #a2a2a2; + color: $settings-grey-fg-color; } .mx_RoomSettings_buttons { diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss index b404da02..6116dd92 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_SearchableEntityList.scss @@ -69,8 +69,8 @@ limitations under the License. .mx_SearchableEntityList hr { height: 1px; border: 0px; - color: #e1dddd; - background-color: #e1dddd; + color: $primary-fg-color; + background-color: $primary-fg-color; margin-right: 15px; margin-top: 11px; margin-bottom: 11px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss index d45c4a58..64eac25d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/voip/_IncomingCallbox.scss @@ -61,9 +61,9 @@ limitations under the License. } .mx_IncomingCallBox_buttons_decline { - background-color: #f48080; + background-color: $voip-decline-color; } .mx_IncomingCallBox_buttons_accept { - background-color: #80f480; + background-color: $voip-accept-color; } diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index e623372a..4f47df64 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -55,6 +55,19 @@ $h3-color: #3d3b39; $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; +$greyed-fg-color: #888; + +$preview-widget-bar-color: #ddd; +$preview-widget-fg-color: $greyed-fg-color; + +$blockquote-bar-color: #ddd; +$blockquote-fg-color: #777; + +$settings-grey-fg-color: #a2a2a2; + +$voip-decline-color: #f48080; +$voip-accept-color: #80f480; + // ******************** $roomtile-name-color: rgba(69, 69, 69, 0.8); diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 1d93c1a4..28b80a20 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -55,6 +55,19 @@ $h3-color: $primary-fg-color; $dialog-background-bg-color: #000; $lightbox-background-bg-color: #000; +$greyed-fg-color: #888; + +$preview-widget-bar-color: $menu-bg-color; +$preview-widget-fg-color: $greyed-fg-color; + +$blockquote-bar-color: #ddd; +$blockquote-fg-color: #777; + +$settings-grey-fg-color: #a2a2a2; + +$voip-decline-color: #f48080; +$voip-accept-color: #80f480; + // ******************** $roomtile-name-color: rgba(186, 186, 186, 0.8); diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index f0d4c860..8c8ceeaf 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -101,7 +101,7 @@ limitations under the License. text-transform: uppercase; font-weight: 600; font-size: 11px; - color: #61c295; + color: $accent-fg-color; } .mx_RoomDirectory_topic { @@ -110,7 +110,7 @@ limitations under the License. .mx_RoomDirectory_alias { font-size: 12px; - color: #b3b3b3; + color: $settings-grey-fg-color; } .mx_RoomDirectory_roomMemberCount { diff --git a/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss b/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss index 0a2e7605..16a3ab79 100644 --- a/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss +++ b/src/skins/vector/css/vector-web/views/context_menus/_RoomTagContextMenu.scss @@ -70,8 +70,7 @@ limitations under the License. border-right-style: none; border-top-style: solid; border-top-width: 1px; - border-color: #bbbbbb; - opacity: 0.4; + border-color: $menu-border-color; } .mx_RoomTagContextMenu_fieldSet .mx_RoomTagContextMenu_icon { diff --git a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss index d31a9d27..66c26e12 100644 --- a/src/skins/vector/css/vector-web/views/elements/_ImageView.scss +++ b/src/skins/vector/css/vector-web/views/elements/_ImageView.scss @@ -29,10 +29,8 @@ limitations under the License. order: 1; flex: 1 1 10%; min-width: 60px; - /* - background-color: #080; - height: 20px; - */ + // background-color: #080; + // height: 20px; } .mx_ImageView_content { @@ -41,7 +39,6 @@ limitations under the License. min-width: 0px; height: 90%; flex: 15 15 0; - display: flex; align-items: center; justify-content: center; @@ -132,8 +129,6 @@ limitations under the License. order: 3; flex: 1 1 10%; min-width: 300px; - /* - background-color: #800; - height: 20px; - */ + // background-color: #800; + // height: 20px; } From 4f8d3b0e2b02e3c41344e86abd57aff555d7c1e0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Jan 2017 02:00:34 +0000 Subject: [PATCH 0056/1951] more factoring --- src/skins/vector/css/_common.scss | 4 ++-- .../vector/css/matrix-react-sdk/structures/_RoomView.scss | 2 +- src/skins/vector/css/themes/_base.scss | 2 ++ src/skins/vector/css/themes/_dark.scss | 2 ++ src/skins/vector/css/vector-web/structures/_RightPanel.scss | 3 ++- .../vector/css/vector-web/structures/_RoomDirectory.scss | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 09f3fcfb..01ba81d8 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -266,12 +266,12 @@ textarea { ::-moz-selection { background-color: $accent-color; - color: white; + color: $selection-fg-color; } ::selection { background-color: $accent-color; - color: white; + color: $selection-fg-color; } /** green button with rounded corners */ 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 919b9f9b..d1aeddec 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -46,7 +46,7 @@ limitations under the License. border-top-left-radius: 10px; border-top-right-radius: 10px; - background-color: rgba(255, 255, 255, 0.9); + background-color: $droptarget-bg-color; border: 2px #e1dddd solid; border-bottom: none; position: absolute; diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index 4f47df64..6488794d 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -13,6 +13,8 @@ $focus-bg-color: #dddddd; $accent-fg-color: #ffffff; $accent-color: #76CFA6; +$selection-fg-color: $primary-bg-color; + // red warning colour $warning-color: #ff0064; diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 28b80a20..62e89a7b 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -13,6 +13,8 @@ $light-fg-color: #747474; $accent-fg-color: $primary-bg-color; $accent-color: #76CFA6; +$selection-fg-color: $primary-bg-color; + // red warning colour $warning-color: #ff0064; diff --git a/src/skins/vector/css/vector-web/structures/_RightPanel.scss b/src/skins/vector/css/vector-web/structures/_RightPanel.scss index 96e8698b..91034e63 100644 --- a/src/skins/vector/css/vector-web/structures/_RightPanel.scss +++ b/src/skins/vector/css/vector-web/structures/_RightPanel.scss @@ -58,7 +58,8 @@ limitations under the License. width: 25px; height: 5px; border-radius: 5px; - background-color: rgba(118, 207, 166, 0.2); + background-color: $accent-color; + opacity: 0.2; } .mx_RightPanel_headerButton_badge { diff --git a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss index 8c8ceeaf..6e508ec7 100644 --- a/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss +++ b/src/skins/vector/css/vector-web/structures/_RoomDirectory.scss @@ -101,7 +101,7 @@ limitations under the License. text-transform: uppercase; font-weight: 600; font-size: 11px; - color: $accent-fg-color; + color: $accent-color; } .mx_RoomDirectory_topic { From 3bdb330f5bb6682764acf781cc2b25aeb834e03d Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Jan 2017 09:57:01 +0000 Subject: [PATCH 0057/1951] cheeky hack to get the ctxt menu colors right --- src/skins/vector/img/icon_context_message.svg | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/skins/vector/img/icon_context_message.svg b/src/skins/vector/img/icon_context_message.svg index f2ceccfa..c3d39b1e 100644 --- a/src/skins/vector/img/icon_context_message.svg +++ b/src/skins/vector/img/icon_context_message.svg @@ -4,11 +4,18 @@ ED5D3E59-2561-4AC1-9B43-82FBC51767FC Created with sketchtool. + + + + - - - + + + From ef07a6035ed58184f94464abcee035592e666078 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 17 Jan 2017 10:48:38 +0000 Subject: [PATCH 0058/1951] Fix links to fonts and images from CSS https://github.com/vector-im/riot-web/pull/2961 put the CSS in a subdirectory, which meant that links from CSS to images and fonts were broken. Fix them up. --- .../views/elements/DirectorySearchBox.css | 4 ++-- .../matrix-react-sdk/views/rooms/RoomTile.css | 2 +- src/skins/vector/css/vector-web/fonts.css | 18 ++++++++++-------- .../vector-web/views/elements/ImageView.css | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css b/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css index 3a0922bc..5686ecd0 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css @@ -46,7 +46,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { padding-right: 10px; background-color: #efefef; border-radius: 3px; - background-image: url('img/icon-return.svg'); + background-image: url('../img/icon-return.svg'); background-position: 8px 70%; background-repeat: no-repeat; text-indent: 18px; @@ -63,7 +63,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { .mx_DirectorySearchBox_clear { display: inline-block; vertical-align: middle; - background: url('img/icon_context_delete.svg'); + background: url('../img/icon_context_delete.svg'); background-position: 0 50%; background-repeat: no-repeat; width: 15px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css index 2822d82e..2411d613 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css @@ -65,7 +65,7 @@ limitations under the License. position: absolute; content: ""; border-radius: 40px; - background-image: url("img/icons_ellipsis.svg"); + background-image: url("../img/icons_ellipsis.svg"); background-size: 25px; width: 24px; height: 24px; diff --git a/src/skins/vector/css/vector-web/fonts.css b/src/skins/vector/css/vector-web/fonts.css index 719eeebc..a57d9952 100644 --- a/src/skins/vector/css/vector-web/fonts.css +++ b/src/skins/vector/css/vector-web/fonts.css @@ -3,44 +3,46 @@ * Includes extended Latin, Greek, Cyrillic and Vietnamese character sets */ +/* the 'src' links are relative to the bundle.css, which is in a subdirectory. + */ @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); font-weight: 400; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); font-weight: 600; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); font-weight: 600; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); + src: url('../fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); font-weight: 700; font-style: italic; } @@ -52,14 +54,14 @@ @font-face { font-family: 'Fira Mono'; - src: url('fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); + src: url('../fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Fira Mono'; - src: url('fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); + src: url('../fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } diff --git a/src/skins/vector/css/vector-web/views/elements/ImageView.css b/src/skins/vector/css/vector-web/views/elements/ImageView.css index 03223f25..0942b593 100644 --- a/src/skins/vector/css/vector-web/views/elements/ImageView.css +++ b/src/skins/vector/css/vector-web/views/elements/ImageView.css @@ -62,7 +62,7 @@ limitations under the License. max-height: 100%; /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; - /* background-image: url('img/trans.png'); */ + /* background-image: url('../img/trans.png'); */ pointer-events: all; } From 205676a97da4a756d63eb4b050caf350668b11d4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Jan 2017 11:30:59 +0000 Subject: [PATCH 0059/1951] Back to develop js-sdk & react-sdk --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f169cf9b..b79db54a 100644 --- a/package.json +++ b/package.json @@ -62,8 +62,8 @@ "gfm.css": "^1.1.1", "highlight.js": "^9.0.0", "linkifyjs": "^2.1.3", - "matrix-js-sdk": "0.7.4", - "matrix-react-sdk": "0.8.5", + "matrix-js-sdk": "matrix-org/matrix-js-sdk#develop", + "matrix-react-sdk": "matrix-org/matrix-react-sdk#develop", "modernizr": "^3.1.0", "q": "^1.4.1", "react": "^15.4.0", From 4a1b04e57be363f3c45f356c1862cf5853769e10 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 17 Jan 2017 11:58:30 +0000 Subject: [PATCH 0060/1951] Add another layer of directory to webpack chunks --- .../views/elements/DirectorySearchBox.css | 4 ++-- .../matrix-react-sdk/views/rooms/RoomTile.css | 2 +- src/skins/vector/css/vector-web/fonts.css | 16 ++++++++-------- .../css/vector-web/views/elements/ImageView.css | 2 +- webpack.config.js | 15 ++++++++++++--- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css b/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css index 5686ecd0..c81974eb 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css +++ b/src/skins/vector/css/matrix-react-sdk/views/elements/DirectorySearchBox.css @@ -46,7 +46,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { padding-right: 10px; background-color: #efefef; border-radius: 3px; - background-image: url('../img/icon-return.svg'); + background-image: url('../../img/icon-return.svg'); background-position: 8px 70%; background-repeat: no-repeat; text-indent: 18px; @@ -63,7 +63,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { .mx_DirectorySearchBox_clear { display: inline-block; vertical-align: middle; - background: url('../img/icon_context_delete.svg'); + background: url('../../img/icon_context_delete.svg'); background-position: 0 50%; background-repeat: no-repeat; width: 15px; diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css index 2411d613..b752d105 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/RoomTile.css @@ -65,7 +65,7 @@ limitations under the License. position: absolute; content: ""; border-radius: 40px; - background-image: url("../img/icons_ellipsis.svg"); + background-image: url("../../img/icons_ellipsis.svg"); background-size: 25px; width: 24px; height: 24px; diff --git a/src/skins/vector/css/vector-web/fonts.css b/src/skins/vector/css/vector-web/fonts.css index a57d9952..52ac95b5 100644 --- a/src/skins/vector/css/vector-web/fonts.css +++ b/src/skins/vector/css/vector-web/fonts.css @@ -7,42 +7,42 @@ */ @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); font-weight: 400; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); font-weight: 600; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); font-weight: 600; font-style: italic; } @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } @font-face { font-family: 'Open Sans'; - src: url('../fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); + src: url('../../fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); font-weight: 700; font-style: italic; } @@ -54,14 +54,14 @@ @font-face { font-family: 'Fira Mono'; - src: url('../fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); + src: url('../../fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); font-weight: 400; font-style: normal; } @font-face { font-family: 'Fira Mono'; - src: url('../fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); + src: url('../../fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } diff --git a/src/skins/vector/css/vector-web/views/elements/ImageView.css b/src/skins/vector/css/vector-web/views/elements/ImageView.css index 0942b593..73aaaa86 100644 --- a/src/skins/vector/css/vector-web/views/elements/ImageView.css +++ b/src/skins/vector/css/vector-web/views/elements/ImageView.css @@ -62,7 +62,7 @@ limitations under the License. max-height: 100%; /* object-fit hack needed for Chrome due to Chrome not re-laying-out until you refresh */ object-fit: contain; - /* background-image: url('../img/trans.png'); */ + /* background-image: url('../../img/trans.png'); */ pointer-events: all; } diff --git a/webpack.config.js b/webpack.config.js index 9c0c6bbb..2a38d139 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -40,8 +40,17 @@ module.exports = { }, output: { path: path.join(__dirname, "webapp"), - filename: "[hash]/[name].js", - chunkFilename: "[hash]/[name].js", + + // the generated js (and CSS, from the ExtractTextPlugin) are put in a + // unique subdirectory for the build. There will only be one such + // 'bundle' directory in the generated tarball; however, hosting + // servers can collect 'bundles' from multiple versions into one + // directory and symlink it into place - this allows users who loaded + // an older version of the application to continue to access webpack + // chunks even after the app is redeployed. + // + filename: "bundles/[hash]/[name].js", + chunkFilename: "bundles/[hash]/[name].js", devtoolModuleFilenameTemplate: function(info) { // Reading input source maps gives only relative paths here for // everything. Until I figure out how to fix this, this is a @@ -80,7 +89,7 @@ module.exports = { }), new ExtractTextPlugin( - "[hash]/[name].css", + "bundles/[hash]/[name].css", { allChunks: true } From 3b109f761283d1aac5f4ff26462bd4f77ff83155 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Jan 2017 13:16:08 +0000 Subject: [PATCH 0061/1951] Include current version in update check explicitly Hopefully fix https://github.com/vector-im/riot-web/issues/2847 --- electron/src/electron-main.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 675640a5..3f7c0f07 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -112,7 +112,15 @@ function startAutoUpdate(update_base_url) { // 204 No Content. On windows it takes a base path and looks for // files under that path. if (process.platform == 'darwin') { - electron.autoUpdater.setFeedURL(update_base_url + 'macos/'); + // include the current version in the URL we hit. Electron doesn't add + // it anywhere (apart from the User-Agent) so it's up to us. We could + // (and previously did) just use the User-Agent, but this doesn't + // rely on NSURLConnection setting the User-Agent to what we expect, + // and also acts as a convenient cache-buster between versions. + electron.autoUpdater.setFeedURL( + update_base_url + + 'macos/?localVersion=' + encodeURIComponent(electron.app.getVersion()) + ); } else if (process.platform == 'win32') { electron.autoUpdater.setFeedURL(update_base_url + 'win32/' + process.arch + '/'); } else { From 8371006d90e37d554cc1d05e94f10f6d0f4134b7 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 17 Jan 2017 13:55:33 +0000 Subject: [PATCH 0062/1951] Update redeploy script to keep old bundles ... so that people using old versions of the master chunk can still load other webpack chunks. --- scripts/redeploy.py | 142 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 118 insertions(+), 24 deletions(-) diff --git a/scripts/redeploy.py b/scripts/redeploy.py index 36585f53..4394f292 100755 --- a/scripts/redeploy.py +++ b/scripts/redeploy.py @@ -1,13 +1,32 @@ #!/usr/bin/env python +# +# auto-deploy script for https://riot.im/develop +# +# Listens for HTTP hits. When it gets one, downloads the artifact from jenkins +# and deploys it as the new version. +# +# Requires the following python packages: +# +# - requests +# - flask +# from __future__ import print_function import json, requests, tarfile, argparse, os, errno +import time from urlparse import urljoin from flask import Flask, jsonify, request, abort + app = Flask(__name__) -arg_jenkins_url, arg_extract_path, arg_should_clean, arg_symlink, arg_config_location = ( - None, None, None, None, None -) +arg_jenkins_url = None +arg_extract_path = None +arg_bundles_path = None +arg_should_clean = None +arg_symlink = None +arg_config_location = None + +class DeployException(Exception): + pass def download_file(url): local_filename = url.split('/')[-1] @@ -57,6 +76,9 @@ def on_receive_jenkins_poke(): abort(400, "Missing or bad build number") return + return fetch_jenkins_build(job_name, build_num) + +def fetch_jenkins_build(job_name, build_num): artifact_url = urljoin( arg_jenkins_url, "job/%s/%s/api/json" % (job_name, build_num) ) @@ -106,7 +128,31 @@ def on_receive_jenkins_poke(): arg_jenkins_url, "job/%s/%s/artifact/%s" % (job_name, build_num, tar_gz_path) ) - print("Retrieving .tar.gz file: %s" % tar_gz_url) + # we extract into a directory based on the build number. This avoids the + # problem of multiple builds building the same git version and thus having + # the same tarball name. That would lead to two potential problems: + # (a) sometimes jenkins serves corrupted artifacts; we would replace + # a good deploy with a bad one + # (b) we'll be overwriting the live deployment, which means people might + # see half-written files. + build_dir = os.path.join(arg_extract_path, "%s-#%s" % (job_name, build_num)) + try: + deploy_tarball(tar_gz_url, build_dir) + except DeployException as e: + abort(400, e.message) + + return jsonify({}) + +def deploy_tarball(tar_gz_url, build_dir): + """Download a tarball from jenkins and deploy it as the new version + """ + print("Deploying %s to %s" % (tar_gz_url, build_dir)) + + if os.path.exists(build_dir): + raise DeployException( + "Not deploying. We have previously deployed this build." + ) + os.mkdir(build_dir) # we rely on the fact that flask only serves one request at a time to # ensure that we do not overwrite a tarball from a concurrent request. @@ -114,19 +160,6 @@ def on_receive_jenkins_poke(): print("Downloaded file: %s" % filename) try: - # we extract into a directory based on the build number. This avoids the - # problem of multiple builds building the same git version and thus having - # the same tarball name. That would lead to two potential problems: - # (a) sometimes jenkins serves corrupted artifacts; we would replace - # a good deploy with a bad one - # (b) we'll be overwriting the live deployment, which means people might - # see half-written files. - build_dir = os.path.join(arg_extract_path, "%s-#%s" % (job_name, build_num)) - if os.path.exists(build_dir): - abort(400, "Not deploying. We have previously deployed this build.") - return - os.mkdir(build_dir) - untar_to(filename, build_dir) print("Extracted to: %s" % build_dir) finally: @@ -139,9 +172,47 @@ def on_receive_jenkins_poke(): if arg_config_location: create_symlink(source=arg_config_location, linkname=os.path.join(extracted_dir, 'config.json')) + if arg_bundles_path: + extracted_bundles = os.path.join(extracted_dir, 'bundles') + move_bundles(source=extracted_bundles, dest=arg_bundles_path) + + # replace the (hopefully now empty) extracted_bundles dir with a + # symlink to the common dir. + relpath = os.path.relpath(arg_bundles_path, extracted_dir) + os.rmdir(extracted_bundles) + print ("Symlink %s -> %s" % (extracted_bundles, relpath)) + os.symlink(relpath, extracted_bundles) + create_symlink(source=extracted_dir, linkname=arg_symlink) - return jsonify({}) +def move_bundles(source, dest): + """Move the contents of the 'bundles' directory to a common dir + + We check that we will not be overwriting anything before we proceed. + + Args: + source (str): path to 'bundles' within the extracted tarball + dest (str): target common directory + """ + + if not os.path.isdir(dest): + os.mkdir(dest) + + # build a map from source to destination, checking for non-existence as we go. + renames = {} + for f in os.listdir(source): + dst = os.path.join(dest, f) + if os.path.exists(dst): + raise DeployException( + "Not deploying. The bundle includes '%s' which we have previously deployed." + % f + ) + renames[os.path.join(source, f)] = dst + + for (src, dst) in renames.iteritems(): + print ("Move %s -> %s" % (src, dst)) + os.rename(src, dst) + if __name__ == "__main__": parser = argparse.ArgumentParser("Runs a Vector redeployment server.") @@ -161,6 +232,13 @@ if __name__ == "__main__": "The location to extract .tar.gz files to." ) ) + parser.add_argument( + "-b", "--bundles-dir", dest="bundles_dir", help=( + "A directory to move the contents of the 'bundles' directory to. A \ + symlink to the bundles directory will also be written inside the \ + extracted tarball. Example: './bundles'." + ) + ) parser.add_argument( "-c", "--clean", dest="clean", action="store_true", default=False, help=( "Remove .tar.gz files after they have been downloaded and extracted." @@ -179,18 +257,34 @@ if __name__ == "__main__": To this location." ) ) + parser.add_argument( + "--test", dest="tarball_uri", help=( + "Don't start an HTTP listener. Instead download a build from Jenkins \ + immediately." + ), + ) + args = parser.parse_args() if args.jenkins.endswith("/"): # important for urljoin arg_jenkins_url = args.jenkins else: arg_jenkins_url = args.jenkins + "/" arg_extract_path = args.extract + arg_bundles_path = args.bundles_dir arg_should_clean = args.clean arg_symlink = args.symlink arg_config_location = args.config - print( - "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" % - (args.port, arg_extract_path, - " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url, arg_config_location) - ) - app.run(host="0.0.0.0", port=args.port, debug=True) + + if not os.path.isdir(arg_extract_path): + os.mkdir(arg_extract_path) + + if args.tarball_uri is not None: + build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time())) + deploy_tarball(args.tarball_uri, build_dir) + else: + print( + "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" % + (args.port, arg_extract_path, + " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url, arg_config_location) + ) + app.run(host="0.0.0.0", port=args.port, debug=True) From cf92e7f64bfc480dd860ce52d8c986c9d8c708df Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Jan 2017 14:04:42 +0000 Subject: [PATCH 0063/1951] Clarify comment --- electron/src/electron-main.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 3f7c0f07..a03a8755 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -116,7 +116,8 @@ function startAutoUpdate(update_base_url) { // it anywhere (apart from the User-Agent) so it's up to us. We could // (and previously did) just use the User-Agent, but this doesn't // rely on NSURLConnection setting the User-Agent to what we expect, - // and also acts as a convenient cache-buster between versions. + // and also acts as a convenient cache-buster to ensure that when the + // app updates it always gets a fresh value to avoid update-looping. electron.autoUpdater.setFeedURL( update_base_url + 'macos/?localVersion=' + encodeURIComponent(electron.app.getVersion()) From c9c58ab0ecb966aca6d3d710db149b83bb4fa5ba Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Jan 2017 14:10:09 +0000 Subject: [PATCH 0064/1951] fix up the contextual menu button --- .../matrix-react-sdk/views/rooms/_EventTile.scss | 10 +++++++++- src/skins/vector/css/themes/_base.scss | 2 ++ src/skins/vector/css/themes/_dark.scss | 2 ++ src/skins/vector/img/icon_context_message.svg | 13 +++---------- .../vector/img/icon_context_message_dark.svg | 15 +++++++++++++++ 5 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 src/skins/vector/img/icon_context_message_dark.svg diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss index a6b10e24..b79db919 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss @@ -146,7 +146,12 @@ limitations under the License. z-index: 1; position: relative; width: 90px; - height: 1px; /* Hack to stop the height of this pushing the messages apart. Replaces marigin-top: -6px. This interacts better with a read marker being in between. Content overflows. */ + + /* Hack to stop the height of this pushing the messages apart. + Replaces margin-top: -6px. This interacts better with a read + marker being in between. Content overflows. */ + height: 1px; + margin-right: 10px; } @@ -172,6 +177,9 @@ limitations under the License. cursor: pointer; top: 6px; right: 6px; + width: 19px; + height: 19px; + background-image: url($edit-button-url); } .mx_EventTile:hover .mx_EventTile_editButton, diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index 6488794d..ad18eb49 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -88,6 +88,8 @@ $event-notsent-color: #f44; // event timestamp $event-timestamp-color: #acacac; +$edit-button-url: "/img/icon_context_message.svg"; + // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-unverified-color: #e8bf37; diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 62e89a7b..e3e32e84 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -88,6 +88,8 @@ $event-notsent-color: #f44; // event timestamp $event-timestamp-color: #acacac; +$edit-button-url: "/img/icon_context_message_dark.svg"; + // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color $e2e-unverified-color: #e8bf37; diff --git a/src/skins/vector/img/icon_context_message.svg b/src/skins/vector/img/icon_context_message.svg index c3d39b1e..f2ceccfa 100644 --- a/src/skins/vector/img/icon_context_message.svg +++ b/src/skins/vector/img/icon_context_message.svg @@ -4,18 +4,11 @@ ED5D3E59-2561-4AC1-9B43-82FBC51767FC Created with sketchtool. - - - - - - - + + + diff --git a/src/skins/vector/img/icon_context_message_dark.svg b/src/skins/vector/img/icon_context_message_dark.svg new file mode 100644 index 00000000..b4336cc3 --- /dev/null +++ b/src/skins/vector/img/icon_context_message_dark.svg @@ -0,0 +1,15 @@ + + + + ED5D3E59-2561-4AC1-9B43-82FBC51767FC + Created with sketchtool. + + + + + + + + + + From 323c5d073289998ef4245a36032644927eaab351 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 17 Jan 2017 14:54:55 +0000 Subject: [PATCH 0065/1951] CSS for 'searching known users' --- .../css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css b/src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css index aa1dced8..fe72d57d 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/ChatInviteDialog.css @@ -63,3 +63,9 @@ limitations under the License. .mx_ChatInviteDialog_cancel object { pointer-events: none; } + +.mx_ChatInviteDialog_addressSelectHeader { + font-weight: bold; + line-height: 150%; + text-indent: 4px; +} From 6c88201e23b7c16a7732dea65a4009ad02efc18f Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Jan 2017 19:14:16 +0000 Subject: [PATCH 0067/1951] use ye olde rel='alternate stylesheets' for theming --- src/vector/index.html | 14 ++++++++++---- src/vector/index.js | 1 - webpack.config.js | 4 ++++ 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/vector/index.html b/src/vector/index.html index 73cdd2df..1895cc6b 100644 --- a/src/vector/index.html +++ b/src/vector/index.html @@ -20,14 +20,20 @@ - <% for(var i=0; i - - <% } %> + <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { + var file = htmlWebpackPlugin.files.css[i]; + if (file.match(/^theme-(?!light\.)/)) { + %> + + <% } else { %> + + <% } + } %>
    - <% for(var i=0; i + <% for (var i=0; i < htmlWebpackPlugin.files.js.length; i++) {%> <% } %> diff --git a/src/vector/index.js b/src/vector/index.js index 64b87698..0d254202 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -30,7 +30,6 @@ require('babel-polyfill'); // CSS requires: just putting them here for now as CSS is going to be // refactored "soon" anyway -require('../../build/dark.scss'); require('gemini-scrollbar/gemini-scrollbar.css'); require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); diff --git a/webpack.config.js b/webpack.config.js index 1f2a8a60..e05f877a 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -15,6 +15,10 @@ module.exports = { // point, so that it doesn't block the pageload, but that is a separate // problem) "olm": "./src/vector/olm-loader.js", + + // CSS themes + "theme-light": "./build/light.scss", + "theme-dark": "./build/dark.scss", }, module: { preLoaders: [ From 0cd895b417bdc1163511d993c6b24ba1fe0910f3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 17 Jan 2017 19:19:30 +0000 Subject: [PATCH 0068/1951] kill the moofle --- src/skins/vector/css/themes/dark.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/src/skins/vector/css/themes/dark.scss b/src/skins/vector/css/themes/dark.scss index 0c51486b..5a37d036 100644 --- a/src/skins/vector/css/themes/dark.scss +++ b/src/skins/vector/css/themes/dark.scss @@ -1,4 +1,3 @@ @import "_base.scss"; @import "_dark.scss"; @import "../_components.scss"; -// moofleasdadsasdadsa \ No newline at end of file From 5206410f2126b6b987ecb654bf13829f8a58016c Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 17 Jan 2017 22:25:02 +0000 Subject: [PATCH 0069/1951] Deployment script Factor some bits out of redeploy.py, so that they can be used in a deployment script suitable for riot.im/app. --- scripts/deploy.py | 158 ++++++++++++++++++++++++++++++++++++++++++++ scripts/redeploy.py | 107 +++++++----------------------- 2 files changed, 182 insertions(+), 83 deletions(-) create mode 100755 scripts/deploy.py diff --git a/scripts/deploy.py b/scripts/deploy.py new file mode 100755 index 00000000..dde13c54 --- /dev/null +++ b/scripts/deploy.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python +# +# download and unpack a riot-web tarball. +# +# Allows `bundles` to be extracted to a common directory, and a link to +# config.json to be added. + +from __future__ import print_function + +import argparse +import os +import os.path +import tarfile +import urllib + +class DeployException(Exception): + pass + +def create_relative_symlink(linkname, target): + relpath = os.path.relpath(target, os.path.dirname(linkname)) + print ("Symlink %s -> %s" % (linkname, relpath)) + os.symlink(relpath, linkname) + + +def move_bundles(source, dest): + """Move the contents of the 'bundles' directory to a common dir + + We check that we will not be overwriting anything before we proceed. + + Args: + source (str): path to 'bundles' within the extracted tarball + dest (str): target common directory + """ + + if not os.path.isdir(dest): + os.mkdir(dest) + + # build a map from source to destination, checking for non-existence as we go. + renames = {} + for f in os.listdir(source): + dst = os.path.join(dest, f) + if os.path.exists(dst): + raise DeployException( + "Not deploying. The bundle includes '%s' which we have previously deployed." + % f + ) + renames[os.path.join(source, f)] = dst + + for (src, dst) in renames.iteritems(): + print ("Move %s -> %s" % (src, dst)) + os.rename(src, dst) + +class Deployer: + def __init__(self): + self.packages_path = "." + self.bundles_path = None + self.should_clean = False + self.config_location = None + + def deploy(self, tarball, extract_path): + """Download a tarball if necessary, and unpack it + + Returns: + (str) the path to the unpacked deployment + """ + print("Deploying %s to %s" % (tarball, extract_path)) + + downloaded = False + if tarball.startswith("http://") or tarball.startswith("https://"): + tarball = self.download_file(tarball) + print("Downloaded file: %s" % tarball) + downloaded = True + + try: + with tarfile.open(tarball) as tar: + tar.extractall(extract_path) + finally: + if self.should_clean and downloaded: + os.remove(tarball) + + name_str = os.path.basename(tarball).replace(".tar.gz", "") + extracted_dir = os.path.join(extract_path, name_str) + print ("Extracted into: %s" % extracted_dir) + + if self.config_location: + create_relative_symlink( + target=self.config_location, + linkname=os.path.join(extracted_dir, 'config.json') + ) + + if self.bundles_path: + extracted_bundles = os.path.join(extracted_dir, 'bundles') + move_bundles(source=extracted_bundles, dest=self.bundles_path) + + # replace the (hopefully now empty) extracted_bundles dir with a + # symlink to the common dir. + os.rmdir(extracted_bundles) + create_relative_symlink( + target=self.bundles_path, + linkname=extracted_bundles, + ) + return extracted_dir + + def download_file(self, url): + if not os.path.isdir(self.packages_path): + os.mkdir(self.packages_path) + local_filename = os.path.join(self.packages_path, + url.split('/')[-1]) + urllib.urlretrieve(url, local_filename) + return local_filename + +if __name__ == "__main__": + parser = argparse.ArgumentParser("Deploy a Riot build on a web server.") + parser.add_argument( + "-p", "--packages-dir", default="./packages", help=( + "The directory to download the tarball into. (Default: '%(default)s')" + ) + ) + parser.add_argument( + "-e", "--extract-path", default="./deploys", help=( + "The location to extract .tar.gz files to. (Default: '%(default)s')" + ) + ) + parser.add_argument( + "-b", "--bundles-dir", nargs='?', default="./bundles", help=( + "A directory to move the contents of the 'bundles' directory to. A \ + symlink to the bundles directory will also be written inside the \ + extracted tarball. Example: './bundles'. \ + (Default: '%(default)s')" + ) + ) + parser.add_argument( + "-c", "--clean", action="store_true", default=False, help=( + "Remove .tar.gz files after they have been downloaded and extracted. \ + (Default: %(default)s)" + ) + ) + parser.add_argument( + "--config", nargs='?', default='./config.json', help=( + "Write a symlink at config.json in the extracted tarball to this \ + location. (Default: '%(default)s')" + ) + ) + parser.add_argument( + "tarball", help=( + "filename of tarball, or URL to download." + ), + ) + + args = parser.parse_args() + + deployer = Deployer() + deployer.packages_path = args.packages_dir + deployer.bundles_path = args.bundles_dir + deployer.should_clean = args.clean + deployer.config_location = args.config + + deployer.deploy(args.tarball, args.extract_path) diff --git a/scripts/redeploy.py b/scripts/redeploy.py index 4394f292..0796b963 100755 --- a/scripts/redeploy.py +++ b/scripts/redeploy.py @@ -14,32 +14,17 @@ from __future__ import print_function import json, requests, tarfile, argparse, os, errno import time from urlparse import urljoin + from flask import Flask, jsonify, request, abort +from deploy import Deployer, DeployException + app = Flask(__name__) arg_jenkins_url = None +deployer = None arg_extract_path = None -arg_bundles_path = None -arg_should_clean = None arg_symlink = None -arg_config_location = None - -class DeployException(Exception): - pass - -def download_file(url): - local_filename = url.split('/')[-1] - r = requests.get(url, stream=True) - with open(local_filename, 'wb') as f: - for chunk in r.iter_content(chunk_size=1024): - if chunk: # filter out keep-alive new chunks - f.write(chunk) - return local_filename - -def untar_to(tarball, dest): - with tarfile.open(tarball) as tar: - tar.extractall(dest) def create_symlink(source, linkname): try: @@ -137,17 +122,20 @@ def fetch_jenkins_build(job_name, build_num): # see half-written files. build_dir = os.path.join(arg_extract_path, "%s-#%s" % (job_name, build_num)) try: - deploy_tarball(tar_gz_url, build_dir) + extracted_dir = deploy_tarball(tar_gz_url, build_dir) except DeployException as e: abort(400, e.message) + create_symlink(source=extracted_dir, linkname=arg_symlink) + return jsonify({}) def deploy_tarball(tar_gz_url, build_dir): - """Download a tarball from jenkins and deploy it as the new version - """ - print("Deploying %s to %s" % (tar_gz_url, build_dir)) + """Download a tarball from jenkins and unpack it + Returns: + (str) the path to the unpacked deployment + """ if os.path.exists(build_dir): raise DeployException( "Not deploying. We have previously deployed this build." @@ -156,62 +144,8 @@ def deploy_tarball(tar_gz_url, build_dir): # we rely on the fact that flask only serves one request at a time to # ensure that we do not overwrite a tarball from a concurrent request. - filename = download_file(tar_gz_url) - print("Downloaded file: %s" % filename) - try: - untar_to(filename, build_dir) - print("Extracted to: %s" % build_dir) - finally: - if arg_should_clean: - os.remove(filename) - - name_str = filename.replace(".tar.gz", "") - extracted_dir = os.path.join(build_dir, name_str) - - if arg_config_location: - create_symlink(source=arg_config_location, linkname=os.path.join(extracted_dir, 'config.json')) - - if arg_bundles_path: - extracted_bundles = os.path.join(extracted_dir, 'bundles') - move_bundles(source=extracted_bundles, dest=arg_bundles_path) - - # replace the (hopefully now empty) extracted_bundles dir with a - # symlink to the common dir. - relpath = os.path.relpath(arg_bundles_path, extracted_dir) - os.rmdir(extracted_bundles) - print ("Symlink %s -> %s" % (extracted_bundles, relpath)) - os.symlink(relpath, extracted_bundles) - - create_symlink(source=extracted_dir, linkname=arg_symlink) - -def move_bundles(source, dest): - """Move the contents of the 'bundles' directory to a common dir - - We check that we will not be overwriting anything before we proceed. - - Args: - source (str): path to 'bundles' within the extracted tarball - dest (str): target common directory - """ - - if not os.path.isdir(dest): - os.mkdir(dest) - - # build a map from source to destination, checking for non-existence as we go. - renames = {} - for f in os.listdir(source): - dst = os.path.join(dest, f) - if os.path.exists(dst): - raise DeployException( - "Not deploying. The bundle includes '%s' which we have previously deployed." - % f - ) - renames[os.path.join(source, f)] = dst - - for (src, dst) in renames.iteritems(): - print ("Move %s -> %s" % (src, dst)) - os.rename(src, dst) + return deployer.deploy(tar_gz_url, build_dir) if __name__ == "__main__": @@ -270,21 +204,28 @@ if __name__ == "__main__": else: arg_jenkins_url = args.jenkins + "/" arg_extract_path = args.extract - arg_bundles_path = args.bundles_dir - arg_should_clean = args.clean arg_symlink = args.symlink - arg_config_location = args.config if not os.path.isdir(arg_extract_path): os.mkdir(arg_extract_path) + deployer = Deployer() + deployer.bundles_path = args.bundles_dir + deployer.should_clean = args.clean + deployer.config_location = args.config + if args.tarball_uri is not None: build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time())) deploy_tarball(args.tarball_uri, build_dir) else: print( "Listening on port %s. Extracting to %s%s. Symlinking to %s. Jenkins URL: %s. Config location: %s" % - (args.port, arg_extract_path, - " (clean after)" if arg_should_clean else "", arg_symlink, arg_jenkins_url, arg_config_location) + (args.port, + arg_extract_path, + " (clean after)" if deployer.should_clean else "", + arg_symlink, + arg_jenkins_url, + deployer.config_location, + ) ) app.run(host="0.0.0.0", port=args.port, debug=True) From 83145e80e5c17339f14dd4ec053843f5b40bbf1a Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 18 Jan 2017 10:39:59 +0000 Subject: [PATCH 0070/1951] Auto-hide the electron menu bar From https://github.com/vector-im/riot-web/issues/2962 This allows all the shortcuts to still work, and the menu bar can be un-hidden with the alt key. --- electron/src/electron-main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index a03a8755..929b7892 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -175,6 +175,7 @@ electron.app.on('ready', () => { icon: icon_path, width: 1024, height: 768, show: false, + autoHideMenuBar: true, }); mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`); electron.Menu.setApplicationMenu(VectorMenu); From 69bedf0d37384f3039a145180c4ba7834bfa45c7 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 12:42:48 +0000 Subject: [PATCH 0071/1951] review feedback --- package.json | 2 -- src/skins/vector/css/rethemendex.sh | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 1e273dfe..b8fd1574 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,6 @@ "babel-preset-es2017": "^6.16.0", "babel-preset-react": "^6.16.0", "babel-preset-stage-2": "^6.17.0", - "catw": "^1.0.1", "chokidar": "^1.6.1", "cpx": "^1.3.2", "css-raw-loader": "^0.1.1", @@ -113,7 +112,6 @@ "minimist": "^1.2.0", "mkdirp": "^0.5.1", "mocha": "^2.4.5", - "node-sass": "^4.1.1", "parallelshell": "^1.2.0", "phantomjs-prebuilt": "^2.1.7", "postcss-cli": "^2.6.0", diff --git a/src/skins/vector/css/rethemendex.sh b/src/skins/vector/css/rethemendex.sh index 915b235d..9381c5cd 100755 --- a/src/skins/vector/css/rethemendex.sh +++ b/src/skins/vector/css/rethemendex.sh @@ -2,7 +2,7 @@ echo "// autogenerated by rethemendex.sh" > _components.scss -for i in `find . -iname _\*.scss | fgrep -v _components.scss`; +for i in `find . -iname _\*.scss | fgrep -v _components.scss | sort`; do echo "@import \"$i\";" >> _components.scss done From 708fd640f7af9a641a5b3a66dacbb17392e8410c Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 14:06:20 +0000 Subject: [PATCH 0072/1951] unbreak switching after vdh's webpack changes --- src/vector/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vector/index.html b/src/vector/index.html index 1895cc6b..a6c3092d 100644 --- a/src/vector/index.html +++ b/src/vector/index.html @@ -22,9 +22,11 @@ <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { var file = htmlWebpackPlugin.files.css[i]; - if (file.match(/^theme-(?!light\.)/)) { + var match = file.match(/^bundles\/.*?\/theme-((?!light\.).*)\.css$/); + if (match) { + var title = match[1].charAt(0).toUpperCase() + match[1].slice(1); %> - + <% } else { %> <% } From f10bc8eef17a56b50ba4901184ad325fa181e04c Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Wed, 18 Jan 2017 16:49:20 +0100 Subject: [PATCH 0073/1951] Animate status bar max-height and margin-top When collapsed, the max-height is set to 0px. When expanded, max-height is set to 50px, margin-top is set to 0px. When expanded and when the timeline is not scrolled down to the bottom, margin-top is set to -50px to offset the change in height, keeping it at the same scroll position. Without the animation, there would be a jump when the user starts scrolling up from the bottom whilst the StatusBar is expanded. --- .../matrix-react-sdk/structures/RoomView.css | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css index c3f7ceed..3b3c396f 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css @@ -202,6 +202,25 @@ hr.mx_RoomView_myReadMarker { width: 100%; -webkit-flex: 0 0 auto; flex: 0 0 auto; + + max-height: 0px; + background-color: #fff; + z-index: 1000; + overflow: hidden; + + -webkit-transition: all .5s ease-in-out; + -moz-transition: all .5s ease-in-out; + -ms-transition: all .5s ease-in-out; + -o-transition: all .5s ease-in-out; +} + +.mx_RoomView_statusArea_expanded { + max-height: 50px; + margin-top: 0px; +} + +.mx_RoomView_statusArea_expanded.mx_RoomView_statusArea_mid_timeline { + margin-top: -50px; } .mx_RoomView_statusAreaBox { From e9884768f6fa770d6f33e9ab456688928ba50f39 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 18 Jan 2017 16:00:13 +0000 Subject: [PATCH 0074/1951] Persist console logs to an IndexedDB instance --- src/vector/index.js | 7 ++ src/vector/rageshake.js | 180 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 src/vector/rageshake.js diff --git a/src/vector/index.js b/src/vector/index.js index 8231950b..b965f01d 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -36,6 +36,13 @@ require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); require('draft-js/dist/Draft.css'); +const rageshake = require("./rageshake"); +rageshake.init().then(() => { + console.log("Initialised rageshake"); +}, (err) => { + console.error("Failed to initialise rageshake: " + err); +}); + // add React and ReactPerf to the global namespace, to make them easier to // access via the console diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js new file mode 100644 index 00000000..fe768630 --- /dev/null +++ b/src/vector/rageshake.js @@ -0,0 +1,180 @@ +/* +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. +*/ + +// This module contains all the code needed to log the console, persist it to disk and submit bug reports. Rationale is as follows: +// - Monkey-patching the console is preferable to having a log library because we can catch logs by other libraries more easily, +// without having to all depend on the same log framework / pass the logger around. +// - We use IndexedDB to persists logs because it has generous disk space limits compared to local storage. IndexedDB does not work +// in incognito mode, in which case this module will not be able to write logs to disk. However, the logs will still be stored +// in-memory, so can still be submitted in a bug report should the user wish to: we can also store more logs in-memory than in +// local storage, which does work in incognito mode. +// - Bug reports are sent as a POST over HTTPS: it purposefully does not use Matrix as bug reports may be made when Matrix is +// not responsive (which may be the cause of the bug). + +const FLUSH_RATE_MS = 30 * 1000; + +// A class which monkey-patches the global console and stores log lines. +class ConsoleLogger { + constructor() { + this.logs = ""; + + // Monkey-patch console logging + const consoleFunctionsToLevels = { + log: "I", + info: "I", + error: "E", + }; + Object.keys(consoleFunctionsToLevels).forEach((fnName) => { + const level = consoleFunctionsToLevels[fnName]; + let originalFn = window.console[fnName].bind(window.console); + window.console[fnName] = (...args) => { + this.log(level, ...args); + originalFn(...args); + } + }); + } + + log(level, ...args) { + // We don't know what locale the user may be running so use ISO strings + const ts = new Date().toISOString(); + // Some browsers support string formatting which we're not doing here + // so the lines are a little more ugly but easy to implement / quick to run. + // Example line: + // 2017-01-18T11:23:53.214Z W Failed to set badge count: Error setting badge. Message: Too many badges requests in queue. + const line = `${ts} ${level} ${args.join(' ')}\n`; + // Using + really is the quickest way in JS + // http://jsperf.com/concat-vs-plus-vs-join + this.logs += line; + } + + /** + * Retrieve log lines to flush to disk. + * @return {string} \n delimited log lines to flush. + */ + flush() { + // The ConsoleLogger doesn't care how these end up on disk, it just flushes them to the caller. + const logsToFlush = this.logs; + this.logs = ""; + return logsToFlush; + } +} + +// A class which stores log lines in an IndexedDB instance. +class IndexedDBLogStore { + constructor(indexedDB, logger) { + this.indexedDB = indexedDB; + this.logger = logger; + this.db = null; + } + + /** + * @return {Promise} Resolves when the store is ready. + */ + connect() { + let req = this.indexedDB.open("logs"); + return new Promise((resolve, reject) => { + req.onsuccess = (event) => { + this.db = event.target.result; + // Periodically flush logs to local storage / indexeddb + setInterval(this.flush.bind(this), FLUSH_RATE_MS); + resolve(); + }; + + req.onerror = (event) => { + const err = "Failed to open log database: " + event.target.errorCode; + console.error(err); + reject(new Error(err)); + }; + + // First time: Setup the object store + req.onupgradeneeded = (event) => { + const db = event.target.result; + const objectStore = db.createObjectStore("logs", { + autoIncrement: true + }) + objectStore.transaction.oncomplete = function(event) { + objectStore.add( + new Date() + " ::: Log database was created." + ); + }; + } + }); + } + + /** + * @return {Promise} Resolved when the logs have been flushed. + */ + flush() { + if (!this.db) { + // not connected yet or user rejected access for us to r/w to the db + return Promise.reject(new Error("No connected database")); + } + const lines = this.logger.flush(); + if (lines.length === 0) { + return Promise.resolve(); + } + return new Promise((resolve, reject) => { + let txn = this.db.transaction("logs", "readwrite"); + let objStore = txn.objectStore("logs"); + objStore.add(lines); + txn.oncomplete = (event) => { + resolve(); + }; + txn.onerror = (event) => { + console.error("Failed to flush logs : " + event.target.errorCode); + reject(new Error("Failed to write logs: " + event.target.errorCode)); + } + }); + } +} + + +let store = null; +let inited = false; +module.exports = { + + /** + * Configure rage shaking support for sending bug reports. + * Modifies globals. + */ + init: function() { + if (inited || !window.indexedDB) { + return; + } + store = new IndexedDBLogStore(window.indexedDB, new ConsoleLogger()); + inited = true; + return store.connect(); + }, + + /** + * Force-flush the logs to storage. + * @return {Promise} Resolved when the logs have been flushed. + */ + flush: function() { + if (!store) { + return; + } + return store.flush(); + }, + + /** + * Send a bug report. + * @param {string} userText Any additional user input. + * @return {Promise} Resolved when the bug report is sent. + */ + sendBugReport: function(userText) { + } +}; From acb85b7b7227e107e4f91e8e473b2e8097eb9559 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 16:06:02 +0000 Subject: [PATCH 0075/1951] f1x0r CSS comment --- src/vector/index.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index 0d254202..a3892248 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -28,8 +28,9 @@ limitations under the License. // https://babeljs.io/docs/plugins/transform-runtime/ require('babel-polyfill'); -// CSS requires: just putting them here for now as CSS is going to be -// refactored "soon" anyway +// Require common CSS here; this will make webpack process it into bundle.css. +// Our own CSS (which is themed) is imported via separate webpack entry points +// in webpack.config.js require('gemini-scrollbar/gemini-scrollbar.css'); require('gfm.css/gfm.css'); require('highlight.js/styles/github.css'); From 5828ab107707752c76934484bad96ad8b1ff35f5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 18 Jan 2017 16:27:11 +0000 Subject: [PATCH 0076/1951] Generate unique IDs for each JS runtime to accomodate multiple tabs --- src/vector/rageshake.js | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index fe768630..5739a934 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -20,7 +20,8 @@ limitations under the License. // - We use IndexedDB to persists logs because it has generous disk space limits compared to local storage. IndexedDB does not work // in incognito mode, in which case this module will not be able to write logs to disk. However, the logs will still be stored // in-memory, so can still be submitted in a bug report should the user wish to: we can also store more logs in-memory than in -// local storage, which does work in incognito mode. +// local storage, which does work in incognito mode. We also need to handle the case where there are 2+ tabs. Each JS runtime +// generates a random string which serves as the "ID" for that tab/session. These IDs are stored along with the log lines. // - Bug reports are sent as a POST over HTTPS: it purposefully does not use Matrix as bug reports may be made when Matrix is // not responsive (which may be the cause of the bug). @@ -77,6 +78,8 @@ class IndexedDBLogStore { constructor(indexedDB, logger) { this.indexedDB = indexedDB; this.logger = logger; + this.id = "instance-" + Date.now(); + this.index = 0; this.db = null; } @@ -103,13 +106,13 @@ class IndexedDBLogStore { req.onupgradeneeded = (event) => { const db = event.target.result; const objectStore = db.createObjectStore("logs", { - autoIncrement: true - }) - objectStore.transaction.oncomplete = function(event) { - objectStore.add( + keyPath: ["id", "index"] + }); + objectStore.add( + this._generateLogEntry( new Date() + " ::: Log database was created." - ); - }; + ) + ); } }); } @@ -129,7 +132,7 @@ class IndexedDBLogStore { return new Promise((resolve, reject) => { let txn = this.db.transaction("logs", "readwrite"); let objStore = txn.objectStore("logs"); - objStore.add(lines); + objStore.add(this._generateLogEntry(lines)); txn.oncomplete = (event) => { resolve(); }; @@ -139,6 +142,14 @@ class IndexedDBLogStore { } }); } + + _generateLogEntry(lines) { + return { + id: this.id, + lines: lines, + index: this.index++ + }; + } } @@ -176,5 +187,9 @@ module.exports = { * @return {Promise} Resolved when the bug report is sent. */ sendBugReport: function(userText) { + // To gather all the logs, we first query for every log entry with index "0", this will let us + // know all the IDs from different tabs/sessions. + + // Send logs grouped by ID } }; From f20efc57f3beb4e8f0bea05c04980bc4b35b7606 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 17:05:13 +0000 Subject: [PATCH 0077/1951] simple doc about theming --- docs/theming.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 docs/theming.md diff --git a/docs/theming.md b/docs/theming.md new file mode 100644 index 00000000..defb18bc --- /dev/null +++ b/docs/theming.md @@ -0,0 +1,25 @@ +Theming Riot +============ + +Themes are a very basic way of providing simple alternative look & feels to the +riot-web app via CSS & custom imagery. + +They are *NOT* co be confused with 'skins', which describe apps which sit on top +of matrix-react-sdk - e.g. in theory Riot itself is a react-sdk skin. +As of Jan 2017, skins are not fully supported; riot is the only available skin. + +To define a theme for Riot: + + 1. Pick a name, e.g. `teal`. at time of writing we have `light` and `dark`. + 2. Fork `src/skins/vector/css/themes/dark.scss` to be teal.scss + 3. Fork `src/skins/vector/css/themes/_base.scss` to be _teal.scss + 4. Override variables in _teal.css as desired. You may wish to delete ones + which don't differ from _base.scss, to make it clear which are being + overridden. If every single colour is being changed (as per _dark.scss) + then you might as well keep them all. + 5. Add the theme to the list of entrypoints in webpack.config.js + 6. Add the theme to the list of themes in matrix-react-sdk's UserSettings.js + 7. Sit back and admire your handywork. + +In future, the assets for a theme will probably be gathered together into a +single directory tree. \ No newline at end of file From 811086ac8ec1ea1b7cd5d5b475712d5277ce9af3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 18 Jan 2017 17:18:02 +0000 Subject: [PATCH 0078/1951] Comment how we should consume logs --- src/vector/rageshake.js | 44 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 5739a934..9b6106ed 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -23,7 +23,9 @@ limitations under the License. // local storage, which does work in incognito mode. We also need to handle the case where there are 2+ tabs. Each JS runtime // generates a random string which serves as the "ID" for that tab/session. These IDs are stored along with the log lines. // - Bug reports are sent as a POST over HTTPS: it purposefully does not use Matrix as bug reports may be made when Matrix is -// not responsive (which may be the cause of the bug). +// not responsive (which may be the cause of the bug). We send the most recent N MB of UTF-8 log data, starting with the most +// recent, which we know because the "ID"s are actually timestamps. We then purge the remaining logs. We also do this check +// on startup to prevent logs from accumulating. const FLUSH_RATE_MS = 30 * 1000; @@ -143,6 +145,39 @@ class IndexedDBLogStore { }); } + /** + * Consume the most recent logs and return them. Older logs which are not returned are deleted at the same time, + * so this can be called at startup to do house-keeping to keep the logs from growing too large. + * + * @param {boolean} clearAll True to clear the most recent logs returned in addition to the + * older logs. This is desirable when sending bug reports as we do not want to submit the + * same logs twice. This is not desirable when doing house-keeping at startup in case they + * want to submit a bug report later. + * @return {Promise} Resolves to an array of objects. The array is sorted in time (oldest first) based on + * when the log file was created (the log ID). The objects have said log ID in an "id" field and "lines" which is a + * big string with all the new-line delimited logs. + */ + consume(clearAll) { + const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB + // To gather all the logs, we first query for every log entry with index "0", this will let us + // know all the IDs from different tabs/sessions. + return new Promise((resolve, reject) => { + // let txn = this.db.transaction("logs", "readonly"); + // let objectStore = txn.objectStore("logs"); + + // we know each entry has a unique ID, and we know IDs are timestamps, so accumulate all the IDs, + // ignoring the logs for now, and sort them to work out the correct log ID ordering. + + // Starting with the most recent ID, fetch the logs (all indices) for said ID and accumulate them + // in order. After fetching ALL the logs for an ID, recheck the total length of the logs to work out + // if we have exceeded the max size cutoff for "recent" logs. + + // Remove all logs that are older than the cutoff (or the entire logs if clearAll is set). + + // Return the logs that are within the cutoff. + }); + } + _generateLogEntry(lines) { return { id: this.id, @@ -187,9 +222,8 @@ module.exports = { * @return {Promise} Resolved when the bug report is sent. */ sendBugReport: function(userText) { - // To gather all the logs, we first query for every log entry with index "0", this will let us - // know all the IDs from different tabs/sessions. - - // Send logs grouped by ID + return store.consume(true).then((data) => { + // Send logs grouped by ID + }); } }; From ec1a2f6abfd73d8b181401eb37285aaf8a9cb55e Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 19:29:53 +0000 Subject: [PATCH 0079/1951] fix CSS regression in searchbox --- .../vector/css/matrix-react-sdk/structures/_SearchBox.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss index bd335f60..382a07f9 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss @@ -44,7 +44,7 @@ limitations under the License. height: 24px; border: 0px ! important; /* border-bottom: 1px solid rgba(0, 0, 0, 0.1) ! important; */ - background-color: transparent; + background-color: transparent ! important; border: 0px; } From 5b95986705d9958545ad5ed4f94ed169da4eefa1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 20:05:18 +0000 Subject: [PATCH 0080/1951] fix various SCSS snafus --- src/skins/vector/css/_common.scss | 4 ++-- .../vector/css/matrix-react-sdk/structures/_SearchBox.scss | 1 - src/skins/vector/img/icon-call.svg | 2 +- src/skins/vector/img/icons-close-button.svg | 2 +- src/skins/vector/img/icons-search.svg | 2 +- src/skins/vector/img/icons-upload.svg | 2 +- src/skins/vector/img/icons-video.svg | 2 +- 7 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 01ba81d8..94362a2f 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -55,8 +55,8 @@ a:visited { color: $accent-color; } -input[type=text] { - background-color: $primary-bg-color; +input[type=text], input[type=password] { + background-color: transparent; color: $primary-fg-color; } diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss index 382a07f9..0f34f056 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_SearchBox.scss @@ -44,7 +44,6 @@ limitations under the License. height: 24px; border: 0px ! important; /* border-bottom: 1px solid rgba(0, 0, 0, 0.1) ! important; */ - background-color: transparent ! important; border: 0px; } diff --git a/src/skins/vector/img/icon-call.svg b/src/skins/vector/img/icon-call.svg index 2d96b145..0ca5c29e 100644 --- a/src/skins/vector/img/icon-call.svg +++ b/src/skins/vector/img/icon-call.svg @@ -2,7 +2,7 @@ - + diff --git a/src/skins/vector/img/icons-close-button.svg b/src/skins/vector/img/icons-close-button.svg index f17940f5..f960d73a 100644 --- a/src/skins/vector/img/icons-close-button.svg +++ b/src/skins/vector/img/icons-close-button.svg @@ -7,7 +7,7 @@ - + diff --git a/src/skins/vector/img/icons-search.svg b/src/skins/vector/img/icons-search.svg index 4f5002ab..d85709e6 100644 --- a/src/skins/vector/img/icons-search.svg +++ b/src/skins/vector/img/icons-search.svg @@ -2,7 +2,7 @@ - + diff --git a/src/skins/vector/img/icons-upload.svg b/src/skins/vector/img/icons-upload.svg index 9074fcf9..b0101e87 100644 --- a/src/skins/vector/img/icons-upload.svg +++ b/src/skins/vector/img/icons-upload.svg @@ -2,7 +2,7 @@ - + diff --git a/src/skins/vector/img/icons-video.svg b/src/skins/vector/img/icons-video.svg index a35df49b..d367f496 100644 --- a/src/skins/vector/img/icons-video.svg +++ b/src/skins/vector/img/icons-video.svg @@ -7,7 +7,7 @@ - + From 237f4df08eeb0333c451928d5af4adacd1000aca Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 18 Jan 2017 21:30:53 +0000 Subject: [PATCH 0081/1951] A couple of tweaks to the karma config * allow the imgs to be served from the karma server to avoid 404s * Use the source-map-loader for js --- karma.conf.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 24742169..901832c7 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -29,12 +29,22 @@ module.exports = function (config) { files: [ 'node_modules/babel-polyfill/browser.js', testFile, - {pattern: 'vector/img/*', watched: false, included: false, served: true, nocache: false}, + + // make the images available via our httpd. They will be avaliable + // below http://localhost:[PORT]/base/. See also `proxies` which + // defines alternative URLs for them. + // + // This isn't required by any of the tests, but it stops karma + // logging warnings when it serves a 404 for them. + { + pattern: 'src/skins/vector/img/*', + watched: false, included: false, served: true, nocache: false, + }, ], - // redirect img links to the karma server proxies: { - "/img/": "/base/vector/img/", + // redirect img links to the karma server. See above. + "/img/": "/base/src/skins/vector/img/", }, // preprocess matching files before serving them to the browser @@ -86,6 +96,12 @@ module.exports = function (config) { webpack: { module: { + preLoaders: [ + // use the source-map-loader for javascript. This means + // that we have a better chance of seeing line numbers from + // the pre-babeled source. + { test: /\.js$/, loader: "source-map-loader" }, + ], loaders: [ { test: /\.json$/, loader: "json" }, { From c5032ba1bc4a7cde6ccd69c370ab336b7d2deac8 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Wed, 18 Jan 2017 22:46:12 +0000 Subject: [PATCH 0082/1951] Update theming.md --- docs/theming.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/theming.md b/docs/theming.md index defb18bc..c6373e52 100644 --- a/docs/theming.md +++ b/docs/theming.md @@ -13,7 +13,7 @@ To define a theme for Riot: 1. Pick a name, e.g. `teal`. at time of writing we have `light` and `dark`. 2. Fork `src/skins/vector/css/themes/dark.scss` to be teal.scss 3. Fork `src/skins/vector/css/themes/_base.scss` to be _teal.scss - 4. Override variables in _teal.css as desired. You may wish to delete ones + 4. Override variables in _teal.scss as desired. You may wish to delete ones which don't differ from _base.scss, to make it clear which are being overridden. If every single colour is being changed (as per _dark.scss) then you might as well keep them all. @@ -22,4 +22,4 @@ To define a theme for Riot: 7. Sit back and admire your handywork. In future, the assets for a theme will probably be gathered together into a -single directory tree. \ No newline at end of file +single directory tree. From d1fbbf90c088f5d97a2b82882f8755dab5b03d5f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 18 Jan 2017 21:51:49 +0000 Subject: [PATCH 0083/1951] Verify PGP signatures on tarballs when deploying --- scripts/deploy.py | 35 ++++++++++++++++++++++++++++++----- scripts/redeploy.py | 6 ++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/scripts/deploy.py b/scripts/deploy.py index dde13c54..c96b46e8 100755 --- a/scripts/deploy.py +++ b/scripts/deploy.py @@ -10,8 +10,16 @@ from __future__ import print_function import argparse import os import os.path +import subprocess +import sys import tarfile -import urllib + +try: + # python3 + from urllib.request import urlretrieve +except ImportError: + # python2 + from urllib import urlretrieve class DeployException(Exception): pass @@ -56,6 +64,7 @@ class Deployer: self.bundles_path = None self.should_clean = False self.config_location = None + self.verify_signature = True def deploy(self, tarball, extract_path): """Download a tarball if necessary, and unpack it @@ -65,9 +74,15 @@ class Deployer: """ print("Deploying %s to %s" % (tarball, extract_path)) + name_str = os.path.basename(tarball).replace(".tar.gz", "") + extracted_dir = os.path.join(extract_path, name_str) + if os.path.exists(extracted_dir): + raise DeployException('Cannot unpack %s: %s already exists' % ( + tarball, extracted_dir)) + downloaded = False if tarball.startswith("http://") or tarball.startswith("https://"): - tarball = self.download_file(tarball) + tarball = self.download_and_verify(tarball) print("Downloaded file: %s" % tarball) downloaded = True @@ -78,8 +93,6 @@ class Deployer: if self.should_clean and downloaded: os.remove(tarball) - name_str = os.path.basename(tarball).replace(".tar.gz", "") - extracted_dir = os.path.join(extract_path, name_str) print ("Extracted into: %s" % extracted_dir) if self.config_location: @@ -101,12 +114,24 @@ class Deployer: ) return extracted_dir + def download_and_verify(self, url): + tarball = self.download_file(url) + + if self.verify_signature: + sigfile = self.download_file(url + ".asc") + subprocess.check_call(["gpg", "--verify", sigfile, tarball]) + + return tarball + def download_file(self, url): if not os.path.isdir(self.packages_path): os.mkdir(self.packages_path) local_filename = os.path.join(self.packages_path, url.split('/')[-1]) - urllib.urlretrieve(url, local_filename) + sys.stdout.write("Downloading %s -> %s..." % (url, local_filename)) + sys.stdout.flush() + urlretrieve(url, local_filename) + print ("Done") return local_filename if __name__ == "__main__": diff --git a/scripts/redeploy.py b/scripts/redeploy.py index 0796b963..598f6c52 100755 --- a/scripts/redeploy.py +++ b/scripts/redeploy.py @@ -214,6 +214,12 @@ if __name__ == "__main__": deployer.should_clean = args.clean deployer.config_location = args.config + # we don't pgp-sign jenkins artifacts; instead we rely on HTTPS access to + # the jenkins server (and the jenkins server not being compromised and/or + # github not serving it compromised source). If that's not good enough for + # you, don't use riot.im/develop. + deployer.verify_signature = False + if args.tarball_uri is not None: build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time())) deploy_tarball(args.tarball_uri, build_dir) From c3fa6ff8053f047d6d8dd8270dbb243abed5d587 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 19 Jan 2017 02:48:17 +0000 Subject: [PATCH 0084/1951] Use the postcss-webpack-loader Use postcss-webpack-loader instead of webpack-cli to process the scss. Doing so mostly means that we avoid the problem that webpack-dev-server fails to start if we haven't already built the CSS. (It also simplifies package.json somewhat, which is no bad thing) --- package.json | 14 ++++++-------- postcss.config.js | 13 +++++++++++++ postcss.config.json | 15 --------------- webpack.config.js | 26 +++++++++++++++++++++----- 4 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 postcss.config.js delete mode 100644 postcss.config.json diff --git a/package.json b/package.json index 722e0cf0..84e05478 100644 --- a/package.json +++ b/package.json @@ -29,21 +29,19 @@ "reskindex": "reskindex -h src/header", "build:res": "node scripts/copy-res.js", "build:modernizr": "modernizr -c .modernizr.json -d src/vector/modernizr.js", - "build:css": "mkdirp build && postcss -c postcss.config.json", "build:compile": "babel --source-maps -d lib src", "build:bundle": "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:css && npm run build:bundle", - "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:css && npm run build:bundle:dev", + "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", "dist": "scripts/package.sh", "start:res": "node scripts/copy-res.js -w", "start:js": "webpack-dev-server -w --progress", "start:js:prod": "NODE_ENV=production webpack-dev-server -w --progress", - "start:css": "mkdirp build && postcss -c postcss.config.json -w", - "start": "node scripts/babelcheck.js && parallelshell \"npm run start:res\" \"npm run start:js\" \"npm run start:css\"", - "start:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\" \"npm run start:css\"", - "clean": "rimraf build lib webapp electron/dist", + "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\"", + "clean": "rimraf lib webapp electron/dist", "prepublish": "npm run build:compile", "test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false", "test:multi": "karma start" @@ -114,9 +112,9 @@ "mocha": "^2.4.5", "parallelshell": "^1.2.0", "phantomjs-prebuilt": "^2.1.7", - "postcss-cli": "^2.6.0", "postcss-extend": "^1.0.5", "postcss-import": "^9.0.0", + "postcss-loader": "^1.2.2", "postcss-mixins": "^5.4.1", "postcss-nested": "^1.0.0", "postcss-scss": "^0.4.0", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..5305d9ed --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,13 @@ +module.exports = { + plugins: [ + require("postcss-import")(), + require("autoprefixer")(), + require("postcss-simple-vars")(), + require("postcss-extend")(), + require("postcss-nested")(), + require("postcss-mixins")(), + require("postcss-strip-inline-comments")(), + ], + "parser": "postcss-scss", + "local-plugins": true, +}; diff --git a/postcss.config.json b/postcss.config.json deleted file mode 100644 index 7ed32cda..00000000 --- a/postcss.config.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "use": [ - "postcss-import", - "autoprefixer", - "postcss-simple-vars", - "postcss-extend", - "postcss-nested", - "postcss-mixins", - "postcss-strip-inline-comments" - ], - "parser": "postcss-scss", - "input": "src/skins/vector/css/themes/[^_]*.scss", - "dir": "build", - "local-plugins": true -} diff --git a/webpack.config.js b/webpack.config.js index 3a701965..136e0af7 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -17,8 +17,8 @@ module.exports = { "olm": "./src/vector/olm-loader.js", // CSS themes - "theme-light": "./build/light.scss", - "theme-dark": "./build/dark.scss", + "theme-light": "./src/skins/vector/css/themes/light.scss", + "theme-dark": "./src/skins/vector/css/themes/dark.scss" }, module: { preLoaders: [ @@ -27,9 +27,25 @@ module.exports = { loaders: [ { test: /\.json$/, loader: "json" }, { test: /\.js$/, loader: "babel", include: path.resolve('./src') }, - // css-raw-loader loads CSS but doesn't try to treat url()s as require()s - // we're assuming that postcss has already preprocessed SCSS by this point - { test: /\.s?css$/, loader: ExtractTextPlugin.extract("css-raw-loader") }, + { + test: /\.scss$/, + + // 1. postcss-loader turns the SCSS into normal CSS. + // 2. css-raw-loader turns the CSS into a javascript module + // whose default export is a string containing the CSS. + // (css-raw-loader is similar to css-loader, but the latter + // 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" + ), + }, + { + // this works similarly to the scss case, without postcss. + test: /\.css$/, + loader: ExtractTextPlugin.extract("css-raw-loader"), + }, ], noParse: [ // don't parse the languages within highlight.js. They cause stack From e08f97a549925ee6bada12963dac1afdae0de714 Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Thu, 19 Jan 2017 11:52:15 +0100 Subject: [PATCH 0085/1951] Reduce animation time to 200ms --- .../vector/css/matrix-react-sdk/structures/RoomView.css | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css index 3b3c396f..b7fe0441 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css @@ -208,10 +208,10 @@ hr.mx_RoomView_myReadMarker { z-index: 1000; overflow: hidden; - -webkit-transition: all .5s ease-in-out; - -moz-transition: all .5s ease-in-out; - -ms-transition: all .5s ease-in-out; - -o-transition: all .5s ease-in-out; + -webkit-transition: all .2s ease-in-out; + -moz-transition: all .2s ease-in-out; + -ms-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; } .mx_RoomView_statusArea_expanded { From 6b1d1389187c3a0904ae2c04097033cd7631a12d Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Thu, 19 Jan 2017 12:45:26 +0100 Subject: [PATCH 0086/1951] Make scrolling to bottom whilst expanded seem less weird inertially --- .../css/matrix-react-sdk/structures/RoomView.css | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css index b7fe0441..6dc22e2f 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css @@ -208,10 +208,10 @@ hr.mx_RoomView_myReadMarker { z-index: 1000; overflow: hidden; - -webkit-transition: all .2s ease-in-out; - -moz-transition: all .2s ease-in-out; - -ms-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; + -webkit-transition: all .2s ease-out; + -moz-transition: all .2s ease-out; + -ms-transition: all .2s ease-out; + -o-transition: all .2s ease-out; } .mx_RoomView_statusArea_expanded { @@ -221,6 +221,11 @@ hr.mx_RoomView_myReadMarker { .mx_RoomView_statusArea_expanded.mx_RoomView_statusArea_mid_timeline { margin-top: -50px; + + -webkit-transition: all .2s ease-in; + -moz-transition: all .2s ease-in; + -ms-transition: all .2s ease-in; + -o-transition: all .2s ease-in; } .mx_RoomView_statusAreaBox { From 89d514a532827f5eb576a43138228a96a1606c02 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 12:02:19 +0000 Subject: [PATCH 0087/1951] Query IndexedDB for all results with index=0 --- src/vector/rageshake.js | 46 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 9b6106ed..65462d15 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -24,7 +24,7 @@ limitations under the License. // generates a random string which serves as the "ID" for that tab/session. These IDs are stored along with the log lines. // - Bug reports are sent as a POST over HTTPS: it purposefully does not use Matrix as bug reports may be made when Matrix is // not responsive (which may be the cause of the bug). We send the most recent N MB of UTF-8 log data, starting with the most -// recent, which we know because the "ID"s are actually timestamps. We then purge the remaining logs. We also do this check +// recent, which we know because the "ID"s are actually timestamps. We then purge the remaining logs. We also do this purge // on startup to prevent logs from accumulating. const FLUSH_RATE_MS = 30 * 1000; @@ -38,6 +38,7 @@ class ConsoleLogger { const consoleFunctionsToLevels = { log: "I", info: "I", + warn: "W", error: "E", }; Object.keys(consoleFunctionsToLevels).forEach((fnName) => { @@ -110,6 +111,12 @@ class IndexedDBLogStore { const objectStore = db.createObjectStore("logs", { keyPath: ["id", "index"] }); + // Keys in the database look like: [ "instance-148938490", 0 ] + // Later on we need to query for everything with index=0, and query everything for an instance id. + // In order to do this, we need to set up indexes on both "id" and "index". + objectStore.createIndex("index", "index", { unique: false }); + objectStore.createIndex("id", "id", { unique: false }); + objectStore.add( this._generateLogEntry( new Date() + " ::: Log database was created." @@ -161,9 +168,11 @@ class IndexedDBLogStore { const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB // To gather all the logs, we first query for every log entry with index "0", this will let us // know all the IDs from different tabs/sessions. - return new Promise((resolve, reject) => { - // let txn = this.db.transaction("logs", "readonly"); - // let objectStore = txn.objectStore("logs"); + const txn = this.db.transaction("logs", "readonly"); + const objectStore = txn.objectStore("logs"); + return selectQuery(objectStore.index("index"), IDBKeyRange.only(0), (cursor) => cursor.value.id).then((res) => { + console.log("Instances: ", res); + }); // we know each entry has a unique ID, and we know IDs are timestamps, so accumulate all the IDs, // ignoring the logs for now, and sort them to work out the correct log ID ordering. @@ -175,7 +184,6 @@ class IndexedDBLogStore { // Remove all logs that are older than the cutoff (or the entire logs if clearAll is set). // Return the logs that are within the cutoff. - }); } _generateLogEntry(lines) { @@ -187,6 +195,34 @@ class IndexedDBLogStore { } } +/** + * Helper method to collect results from a Cursor and promiseify it. + * @param {ObjectStore|Index} store The store to perform openCursor on. + * @param {IDBKeyRange=} keyRange Optional key range to apply on the cursor. + * @param {Function} resultMapper A function which is repeatedly called with a Cursor. + * Return the data you want to keep. + * @return {Promise} Resolves to an array of whatever you returned from resultMapper. + */ +function selectQuery(store, keyRange, resultMapper) { + const query = store.openCursor(keyRange); + return new Promise((resolve, reject) => { + let results = []; + query.onerror = (event) => { + reject(new Error("Query failed: " + event.target.errorCode)); + }; + // collect results + query.onsuccess = (event) => { + const cursor = event.target.result; + if (!cursor) { + resolve(results); + return; // end of results + } + results.push(resultMapper(cursor)); + cursor.continue(); + } + }); +} + let store = null; let inited = false; From 231adbcd311359b858dbb7dd7911d4dab9b21891 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2017 13:02:08 +0000 Subject: [PATCH 0088/1951] Add copyright header --- electron/src/squirrelhooks.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/electron/src/squirrelhooks.js b/electron/src/squirrelhooks.js index ca0983b6..15ed670f 100644 --- a/electron/src/squirrelhooks.js +++ b/electron/src/squirrelhooks.js @@ -1,3 +1,19 @@ +/* +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. +*/ + const path = require('path'); const spawn = require('child_process').spawn; const app = require('electron').app; From bee4ca2b28babf3cf43ec3c744a6b4e10bf3e624 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2017 13:25:56 +0000 Subject: [PATCH 0089/1951] Fixes to electron desktop notifs Merge the notification part of https://github.com/vector-im/riot-web/pull/2960 * Show and focus the window when the notification is clicked, rather than just restoring it. * Implement requestNotificationPermission and return a resolved promise (although in practice it should never be called) --- src/vector/platform/ElectronPlatform.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vector/platform/ElectronPlatform.js b/src/vector/platform/ElectronPlatform.js index 68df88b0..47bfd990 100644 --- a/src/vector/platform/ElectronPlatform.js +++ b/src/vector/platform/ElectronPlatform.js @@ -97,7 +97,10 @@ export default class ElectronPlatform extends VectorBasePlatform { room_id: room.roomId }); global.focus(); - electron.remote.getCurrentWindow().restore(); + const currentWin = electron.remote.getCurrentWindow(); + currentWin.show(); + currentWin.restore(); + currentWin.focus(); }; return notification; @@ -131,4 +134,8 @@ export default class ElectronPlatform extends VectorBasePlatform { screenCaptureErrorString() { return null; } + + requestNotificationPermission() : Promise { + return q('granted'); + } } From d11bcdad5fdd80c9eed34328a566fb707111b6b6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2017 14:29:07 +0000 Subject: [PATCH 0090/1951] Add electron tray icon From https://github.com/vector-im/riot-web/pull/2960 Makes riot minimise to the tray on windows / linux. --- electron/src/electron-main.js | 10 +++++- electron/src/tray.js | 67 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 electron/src/tray.js diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 929b7892..653dfd46 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -26,6 +26,8 @@ if (check_squirrel_hooks()) return; const electron = require('electron'); const url = require('url'); +const tray = require('./tray'); + const VectorMenu = require('./vectormenu'); let vectorConfig = {}; @@ -180,6 +182,12 @@ electron.app.on('ready', () => { mainWindow.loadURL(`file://${__dirname}/../../webapp/index.html`); electron.Menu.setApplicationMenu(VectorMenu); + // Create trayIcon icon + tray.create(mainWindow, { + icon_path: icon_path, + brand: vectorConfig.brand || 'Riot' + }); + mainWindow.once('ready-to-show', () => { mainWindow.show(); }); @@ -187,7 +195,7 @@ electron.app.on('ready', () => { mainWindow = null; }); mainWindow.on('close', (e) => { - if (process.platform == 'darwin' && !appQuitting) { + if (!appQuitting && (tray.hasTray() || process.platform == 'darwin')) { // On Mac, closing the window just hides it // (this is generally how single-window Mac apps // behave, eg. Mail.app) diff --git a/electron/src/tray.js b/electron/src/tray.js new file mode 100644 index 00000000..2ccdf40c --- /dev/null +++ b/electron/src/tray.js @@ -0,0 +1,67 @@ +/* +Copyright 2017 Karl Glatz +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. +*/ + +const path = require('path'); +const electron = require('electron'); + +const app = electron.app; +const Tray = electron.Tray; +const MenuItem = electron.MenuItem; + +let trayIcon = null; + +exports.hasTray = function hasTray() { + return (trayIcon !== null); +} + +exports.create = function (win, config) { + // no trays on darwin + if (process.platform === 'darwin' || trayIcon) { + return; + } + + const toggleWin = function () { + if (win.isVisible() && !win.isMinimized()) { + win.hide(); + } else { + if (win.isMinimized()) win.restore(); + if (!win.isVisible()) win.show(); + win.focus(); + } + }; + + const contextMenu = electron.Menu.buildFromTemplate([ + { + label: 'Show/Hide ' + config.brand, + click: toggleWin + }, + { + type: 'separator' + }, + { + label: 'Quit', + click: function () { + app.quit(); + } + } + ]); + + trayIcon = new Tray(config.icon_path); + trayIcon.setToolTip(config.brand); + trayIcon.setContextMenu(contextMenu); + trayIcon.on('click', toggleWin); +}; From 608c1b02081a75617a9d9a9ed483a962c7f0940a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 15:03:47 +0000 Subject: [PATCH 0091/1951] Finish implementing consume() - Fetches all logs in order and concatenates correctly. - Purges old logs correctly. --- src/vector/index.js | 1 + src/vector/rageshake.js | 133 ++++++++++++++++++++++++++++++++++------ 2 files changed, 116 insertions(+), 18 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index b965f01d..d773db85 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -39,6 +39,7 @@ require('draft-js/dist/Draft.css'); const rageshake = require("./rageshake"); rageshake.init().then(() => { console.log("Initialised rageshake"); + rageshake.sendBugReport(); }, (err) => { console.error("Failed to initialise rageshake: " + err); }); diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 65462d15..6093515d 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -166,24 +166,120 @@ class IndexedDBLogStore { */ consume(clearAll) { const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB - // To gather all the logs, we first query for every log entry with index "0", this will let us - // know all the IDs from different tabs/sessions. - const txn = this.db.transaction("logs", "readonly"); - const objectStore = txn.objectStore("logs"); - return selectQuery(objectStore.index("index"), IDBKeyRange.only(0), (cursor) => cursor.value.id).then((res) => { - console.log("Instances: ", res); + const db = this.db; + + // Returns: a string representing the concatenated logs for this ID. + function fetchLogs(id) { + const o = db.transaction("logs", "readonly").objectStore("logs"); + return selectQuery(o.index("id"), IDBKeyRange.only(id), (cursor) => { + return { + lines: cursor.value.lines, + index: cursor.value.index, + } + }).then((linesArray) => { + // We have been storing logs periodically, so string them all together *in order of index* now + linesArray.sort((a, b) => { + return a.index - b.index; + }) + return linesArray.map((l) => l.lines).join(""); + }); + } + + // Returns: A sorted array of log IDs. (newest first) + function fetchLogIds() { + // To gather all the log IDs, query for every log entry with index "0", this will let us + // know all the IDs from different tabs/sessions. + const o = db.transaction("logs", "readonly").objectStore("logs"); + return selectQuery(o.index("index"), IDBKeyRange.only(0), (cursor) => cursor.value.id).then((res) => { + // we know each entry has a unique ID, and we know IDs are timestamps, so accumulate all the IDs, + // ignoring the logs for now, and sort them to work out the correct log ID ordering, newest first. + // E.g. [ "instance-1484827160051", "instance-1374827160051", "instance-1000007160051"] + return res.sort().reverse(); + }); + } + + function deleteLogs(id) { + return new Promise((resolve, reject) => { + const txn = db.transaction("logs", "readwrite"); + const o = txn.objectStore("logs"); + // only load the key path, not the data which may be huge + const query = o.index("id").openKeyCursor(IDBKeyRange.only(id)); + query.onsuccess = (event) => { + const cursor = event.target.result; + if (!cursor) { + return; + } + o.delete(cursor.primaryKey); + cursor.continue(); + } + txn.oncomplete = () => { + resolve(); + }; + txn.onerror = (event) => { + reject(new Error(`Failed to delete logs for '${id}' : ${event.target.errorCode}`)); + } + }); + } + + // Ideally we'd just use coroutines and a for loop but riot-web doesn't support async/await so instead + // recursively fetch logs up to the given threshold. We can't cheat and fetch all the logs + // from all time, but we may OOM if we do so. + // Returns: Promise : Each object having 'id' and 'lines'. Same ordering as logIds. + function fetchLogsToThreshold(logIds, threshold, logs) { + // Base case: check log size and return if bigger than threshold + let size = 0; + logs.forEach((l) => { + size += l.lines.length; + }); + if (size > threshold) { + return Promise.resolve(logs); + } + + // fetch logs for the first element + let logId = logIds.shift(); + if (!logId) { + // no more entries + return Promise.resolve(logs); + } + return fetchLogs(logId).then((lines) => { + // add result to logs + logs.push({ + lines: lines, + id: logId, + }); + // recurse with the next log ID. TODO: Stack overflow risk? + return fetchLogsToThreshold(logIds, threshold, logs); + }) + } + + let allLogIds = []; + return fetchLogIds().then((logIds) => { + allLogIds = logIds.map((id) => id); // deep copy array as we'll modify it when fetching logs + return fetchLogsToThreshold(logIds, MAX_LOG_SIZE, []); + }).then((logs) => { + // Remove all logs that are beyond the threshold (not in logs), or the entire logs if clearAll was set. + let removeLogIds = allLogIds; + if (!clearAll) { + removeLogIds = removeLogIds.filter((id) => { + for (let i = 0; i < logs.length; i++) { + if (logs[i].id === id) { + return false; // do not remove logs that we're about to return to the caller. + } + } + return true; + }); + } + if (removeLogIds.length > 0) { + console.log("Removing logs: ", removeLogIds); + // Don't promise chain this because it's non-fatal if we can't clean up logs. + Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => { + console.log(`Removed ${removeLogIds.length} old logs.`); + }, (err) => { + console.error(err); + }) + } + return logs; }); - - // we know each entry has a unique ID, and we know IDs are timestamps, so accumulate all the IDs, - // ignoring the logs for now, and sort them to work out the correct log ID ordering. - - // Starting with the most recent ID, fetch the logs (all indices) for said ID and accumulate them - // in order. After fetching ALL the logs for an ID, recheck the total length of the logs to work out - // if we have exceeded the max size cutoff for "recent" logs. - - // Remove all logs that are older than the cutoff (or the entire logs if clearAll is set). - - // Return the logs that are within the cutoff. } _generateLogEntry(lines) { @@ -258,8 +354,9 @@ module.exports = { * @return {Promise} Resolved when the bug report is sent. */ sendBugReport: function(userText) { - return store.consume(true).then((data) => { + return store.consume(false).then((logs) => { // Send logs grouped by ID + console.log(logs); }); } }; From 61c5253dbf41fed7deec32d445bfa3c222833a7a Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 19 Jan 2017 15:40:54 +0000 Subject: [PATCH 0092/1951] Give the 'Light' theme link a title too ... mostly to make it appear on the Firefox style menu. --- src/vector/index.html | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vector/index.html b/src/vector/index.html index a6c3092d..3ab667cc 100644 --- a/src/vector/index.html +++ b/src/vector/index.html @@ -22,11 +22,13 @@ <% for (var i=0; i < htmlWebpackPlugin.files.css.length; i++) { var file = htmlWebpackPlugin.files.css[i]; - var match = file.match(/^bundles\/.*?\/theme-((?!light\.).*)\.css$/); + var match = file.match(/^bundles\/.*?\/theme-(.*)\.css$/); if (match) { var title = match[1].charAt(0).toUpperCase() + match[1].slice(1); + var light = match[1] == 'light'; %> - + <% } else { %> <% } From 81d437ac1e16104f32fef3c0d694a215fb7129a5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 15:47:55 +0000 Subject: [PATCH 0093/1951] POST reports to localhost for now. Also send live console logs --- src/vector/index.js | 2 +- src/vector/rageshake.js | 75 +++++++++++++++++++++++++++++++++++------ 2 files changed, 66 insertions(+), 11 deletions(-) diff --git a/src/vector/index.js b/src/vector/index.js index d773db85..d1d457e9 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -39,7 +39,7 @@ require('draft-js/dist/Draft.css'); const rageshake = require("./rageshake"); rageshake.init().then(() => { console.log("Initialised rageshake"); - rageshake.sendBugReport(); + rageshake.cleanup(); }, (err) => { console.error("Failed to initialise rageshake: " + err); }); diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 6093515d..1f3dbc7d 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -14,6 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ +import request from "browser-request"; + // This module contains all the code needed to log the console, persist it to disk and submit bug reports. Rationale is as follows: // - Monkey-patching the console is preferable to having a log library because we can catch logs by other libraries more easily, // without having to all depend on the same log framework / pass the logger around. @@ -321,20 +323,27 @@ function selectQuery(store, keyRange, resultMapper) { let store = null; -let inited = false; +let logger = null; +let initPromise = null; module.exports = { /** * Configure rage shaking support for sending bug reports. * Modifies globals. + * @return {Promise} Resolves when set up. */ init: function() { - if (inited || !window.indexedDB) { - return; + if (initPromise) { + return initPromise; } - store = new IndexedDBLogStore(window.indexedDB, new ConsoleLogger()); - inited = true; - return store.connect(); + logger = new ConsoleLogger(); + if (window.indexedDB) { + store = new IndexedDBLogStore(window.indexedDB, logger); + initPromise = store.connect(); + return initPromise; + } + initPromise = Promise.resolve(); + return initPromise; }, /** @@ -343,20 +352,66 @@ module.exports = { */ flush: function() { if (!store) { - return; + return Promise.resolve(); } return store.flush(); }, + /** + * Clean up old logs. + * @return Promise Resolves if cleaned logs. + */ + cleanup: function() { + if (!store) { + return Promise.resolve(); + } + return store.consume(false); + }, + /** * Send a bug report. * @param {string} userText Any additional user input. * @return {Promise} Resolved when the bug report is sent. */ sendBugReport: function(userText) { - return store.consume(false).then((logs) => { - // Send logs grouped by ID - console.log(logs); + if (!logger) { + return Promise.reject(new Error("No console logger, did you forget to call init()?")); + } + // If in incognito mode, store is null, but we still want bug report sending to work going off + // the in-memory console logs. + let promise = Promise.resolve([]); + if (store) { + promise = store.consume(false); // TODO Swap to true to remove all logs + } + return promise.then((logs) => { + // and add the most recent console logs which won't be in the store yet. + const consoleLogs = logger.flush(); // remove logs from console + const currentId = store ? store.id : "-"; + logs.unshift({ + lines: consoleLogs, + id: currentId, + }); + return new Promise((resolve, reject) => { + request({ + method: "POST", + url: "http://localhost:1337", + body: { + logs: logs, + text: userText || "User did not supply any additional text.", + }, + json: true, + }, (err, res) => { + if (err) { + reject(err); + return; + } + if (res.status < 200 || res.status >= 400) { + reject(new Error(`HTTP ${res.status}`)); + return; + } + resolve(); + }) + }); }); } }; From 789c7b60cc238d4b264d444e8e911a60d82c7b50 Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Thu, 19 Jan 2017 16:51:41 +0100 Subject: [PATCH 0094/1951] CSS for avatars that appear when users are typing --- .../structures/_RoomStatusBar.scss | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index 5daac88f..12cecffe 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -40,12 +40,12 @@ limitations under the License. opacity: 0.5; position: relative; top: -4px; -/* +/* animation-duration: 1s; animation-name: bounce; animation-direction: alternate; animation-iteration-count: infinite; -*/ +*/ } .mx_RoomStatusBar_placeholderIndicator span:nth-child(1) { @@ -70,6 +70,29 @@ limitations under the License. } } +.mx_RoomStatusBar_typingIndicatorAvatars { + width: 24px; + text-align: left; +} + +.mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar { + margin-right: -12px; +} + +.mx_RoomStatusBar_typingIndicatorRemaining { + display: inline-block; + color: #acacac; + background-color: #ddd; + border-radius: 40px; + width: 24px; + height: 24px; + line-height: 24px; + font-size: 0.8em; + vertical-align: top; + text-align: center; + position: absolute; +} + .mx_RoomStatusBar_scrollDownIndicator { cursor: pointer; } From 537194608e34676450fa916a98d8a099a89af161 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2017 16:01:37 +0000 Subject: [PATCH 0095/1951] Make riot desktop single instance So launching a new instance will focus the old one, meaning that if you have Riot minimised to the tray and launch it via the desktop shortcut / start menu, you don't end up with more & more copies of the app. This doesn't really prevent you from running multiple copies of the app to fake multi account support since they share a data directory anyway. --- electron/src/electron-main.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/electron/src/electron-main.js b/electron/src/electron-main.js index 929b7892..1a6ea692 100644 --- a/electron/src/electron-main.js +++ b/electron/src/electron-main.js @@ -159,6 +159,19 @@ electron.ipcMain.on('install_update', installUpdate); electron.app.commandLine.appendSwitch('--enable-usermedia-screen-capturing'); +const shouldQuit = electron.app.makeSingleInstance((commandLine, workingDirectory) => { + // Someone tried to run a second instance, we should focus our window. + if (mainWindow) { + if (!mainWindow.isVisible()) mainWindow.show(); + if (mainWindow.isMinimized()) mainWindow.restore(); + mainWindow.focus(); + } +}); + +if (shouldQuit) { + electron.app.quit() +} + electron.app.on('ready', () => { if (vectorConfig.update_base_url) { console.log("Starting auto update with base URL: " + vectorConfig.update_base_url); From f5d7f3ca9f77f491f93a5573e916463bfb2d5634 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 19 Jan 2017 16:21:43 +0000 Subject: [PATCH 0096/1951] Add StartupWMClass so GNOME doesn't get confused by the hidden windows --- package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 84e05478..b7b69d0c 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,10 @@ ], "linux": { "target": "deb", - "maintainer": "support@riot.im" + "maintainer": "support@riot.im", + "desktop": { + "StartupWMClass": "riot-web" + } }, "win": { "target": "squirrel" From bf887e82fec360a237bb1262ed4426311b5f7b18 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 16:40:54 +0000 Subject: [PATCH 0097/1951] Swap to async/await rather than promise chains Since we do in fact support coroutines! --- src/vector/rageshake.js | 161 ++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 96 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 1f3dbc7d..299a8497 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -166,7 +166,7 @@ class IndexedDBLogStore { * when the log file was created (the log ID). The objects have said log ID in an "id" field and "lines" which is a * big string with all the new-line delimited logs. */ - consume(clearAll) { + async consume(clearAll) { const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB const db = this.db; @@ -223,65 +223,36 @@ class IndexedDBLogStore { }); } - // Ideally we'd just use coroutines and a for loop but riot-web doesn't support async/await so instead - // recursively fetch logs up to the given threshold. We can't cheat and fetch all the logs - // from all time, but we may OOM if we do so. - // Returns: Promise : Each object having 'id' and 'lines'. Same ordering as logIds. - function fetchLogsToThreshold(logIds, threshold, logs) { - // Base case: check log size and return if bigger than threshold - let size = 0; - logs.forEach((l) => { - size += l.lines.length; + let allLogIds = await fetchLogIds(); + let removeLogIds = []; + let logs = []; + let size = 0; + for (let i = 0; i < allLogIds.length; i++) { + let lines = await fetchLogs(allLogIds[i]); + logs.push({ + lines: lines, + id: allLogIds[i], }); - if (size > threshold) { - return Promise.resolve(logs); + size += lines.length; + if (size > MAX_LOG_SIZE) { + // the remaining log IDs should be removed. If we go out of bounds this is just [] + removeLogIds = allLogIds.slice(i + 1); + break; } - - // fetch logs for the first element - let logId = logIds.shift(); - if (!logId) { - // no more entries - return Promise.resolve(logs); - } - return fetchLogs(logId).then((lines) => { - // add result to logs - logs.push({ - lines: lines, - id: logId, - }); - // recurse with the next log ID. TODO: Stack overflow risk? - return fetchLogsToThreshold(logIds, threshold, logs); - }) } - - let allLogIds = []; - return fetchLogIds().then((logIds) => { - allLogIds = logIds.map((id) => id); // deep copy array as we'll modify it when fetching logs - return fetchLogsToThreshold(logIds, MAX_LOG_SIZE, []); - }).then((logs) => { - // Remove all logs that are beyond the threshold (not in logs), or the entire logs if clearAll was set. - let removeLogIds = allLogIds; - if (!clearAll) { - removeLogIds = removeLogIds.filter((id) => { - for (let i = 0; i < logs.length; i++) { - if (logs[i].id === id) { - return false; // do not remove logs that we're about to return to the caller. - } - } - return true; - }); - } - if (removeLogIds.length > 0) { - console.log("Removing logs: ", removeLogIds); - // Don't promise chain this because it's non-fatal if we can't clean up logs. - Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => { - console.log(`Removed ${removeLogIds.length} old logs.`); - }, (err) => { - console.error(err); - }) - } - return logs; - }); + if (clearAll) { + removeLogIds = allLogIds; + } + if (removeLogIds.length > 0) { + console.log("Removing logs: ", removeLogIds); + // Don't await this because it's non-fatal if we can't clean up logs. + Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => { + console.log(`Removed ${removeLogIds.length} old logs.`); + }, (err) => { + console.error(err); + }) + }console.log("async consumeeeee"); + return logs; } _generateLogEntry(lines) { @@ -350,22 +321,22 @@ module.exports = { * Force-flush the logs to storage. * @return {Promise} Resolved when the logs have been flushed. */ - flush: function() { + flush: async function() { if (!store) { - return Promise.resolve(); + return; } - return store.flush(); + await store.flush(); }, /** * Clean up old logs. * @return Promise Resolves if cleaned logs. */ - cleanup: function() { + cleanup: async function() { if (!store) { - return Promise.resolve(); + return; } - return store.consume(false); + await store.consume(false); }, /** @@ -373,45 +344,43 @@ module.exports = { * @param {string} userText Any additional user input. * @return {Promise} Resolved when the bug report is sent. */ - sendBugReport: function(userText) { + sendBugReport: async function(userText) { if (!logger) { - return Promise.reject(new Error("No console logger, did you forget to call init()?")); + throw new Error("No console logger, did you forget to call init()?"); } // If in incognito mode, store is null, but we still want bug report sending to work going off // the in-memory console logs. - let promise = Promise.resolve([]); + let logs = []; if (store) { - promise = store.consume(false); // TODO Swap to true to remove all logs + logs = await store.consume(false); } - return promise.then((logs) => { - // and add the most recent console logs which won't be in the store yet. - const consoleLogs = logger.flush(); // remove logs from console - const currentId = store ? store.id : "-"; - logs.unshift({ - lines: consoleLogs, - id: currentId, - }); - return new Promise((resolve, reject) => { - request({ - method: "POST", - url: "http://localhost:1337", - body: { - logs: logs, - text: userText || "User did not supply any additional text.", - }, - json: true, - }, (err, res) => { - if (err) { - reject(err); - return; - } - if (res.status < 200 || res.status >= 400) { - reject(new Error(`HTTP ${res.status}`)); - return; - } - resolve(); - }) - }); + // and add the most recent console logs which won't be in the store yet. + const consoleLogs = logger.flush(); // remove logs from console + const currentId = store ? store.id : "-"; + logs.unshift({ + lines: consoleLogs, + id: currentId, + }); + await new Promise((resolve, reject) => { + request({ + method: "POST", + url: "http://localhost:1337", + body: { + logs: logs, + text: userText || "User did not supply any additional text.", + }, + json: true, + }, (err, res) => { + if (err) { + reject(err); + return; + } + if (res.status < 200 || res.status >= 400) { + reject(new Error(`HTTP ${res.status}`)); + return; + } + resolve(); + }) }); } }; From 3996d23b1996434723335ed0c3be423ba7298561 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 16:49:25 +0000 Subject: [PATCH 0098/1951] Inject bug report endpoint URL from config.json --- config.sample.json | 1 + src/vector/index.js | 1 + src/vector/rageshake.js | 12 ++++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/config.sample.json b/config.sample.json index e6384221..a65646ac 100644 --- a/config.sample.json +++ b/config.sample.json @@ -4,6 +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", "enableLabs": true, "roomDirectory": { "servers": [ diff --git a/src/vector/index.js b/src/vector/index.js index d1d457e9..d1d85e06 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -241,6 +241,7 @@ async function loadApp() { let configError; try { configJson = await getConfig(); + rageshake.setBugReportEndpoint(configJson.bug_report_endpoint_url); } catch (e) { configError = e; } diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 299a8497..3fb9efdc 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -251,7 +251,7 @@ class IndexedDBLogStore { }, (err) => { console.error(err); }) - }console.log("async consumeeeee"); + } return logs; } @@ -296,6 +296,7 @@ function selectQuery(store, keyRange, resultMapper) { let store = null; let logger = null; let initPromise = null; +let bugReportEndpoint = null; module.exports = { /** @@ -339,6 +340,10 @@ module.exports = { await store.consume(false); }, + setBugReportEndpoint: function(url) { + bugReportEndpoint = url; + }, + /** * Send a bug report. * @param {string} userText Any additional user input. @@ -348,6 +353,9 @@ module.exports = { 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."); + } // If in incognito mode, store is null, but we still want bug report sending to work going off // the in-memory console logs. let logs = []; @@ -364,7 +372,7 @@ module.exports = { await new Promise((resolve, reject) => { request({ method: "POST", - url: "http://localhost:1337", + url: bugReportEndpoint, body: { logs: logs, text: userText || "User did not supply any additional text.", From 36450764f3a341e3b07843af6e6975d494a9a374 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 19 Jan 2017 16:58:55 +0000 Subject: [PATCH 0099/1951] rethemendex fixes - set cwd before doing cwd-specific operations - don't include 'themes' directory, otherwise it's a bit circular. --- src/skins/vector/css/_components.scss | 1 - src/skins/vector/css/rethemendex.sh | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 88435236..880c1054 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -49,7 +49,6 @@ @import "./matrix-react-sdk/views/voip/_CallView.scss"; @import "./matrix-react-sdk/views/voip/_IncomingCallbox.scss"; @import "./matrix-react-sdk/views/voip/_VideoView.scss"; -@import "./themes/_base.scss"; @import "./vector-web/_fonts.scss"; @import "./vector-web/structures/_CompatibilityPage.scss"; @import "./vector-web/structures/_LeftPanel.scss"; diff --git a/src/skins/vector/css/rethemendex.sh b/src/skins/vector/css/rethemendex.sh index 9381c5cd..a7d9a657 100755 --- a/src/skins/vector/css/rethemendex.sh +++ b/src/skins/vector/css/rethemendex.sh @@ -1,8 +1,13 @@ #!/bin/bash -echo "// autogenerated by rethemendex.sh" > _components.scss +cd `dirname $0` -for i in `find . -iname _\*.scss | fgrep -v _components.scss | sort`; -do - echo "@import \"$i\";" >> _components.scss -done +{ + echo "// autogenerated by rethemendex.sh" + + find . \! \( -path ./themes -prune \) -iname _\*.scss | + fgrep -v _components.scss | LC_ALL=C sort | + while read i; do + echo "@import \"$i\";" + done +} > _components.scss From beba4d2ae36c4499e99a27258fb58b9a8e6e12b7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 17:40:21 +0000 Subject: [PATCH 0100/1951] Add version and user agent to bug report --- src/vector/rageshake.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 3fb9efdc..e3cc6661 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -14,6 +14,7 @@ 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"; // This module contains all the code needed to log the console, persist it to disk and submit bug reports. Rationale is as follows: @@ -356,6 +357,18 @@ module.exports = { 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. let logs = []; @@ -376,6 +389,8 @@ module.exports = { body: { logs: logs, text: userText || "User did not supply any additional text.", + version: version, + user_agent: userAgent, }, json: true, }, (err, res) => { From 53a16158c75ff0b938b2383ad91829c9d23c891a Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Thu, 19 Jan 2017 17:41:08 +0000 Subject: [PATCH 0101/1951] Remove logs when submitting bug reports --- src/vector/rageshake.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index e3cc6661..2b4d0b7d 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -373,7 +373,7 @@ module.exports = { // the in-memory console logs. let logs = []; if (store) { - logs = await store.consume(false); + logs = await store.consume(true); } // and add the most recent console logs which won't be in the store yet. const consoleLogs = logger.flush(); // remove logs from console From 8ad0ff24f8f5422a6c15f7744227a241d789aee8 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 19 Jan 2017 18:35:43 +0000 Subject: [PATCH 0102/1951] Fix link to image for event options menu This has to be relative, because we don't know if riot is going to be mounted at the top-level of the domain or not (it's not, on riot.im). Links are relative to the final location of the CSS, which is under bundles/, so need ../.. --- src/skins/vector/css/themes/_base.scss | 2 +- src/skins/vector/css/themes/_dark.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index ad18eb49..476cf7e0 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -88,7 +88,7 @@ $event-notsent-color: #f44; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "/img/icon_context_message.svg"; +$edit-button-url: "../../img/icon_context_message.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index e3e32e84..0f3b77f5 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -88,7 +88,7 @@ $event-notsent-color: #f44; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "/img/icon_context_message_dark.svg"; +$edit-button-url: "../../img/icon_context_message_dark.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color From fa535996ae6c979522252f2540c656f09576ebc9 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 02:22:11 +0000 Subject: [PATCH 0103/1951] fix textarea bg --- src/skins/vector/css/_common.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skins/vector/css/_common.scss b/src/skins/vector/css/_common.scss index 94362a2f..bf31bea6 100644 --- a/src/skins/vector/css/_common.scss +++ b/src/skins/vector/css/_common.scss @@ -55,7 +55,7 @@ a:visited { color: $accent-color; } -input[type=text], input[type=password] { +input[type=text], input[type=password], textarea { background-color: transparent; color: $primary-fg-color; } From 4d8f5d4df1c3bb0ef27b53842f3ed1755e9039d8 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 02:29:56 +0000 Subject: [PATCH 0104/1951] change selection color & fix MD bg --- .../vector/css/matrix-react-sdk/views/rooms/_EventTile.scss | 4 ++++ src/skins/vector/css/themes/_dark.scss | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss index b79db919..29170a83 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_EventTile.scss @@ -294,6 +294,10 @@ limitations under the License. overflow-y: visible; } +.mx_EventTile_content .markdown-body code { + background-color: #f8f8f8; +} + .mx_EventTile_content .markdown-body h1, .mx_EventTile_content .markdown-body h2, .mx_EventTile_content .markdown-body h3, diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 0f3b77f5..03913ca2 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -73,7 +73,7 @@ $voip-accept-color: #80f480; // ******************** $roomtile-name-color: rgba(186, 186, 186, 0.8); -$roomtile-selected-bg-color: rgba(0, 0, 0, 0.8); +$roomtile-selected-bg-color: rgba(255, 255, 255, 0.05); $roomsublist-label-fg-color: $h3-color; $roomsublist-label-bg-color: #454545; From 9b70e2e25f3442374a1fbaeaedf25b29b2ce0539 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 02:34:45 +0000 Subject: [PATCH 0105/1951] fix RTE bg color --- .../vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss index 9265d4dc..062dd0ba 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_Autocomplete.scss @@ -4,7 +4,7 @@ z-index: 1000; width: 100%; border: 1px solid $primary-hairline-color; - background: white; + background: $primary-bg-color; border-bottom: none; border-radius: 4px 4px 0 0; max-height: 50vh; From ea860807c4fef075767e06aa19c33fd97f702846 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 20 Jan 2017 11:56:11 +0000 Subject: [PATCH 0106/1951] Add a monkeyPatch function rather than monkey-patching in the constructor --- src/vector/rageshake.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 2b4d0b7d..e12ee329 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -36,7 +36,9 @@ const FLUSH_RATE_MS = 30 * 1000; class ConsoleLogger { constructor() { this.logs = ""; + } + monkeyPatch(consoleObj) { // Monkey-patch console logging const consoleFunctionsToLevels = { log: "I", @@ -46,8 +48,8 @@ class ConsoleLogger { }; Object.keys(consoleFunctionsToLevels).forEach((fnName) => { const level = consoleFunctionsToLevels[fnName]; - let originalFn = window.console[fnName].bind(window.console); - window.console[fnName] = (...args) => { + let originalFn = consoleObj[fnName].bind(consoleObj); + consoleObj[fnName] = (...args) => { this.log(level, ...args); originalFn(...args); } @@ -310,6 +312,7 @@ module.exports = { return initPromise; } logger = new ConsoleLogger(); + logger.monkeyPatch(window.console); if (window.indexedDB) { store = new IndexedDBLogStore(window.indexedDB, logger); initPromise = store.connect(); From ba1e166ac83ae0f6477d617705a3f333f8d4f74e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 20 Jan 2017 12:02:48 +0000 Subject: [PATCH 0107/1951] Line length 80 like we're still in the 80s --- src/vector/rageshake.js | 133 ++++++++++++++++++++++++++-------------- 1 file changed, 87 insertions(+), 46 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index e12ee329..a3d39635 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -17,18 +17,26 @@ limitations under the License. import PlatformPeg from 'matrix-react-sdk/lib/PlatformPeg'; import request from "browser-request"; -// This module contains all the code needed to log the console, persist it to disk and submit bug reports. Rationale is as follows: -// - Monkey-patching the console is preferable to having a log library because we can catch logs by other libraries more easily, -// without having to all depend on the same log framework / pass the logger around. -// - We use IndexedDB to persists logs because it has generous disk space limits compared to local storage. IndexedDB does not work -// in incognito mode, in which case this module will not be able to write logs to disk. However, the logs will still be stored -// in-memory, so can still be submitted in a bug report should the user wish to: we can also store more logs in-memory than in -// local storage, which does work in incognito mode. We also need to handle the case where there are 2+ tabs. Each JS runtime -// generates a random string which serves as the "ID" for that tab/session. These IDs are stored along with the log lines. -// - Bug reports are sent as a POST over HTTPS: it purposefully does not use Matrix as bug reports may be made when Matrix is -// not responsive (which may be the cause of the bug). We send the most recent N MB of UTF-8 log data, starting with the most -// recent, which we know because the "ID"s are actually timestamps. We then purge the remaining logs. We also do this purge -// on startup to prevent logs from accumulating. +// This module contains all the code needed to log the console, persist it to +// disk and submit bug reports. Rationale is as follows: +// - Monkey-patching the console is preferable to having a log library because +// we can catch logs by other libraries more easily, without having to all +// depend on the same log framework / pass the logger around. +// - We use IndexedDB to persists logs because it has generous disk space +// limits compared to local storage. IndexedDB does not work in incognito +// mode, in which case this module will not be able to write logs to disk. +// However, the logs will still be stored in-memory, so can still be +// submitted in a bug report should the user wish to: we can also store more +// logs in-memory than in local storage, which does work in incognito mode. +// We also need to handle the case where there are 2+ tabs. Each JS runtime +// generates a random string which serves as the "ID" for that tab/session. +// These IDs are stored along with the log lines. +// - Bug reports are sent as a POST over HTTPS: it purposefully does not use +// Matrix as bug reports may be made when Matrix is not responsive (which may +// be the cause of the bug). We send the most recent N MB of UTF-8 log data, +// starting with the most recent, which we know because the "ID"s are +// actually timestamps. We then purge the remaining logs. We also do this +// purge on startup to prevent logs from accumulating. const FLUSH_RATE_MS = 30 * 1000; @@ -60,9 +68,10 @@ class ConsoleLogger { // We don't know what locale the user may be running so use ISO strings const ts = new Date().toISOString(); // Some browsers support string formatting which we're not doing here - // so the lines are a little more ugly but easy to implement / quick to run. + // so the lines are a little more ugly but easy to implement / quick to + // run. // Example line: - // 2017-01-18T11:23:53.214Z W Failed to set badge count: Error setting badge. Message: Too many badges requests in queue. + // 2017-01-18T11:23:53.214Z W Failed to set badge count const line = `${ts} ${level} ${args.join(' ')}\n`; // Using + really is the quickest way in JS // http://jsperf.com/concat-vs-plus-vs-join @@ -74,7 +83,8 @@ class ConsoleLogger { * @return {string} \n delimited log lines to flush. */ flush() { - // The ConsoleLogger doesn't care how these end up on disk, it just flushes them to the caller. + // The ConsoleLogger doesn't care how these end up on disk, it just + // flushes them to the caller. const logsToFlush = this.logs; this.logs = ""; return logsToFlush; @@ -105,7 +115,9 @@ class IndexedDBLogStore { }; req.onerror = (event) => { - const err = "Failed to open log database: " + event.target.errorCode; + const err = ( + "Failed to open log database: " + event.target.errorCode + ); console.error(err); reject(new Error(err)); }; @@ -117,8 +129,10 @@ class IndexedDBLogStore { keyPath: ["id", "index"] }); // Keys in the database look like: [ "instance-148938490", 0 ] - // Later on we need to query for everything with index=0, and query everything for an instance id. - // In order to do this, we need to set up indexes on both "id" and "index". + // Later on we need to query for everything with index=0, and + // query everything for an instance id. + // In order to do this, we need to set up indexes on both "id" + // and "index". objectStore.createIndex("index", "index", { unique: false }); objectStore.createIndex("id", "id", { unique: false }); @@ -151,23 +165,30 @@ class IndexedDBLogStore { resolve(); }; txn.onerror = (event) => { - console.error("Failed to flush logs : " + event.target.errorCode); - reject(new Error("Failed to write logs: " + event.target.errorCode)); + console.error( + "Failed to flush logs : " + event.target.errorCode + ); + reject( + new Error("Failed to write logs: " + event.target.errorCode) + ); } }); } /** - * Consume the most recent logs and return them. Older logs which are not returned are deleted at the same time, - * so this can be called at startup to do house-keeping to keep the logs from growing too large. + * Consume the most recent logs and return them. Older logs which are not + * returned are deleted at the same time, so this can be called at startup + * to do house-keeping to keep the logs from growing too large. * - * @param {boolean} clearAll True to clear the most recent logs returned in addition to the - * older logs. This is desirable when sending bug reports as we do not want to submit the - * same logs twice. This is not desirable when doing house-keeping at startup in case they - * want to submit a bug report later. - * @return {Promise} Resolves to an array of objects. The array is sorted in time (oldest first) based on - * when the log file was created (the log ID). The objects have said log ID in an "id" field and "lines" which is a - * big string with all the new-line delimited logs. + * @param {boolean} clearAll True to clear the most recent logs returned in + * addition to the older logs. This is desirable when sending bug reports as + * we do not want to submit the same logs twice. This is not desirable when + * doing house-keeping at startup in case they want to submit a bug report + * later. + * @return {Promise} Resolves to an array of objects. The array is + * sorted in time (oldest first) based on when the log file was created (the + * log ID). The objects have said log ID in an "id" field and "lines" which + * is a big string with all the new-line delimited logs. */ async consume(clearAll) { const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB @@ -176,13 +197,15 @@ class IndexedDBLogStore { // Returns: a string representing the concatenated logs for this ID. function fetchLogs(id) { const o = db.transaction("logs", "readonly").objectStore("logs"); - return selectQuery(o.index("id"), IDBKeyRange.only(id), (cursor) => { + return selectQuery(o.index("id"), IDBKeyRange.only(id), + (cursor) => { return { lines: cursor.value.lines, index: cursor.value.index, } }).then((linesArray) => { - // We have been storing logs periodically, so string them all together *in order of index* now + // We have been storing logs periodically, so string them all + // together *in order of index* now linesArray.sort((a, b) => { return a.index - b.index; }) @@ -192,13 +215,18 @@ class IndexedDBLogStore { // Returns: A sorted array of log IDs. (newest first) function fetchLogIds() { - // To gather all the log IDs, query for every log entry with index "0", this will let us - // know all the IDs from different tabs/sessions. + // To gather all the log IDs, query for every log entry with index + // "0", this will let us know all the IDs from different + // tabs/sessions. const o = db.transaction("logs", "readonly").objectStore("logs"); - return selectQuery(o.index("index"), IDBKeyRange.only(0), (cursor) => cursor.value.id).then((res) => { - // we know each entry has a unique ID, and we know IDs are timestamps, so accumulate all the IDs, - // ignoring the logs for now, and sort them to work out the correct log ID ordering, newest first. - // E.g. [ "instance-1484827160051", "instance-1374827160051", "instance-1000007160051"] + return selectQuery(o.index("index"), IDBKeyRange.only(0), + (cursor) => cursor.value.id).then((res) => { + // we know each entry has a unique ID, and we know IDs are + // timestamps, so accumulate all the IDs, ignoring the logs for + // now, and sort them to work out the correct log ID ordering, + // newest first. + // E.g. [ "instance-1484827160051", "instance-1374827160051", + // "instance-1000007160051"] return res.sort().reverse(); }); } @@ -221,7 +249,12 @@ class IndexedDBLogStore { resolve(); }; txn.onerror = (event) => { - reject(new Error(`Failed to delete logs for '${id}' : ${event.target.errorCode}`)); + reject( + new Error( + "Failed to delete logs for " + + `'${id}' : ${event.target.errorCode}` + ) + ); } }); } @@ -238,7 +271,8 @@ class IndexedDBLogStore { }); size += lines.length; if (size > MAX_LOG_SIZE) { - // the remaining log IDs should be removed. If we go out of bounds this is just [] + // the remaining log IDs should be removed. If we go out of + // bounds this is just [] removeLogIds = allLogIds.slice(i + 1); break; } @@ -248,7 +282,8 @@ class IndexedDBLogStore { } if (removeLogIds.length > 0) { console.log("Removing logs: ", removeLogIds); - // Don't await this because it's non-fatal if we can't clean up logs. + // Don't await this because it's non-fatal if we can't clean up + // logs. Promise.all(removeLogIds.map((id) => deleteLogs(id))).then(() => { console.log(`Removed ${removeLogIds.length} old logs.`); }, (err) => { @@ -271,9 +306,11 @@ class IndexedDBLogStore { * Helper method to collect results from a Cursor and promiseify it. * @param {ObjectStore|Index} store The store to perform openCursor on. * @param {IDBKeyRange=} keyRange Optional key range to apply on the cursor. - * @param {Function} resultMapper A function which is repeatedly called with a Cursor. + * @param {Function} resultMapper A function which is repeatedly called with a + * Cursor. * Return the data you want to keep. - * @return {Promise} Resolves to an array of whatever you returned from resultMapper. + * @return {Promise} Resolves to an array of whatever you returned from + * resultMapper. */ function selectQuery(store, keyRange, resultMapper) { const query = store.openCursor(keyRange); @@ -355,7 +392,9 @@ module.exports = { */ sendBugReport: async function(userText) { if (!logger) { - throw new Error("No console logger, did you forget to call init()?"); + throw new Error( + "No console logger, did you forget to call init()?" + ); } if (!bugReportEndpoint) { throw new Error("No bug report endpoint has been set."); @@ -372,8 +411,8 @@ module.exports = { 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. + // If in incognito mode, store is null, but we still want bug report + // sending to work going off the in-memory console logs. let logs = []; if (store) { logs = await store.consume(true); @@ -391,7 +430,9 @@ module.exports = { url: bugReportEndpoint, body: { logs: logs, - text: userText || "User did not supply any additional text.", + text: ( + userText || "User did not supply any additional text." + ), version: version, user_agent: userAgent, }, From 41c6294be2e3a6b23fad65771173819c74e35f6e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 20 Jan 2017 13:02:57 +0000 Subject: [PATCH 0108/1951] Remove clearAll from consume(): we want duplicate logs on multiple reports --- src/vector/rageshake.js | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index a3d39635..a96b1f28 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -180,17 +180,12 @@ class IndexedDBLogStore { * returned are deleted at the same time, so this can be called at startup * to do house-keeping to keep the logs from growing too large. * - * @param {boolean} clearAll True to clear the most recent logs returned in - * addition to the older logs. This is desirable when sending bug reports as - * we do not want to submit the same logs twice. This is not desirable when - * doing house-keeping at startup in case they want to submit a bug report - * later. * @return {Promise} Resolves to an array of objects. The array is * sorted in time (oldest first) based on when the log file was created (the * log ID). The objects have said log ID in an "id" field and "lines" which * is a big string with all the new-line delimited logs. */ - async consume(clearAll) { + async consume() { const MAX_LOG_SIZE = 1024 * 1024 * 50; // 50 MB const db = this.db; @@ -277,9 +272,6 @@ class IndexedDBLogStore { break; } } - if (clearAll) { - removeLogIds = allLogIds; - } if (removeLogIds.length > 0) { console.log("Removing logs: ", removeLogIds); // Don't await this because it's non-fatal if we can't clean up @@ -378,7 +370,7 @@ module.exports = { if (!store) { return; } - await store.consume(false); + await store.consume(); }, setBugReportEndpoint: function(url) { @@ -415,7 +407,7 @@ module.exports = { // sending to work going off the in-memory console logs. let logs = []; if (store) { - logs = await store.consume(true); + logs = await store.consume(); } // and add the most recent console logs which won't be in the store yet. const consoleLogs = logger.flush(); // remove logs from console From 378126e746bd78088996a82024ea5c4204955b7c Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 20 Jan 2017 14:00:30 +0000 Subject: [PATCH 0109/1951] Add another store to keep last modified times This makes it easier to get a list of all the log IDs. It also makes it possible to order the logs by the *LAST* log line and not the first as was the case previously, which is important in the case of long-running tabs. --- src/vector/rageshake.js | 69 +++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index a96b1f28..f4fe4396 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -96,7 +96,7 @@ class IndexedDBLogStore { constructor(indexedDB, logger) { this.indexedDB = indexedDB; this.logger = logger; - this.id = "instance-" + Date.now(); + this.id = "instance-" + Math.random() + Date.now(); this.index = 0; this.db = null; } @@ -125,22 +125,24 @@ class IndexedDBLogStore { // First time: Setup the object store req.onupgradeneeded = (event) => { const db = event.target.result; - const objectStore = db.createObjectStore("logs", { + const logObjStore = db.createObjectStore("logs", { keyPath: ["id", "index"] }); // Keys in the database look like: [ "instance-148938490", 0 ] - // Later on we need to query for everything with index=0, and - // query everything for an instance id. - // In order to do this, we need to set up indexes on both "id" - // and "index". - objectStore.createIndex("index", "index", { unique: false }); - objectStore.createIndex("id", "id", { unique: false }); + // Later on we need to query everything based on an instance id. + // In order to do this, we need to set up indexes "id". + logObjStore.createIndex("id", "id", { unique: false }); - objectStore.add( + logObjStore.add( this._generateLogEntry( new Date() + " ::: Log database was created." ) ); + + const lastModifiedStore = db.createObjectStore("logslastmod", { + keyPath: "id", + }); + lastModifiedStore.add(this._generateLastModifiedTime()); } }); } @@ -158,15 +160,17 @@ class IndexedDBLogStore { return Promise.resolve(); } return new Promise((resolve, reject) => { - let txn = this.db.transaction("logs", "readwrite"); + 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(); }; txn.onerror = (event) => { console.error( - "Failed to flush logs : " + event.target.errorCode + "Failed to flush logs : ", event ); reject( new Error("Failed to write logs: " + event.target.errorCode) @@ -210,25 +214,28 @@ class IndexedDBLogStore { // Returns: A sorted array of log IDs. (newest first) function fetchLogIds() { - // To gather all the log IDs, query for every log entry with index - // "0", this will let us know all the IDs from different - // tabs/sessions. - const o = db.transaction("logs", "readonly").objectStore("logs"); - return selectQuery(o.index("index"), IDBKeyRange.only(0), - (cursor) => cursor.value.id).then((res) => { - // we know each entry has a unique ID, and we know IDs are - // timestamps, so accumulate all the IDs, ignoring the logs for - // now, and sort them to work out the correct log ID ordering, - // newest first. - // E.g. [ "instance-1484827160051", "instance-1374827160051", - // "instance-1000007160051"] - return res.sort().reverse(); + // To gather all the log IDs, query for all records in logslastmod. + const o = db.transaction("logslastmod", "readonly").objectStore( + "logslastmod" + ); + return selectQuery(o, undefined, (cursor) => { + return { + id: cursor.value.id, + ts: cursor.value.ts, + }; + }).then((res) => { + // Sort IDs by timestamp (newest first) + return res.sort((a, b) => { + return b.ts - a.ts; + }).map((a) => a.id); }); } function deleteLogs(id) { return new Promise((resolve, reject) => { - const txn = db.transaction("logs", "readwrite"); + const txn = db.transaction( + ["logs", "logslastmod"], "readwrite" + ); const o = txn.objectStore("logs"); // only load the key path, not the data which may be huge const query = o.index("id").openKeyCursor(IDBKeyRange.only(id)); @@ -250,7 +257,10 @@ class IndexedDBLogStore { `'${id}' : ${event.target.errorCode}` ) ); - } + }; + // delete last modified entries + const lastModStore = txn.objectStore("logslastmod"); + lastModStore.delete(id); }); } @@ -292,6 +302,13 @@ class IndexedDBLogStore { index: this.index++ }; } + + _generateLastModifiedTime() { + return { + id: this.id, + ts: Date.now(), + }; + } } /** From ea063ab8b0b2ce6b2f1a5d36132757e0cb52f8ff Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 20 Jan 2017 14:46:19 +0000 Subject: [PATCH 0110/1951] Address race conditions when flushing logs --- src/vector/rageshake.js | 94 +++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 26 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index f4fe4396..7d1a93ef 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -99,6 +99,11 @@ class IndexedDBLogStore { this.id = "instance-" + Math.random() + Date.now(); this.index = 0; this.db = null; + // Promise is not null when a flush is IN PROGRESS + this.flushPromise = null; + // Promise is not null when flush() is called when one is already in + // progress. + this.flushAgainPromise = null; } /** @@ -148,35 +153,80 @@ class IndexedDBLogStore { } /** + * Flush logs to disk. + * + * There are guards to protect against race conditions in order to ensure + * that all previous flushes have completed before the most recent flush. + * Consider without guards: + * - A calls flush() periodically. + * - B calls flush() and wants to send logs immediately afterwards. + * - If B doesn't wait for A's flush to complete, B will be missing the + * contents of A's flush. + * To protect against this, we set 'flushPromise' when a flush is ongoing. + * Subsequent calls to flush() during this period return a new promise + * 'flushAgainPromise' which is chained off the current 'flushPromise'. + * Subsequent calls to flush() when the first flush hasn't completed will + * return the same 'flushAgainPromise' as we can guarantee that we WILL + * do a brand new flush at some point in the future. Once the first flush + * has completed, 'flushAgainPromise' becomes 'flushPromise' and can be + * chained again. + * + * This guarantees that we will always eventually do a flush when flush() is + * called. + * * @return {Promise} Resolved when the logs have been flushed. */ flush() { - if (!this.db) { - // not connected yet or user rejected access for us to r/w to the db - return Promise.reject(new Error("No connected database")); + if (this.flushPromise) { // a flush is ongoing + if (this.flushAgainPromise) { // a flush is queued up, return that. + return this.flushAgainPromise; + } + // queue up a new flush + this.flushAgainPromise = this.flushPromise.then(() => { + // the current flush has completed, so shuffle the promises + // around: + // flushAgainPromise => flushPromise and null flushAgainPromise. + // flushPromise has already nulled itself. + this.flushAgainPromise = null; + return this.flush(); + }); + return this.flushAgainPromise; } - const lines = this.logger.flush(); - if (lines.length === 0) { - return Promise.resolve(); - } - return new Promise((resolve, reject) => { + + this.flushPromise = new Promise((resolve, reject) => { + if (!this.db) { + // not connected yet or user rejected access for us to r/w to + // the db. + this.flushPromise = null; + reject(new Error("No connected database")); + return; + } + const lines = this.logger.flush(); + if (lines.length === 0) { + this.flushPromise = null; + resolve(); + return; + } 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) => { + this.flushPromise = null; resolve(); }; txn.onerror = (event) => { console.error( "Failed to flush logs : ", event ); + this.flushPromise = null; reject( new Error("Failed to write logs: " + event.target.errorCode) ); } }); + return this.flushPromise; } /** @@ -368,17 +418,6 @@ module.exports = { return initPromise; }, - /** - * Force-flush the logs to storage. - * @return {Promise} Resolved when the logs have been flushed. - */ - flush: async function() { - if (!store) { - return; - } - await store.flush(); - }, - /** * Clean up old logs. * @return Promise Resolves if cleaned logs. @@ -422,17 +461,20 @@ module.exports = { // 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 (store) { + // flush most recent logs + await store.flush(); logs = await store.consume(); } - // and add the most recent console logs which won't be in the store yet. - const consoleLogs = logger.flush(); // remove logs from console - const currentId = store ? store.id : "-"; - logs.unshift({ - lines: consoleLogs, - id: currentId, - }); + else { + logs.push({ + lines: logger.flush(), + id: "-", + }); + } + await new Promise((resolve, reject) => { request({ method: "POST", From 956deca844505c75f03a1406a5ffd4532804cbde Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Fri, 20 Jan 2017 16:59:41 +0100 Subject: [PATCH 0111/1951] Add bg-color border to typing avatars --- .../css/matrix-react-sdk/structures/_RoomStatusBar.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index 12cecffe..b7bdeaa0 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -71,18 +71,20 @@ limitations under the License. } .mx_RoomStatusBar_typingIndicatorAvatars { - width: 24px; + width: 52px; text-align: left; } .mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar { margin-right: -12px; + border:1px solid $primary-bg-color; } .mx_RoomStatusBar_typingIndicatorRemaining { display: inline-block; color: #acacac; background-color: #ddd; + border:1px solid $primary-bg-color; border-radius: 40px; width: 24px; height: 24px; From 9ffedf5e7d8b0962c1b39038ecc8e59b5fc1459c Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Fri, 20 Jan 2017 17:00:59 +0100 Subject: [PATCH 0112/1951] space after colons --- .../css/matrix-react-sdk/structures/_RoomStatusBar.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index b7bdeaa0..44e9d983 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -77,14 +77,14 @@ limitations under the License. .mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar { margin-right: -12px; - border:1px solid $primary-bg-color; + border: 1px solid $primary-bg-color; } .mx_RoomStatusBar_typingIndicatorRemaining { display: inline-block; color: #acacac; background-color: #ddd; - border:1px solid $primary-bg-color; + border: 1px solid $primary-bg-color; border-radius: 40px; width: 24px; height: 24px; From afcf9de8d47f463b83e9b40dcfb37c6f5de47215 Mon Sep 17 00:00:00 2001 From: lukebarnard Date: Fri, 20 Jan 2017 17:35:01 +0100 Subject: [PATCH 0113/1951] Handle avatars with initials --- .../css/matrix-react-sdk/structures/_RoomStatusBar.scss | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index 44e9d983..d3f1439d 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -75,11 +75,16 @@ limitations under the License. text-align: left; } -.mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar { +.mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar_image { margin-right: -12px; border: 1px solid $primary-bg-color; } +.mx_RoomStatusBar_typingIndicatorAvatars .mx_BaseAvatar_initial { + padding-left: 1px; + padding-top: 1px; +} + .mx_RoomStatusBar_typingIndicatorRemaining { display: inline-block; color: #acacac; From a9c575b4d095872687969707878f215e23a7a261 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 21:00:33 +0000 Subject: [PATCH 0114/1951] fix a bunch of dark-theme buttons --- src/skins/vector/css/themes/_dark.scss | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 03913ca2..776da282 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -101,4 +101,15 @@ $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; // unused? -$progressbar-color: #000; \ No newline at end of file +$progressbar-color: #000; + +// Nasty hacks to apply a filter to arbitrary monochrome artwork to make it +// better match the theme. Typically applied to dark grey 'off' buttons or +// light grey 'on' buttons. +.mx_filterFlipColor { + filter: invert(); +} + +.gm-scrollbar .thumb { + filter: invert(); +} \ No newline at end of file From 5b1fea46cb6a0c9ec45e01a41c7222069a390507 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 21:09:49 +0000 Subject: [PATCH 0115/1951] fix badge color --- .../vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss index 286a709d..84f8ddde 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss @@ -171,7 +171,7 @@ limitations under the License. .mx_RoomTile.mx_RoomTile_noBadges .mx_RoomTile_badge.mx_RoomTile_badgeButton, .mx_RoomTile.mx_RoomTile_notificationStateMenu.mx_RoomTile_noBadges .mx_RoomTile_badge { - background-color: rgb(214, 214, 214); + background-color: $preview-widget-bar-color; } .mx_RoomTile_unreadNotify .mx_RoomTile_badge { From 83272f5cc58fa4a3efc5a8423642d1f4cb6886a1 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 21:11:28 +0000 Subject: [PATCH 0116/1951] fix badge color again --- .../vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss | 2 +- src/skins/vector/css/themes/_base.scss | 2 ++ src/skins/vector/css/themes/_dark.scss | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss index 84f8ddde..5ca4ac17 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/rooms/_RoomTile.scss @@ -171,7 +171,7 @@ limitations under the License. .mx_RoomTile.mx_RoomTile_noBadges .mx_RoomTile_badge.mx_RoomTile_badgeButton, .mx_RoomTile.mx_RoomTile_notificationStateMenu.mx_RoomTile_noBadges .mx_RoomTile_badge { - background-color: $preview-widget-bar-color; + background-color: $neutral-badge-color; } .mx_RoomTile_unreadNotify .mx_RoomTile_badge { diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss index 476cf7e0..db921034 100644 --- a/src/skins/vector/css/themes/_base.scss +++ b/src/skins/vector/css/themes/_base.scss @@ -59,6 +59,8 @@ $lightbox-background-bg-color: #000; $greyed-fg-color: #888; +$neutral-badge-color: #dbdbdb; + $preview-widget-bar-color: #ddd; $preview-widget-fg-color: $greyed-fg-color; diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 776da282..4902724f 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -59,6 +59,8 @@ $lightbox-background-bg-color: #000; $greyed-fg-color: #888; +$neutral-badge-color: #888; + $preview-widget-bar-color: $menu-bg-color; $preview-widget-fg-color: $greyed-fg-color; From ef519231d63f1aa765e0f1cd9ea78bb4591253b0 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Fri, 20 Jan 2017 21:16:58 +0000 Subject: [PATCH 0117/1951] fix mute button colors --- .../views/context_menus/NotificationStateContextMenu.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/context_menus/NotificationStateContextMenu.js b/src/components/views/context_menus/NotificationStateContextMenu.js index 243275db..d4b40d17 100644 --- a/src/components/views/context_menus/NotificationStateContextMenu.js +++ b/src/components/views/context_menus/NotificationStateContextMenu.js @@ -120,22 +120,22 @@ module.exports = React.createClass({
    - + All messages (loud)
    - + All messages
    - + Mentions only
    - + Mute
    From d907421ad46b64144a8690996ad51ffc716c4e8a Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 22 Jan 2017 01:25:32 +0100 Subject: [PATCH 0118/1951] crappy CSS for UnknownDeviceDialog --- src/skins/vector/css/_components.scss | 1 + .../views/dialogs/_UnknownDeviceDialog.scss | 46 +++++++++++++++++++ src/skins/vector/css/themes/_dark.scss | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 src/skins/vector/css/matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.scss diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 880c1054..51dd0ada 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -15,6 +15,7 @@ @import "./matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss"; @import "./matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss"; @import "./matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss"; +@import "./matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.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"; diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.scss b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.scss new file mode 100644 index 00000000..3a9b64d1 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_UnknownDeviceDialog.scss @@ -0,0 +1,46 @@ +/* +Copyright 2016 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. +*/ + +.mx_UnknownDeviceDialog .mx_MemberDeviceInfo { + float: right; + clear: both; + padding: 0px; + padding-top: 8px; +} + +.mx_UnknownDeviceDialog .mx_MemberDeviceInfo_textButton { + border: 0px; + height: 24px; + border-radius: 40px; + border: solid 1px $accent-color; + font-weight: 600; + font-size: 13px; + font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; + margin-left: 0px; + margin-right: 8px; + padding-left: 0.5em; + padding-right: 0.5em; + width: 70px; + outline: none; + cursor: pointer; + color: $accent-color; + background-color: $primary-bg-color; +} + +.mx_UnknownDeviceDialog .mx_UnknownDeviceDialog_deviceList li { + height: 40px; + border-bottom: 1px solid $primary-hairline-color; +} \ No newline at end of file diff --git a/src/skins/vector/css/themes/_dark.scss b/src/skins/vector/css/themes/_dark.scss index 4902724f..51930433 100644 --- a/src/skins/vector/css/themes/_dark.scss +++ b/src/skins/vector/css/themes/_dark.scss @@ -114,4 +114,4 @@ $progressbar-color: #000; .gm-scrollbar .thumb { filter: invert(); -} \ No newline at end of file +} From 2ecf65f05702075175b211161f05b5e2c57d9182 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 23 Jan 2017 09:28:48 +0000 Subject: [PATCH 0119/1951] Keep the logs if no store exists --- src/vector/rageshake.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 7d1a93ef..3cc15886 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -80,11 +80,15 @@ class ConsoleLogger { /** * Retrieve log lines to flush to disk. + * @param {boolean} keepLogs True to not delete logs after flushing. * @return {string} \n delimited log lines to flush. */ - flush() { + flush(keepLogs) { // The ConsoleLogger doesn't care how these end up on disk, it just // flushes them to the caller. + if (keepLogs) { + return this.logs; + } const logsToFlush = this.logs; this.logs = ""; return logsToFlush; @@ -470,7 +474,7 @@ module.exports = { } else { logs.push({ - lines: logger.flush(), + lines: logger.flush(true), id: "-", }); } From 57126efe86db96396d921651e4d66aea4e3f4c76 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 23 Jan 2017 10:24:23 +0000 Subject: [PATCH 0120/1951] Reposition typing avatars relative to "is typing" --- .../css/matrix-react-sdk/structures/_RoomStatusBar.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss index d3f1439d..d124f9c6 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomStatusBar.scss @@ -21,10 +21,10 @@ limitations under the License. /* position the indicator in the same place horizontally as .mx_EventTile_avatar. */ .mx_RoomStatusBar_indicator { - padding-left: 18px; + padding-left: 17px; padding-right: 12px; margin-left: -73px; - margin-top: 13px; + margin-top: 8px; float: left; width: 24px; text-align: center; From b582cf0a5f24155f4de25320030f34b1b581dbe8 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 23 Jan 2017 13:30:39 +0000 Subject: [PATCH 0121/1951] Remove CSS for StatusBar mid-timeline --- .../css/matrix-react-sdk/structures/RoomView.css | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css index 6dc22e2f..43434d3f 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css +++ b/src/skins/vector/css/matrix-react-sdk/structures/RoomView.css @@ -216,16 +216,6 @@ hr.mx_RoomView_myReadMarker { .mx_RoomView_statusArea_expanded { max-height: 50px; - margin-top: 0px; -} - -.mx_RoomView_statusArea_expanded.mx_RoomView_statusArea_mid_timeline { - margin-top: -50px; - - -webkit-transition: all .2s ease-in; - -moz-transition: all .2s ease-in; - -ms-transition: all .2s ease-in; - -o-transition: all .2s ease-in; } .mx_RoomView_statusAreaBox { From b7204bf868cdf93040afb581465b5b04712081a7 Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Mon, 23 Jan 2017 13:33:39 +0000 Subject: [PATCH 0122/1951] Use primary bg color on statusArea --- src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9e30b9f2..8623e032 100644 --- a/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss +++ b/src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss @@ -160,7 +160,7 @@ hr.mx_RoomView_myReadMarker { flex: 0 0 auto; max-height: 0px; - background-color: #fff; + background-color: $primary-bg-color; z-index: 1000; overflow: hidden; From bc714ba3a051ec64d3726e6933c7e766ecbd92b4 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 23 Jan 2017 14:22:54 +0000 Subject: [PATCH 0123/1951] Add eslint config An early start that gets some linting in place. --- .eslintignore | 1 + .eslintrc.js | 3 +++ package.json | 7 ++++++- test/.eslintrc.js | 5 +++++ 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .eslintignore create mode 100644 .eslintrc.js create mode 100644 test/.eslintrc.js diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..ea40ba29 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +src/vector/modernizr.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..c181384f --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: ["./node_modules/matrix-react-sdk/.eslintrc.js"], +} diff --git a/package.json b/package.json index b7b69d0c..f14fe8e7 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,8 @@ "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:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"", + "lint": "eslint src/", + "lintall": "eslint src/ test/", "clean": "rimraf lib webapp electron/dist", "prepublish": "npm run build:compile", "test": "karma start --single-run=true --autoWatch=false --browsers PhantomJS --colors=false", @@ -95,6 +97,9 @@ "electron-builder": "^11.2.4", "electron-builder-squirrel-windows": "^11.2.1", "emojione": "^2.2.3", + "eslint": "^3.14.0", + "eslint-plugin-flowtype": "^2.30.0", + "eslint-plugin-react": "^6.9.0", "expect": "^1.16.0", "fs-extra": "^0.30.0", "html-webpack-plugin": "^2.24.0", @@ -148,7 +153,7 @@ "target": "deb", "maintainer": "support@riot.im", "desktop": { - "StartupWMClass": "riot-web" + "StartupWMClass": "riot-web" } }, "win": { diff --git a/test/.eslintrc.js b/test/.eslintrc.js new file mode 100644 index 00000000..4cc4659d --- /dev/null +++ b/test/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + mocha: true, + }, +} From 4c3ea1341370a0722c34be6551ca791480c74db6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 23 Jan 2017 15:42:50 +0000 Subject: [PATCH 0124/1951] Make jenkins run the linter --- scripts/jenkins.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/jenkins.sh b/scripts/jenkins.sh index bd27d6e3..5ccc1991 100755 --- a/scripts/jenkins.sh +++ b/scripts/jenkins.sh @@ -26,6 +26,9 @@ cp -r olm/package node_modules/olm # run the mocha tests npm run test +# run eslint +npm run lintall -- -f checkstyle -o eslint.xml || true + rm dist/vector-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist # node_modules deps from 'npm install' don't have a .git dir so can't From f3df86872b4e4bc7b4e7a489993cf3e73c6a3637 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 23 Jan 2017 17:37:57 +0000 Subject: [PATCH 0125/1951] Fix tightlooping when flush()ing without any logs The promise would resolve immediately, nulling out `flushPromise`. This would then immediately be set from `new Promise((resolve, reject) => {...})` turning it back into non-null `flushPromise`. The resolve handler was called so the next `flush()` would see "oh yes, there is a non-null `flushPromise`" then promptly try to set `flushAgainPromise` which chains off the resolved `flushPromise` which relied on `flushPromise` being `null`ed out after `resolve()`, causing the chained `flush()` to see "oh yes, there is a non-null `flushPromise`" which... ad infinitum. This PR fixes it by making the nulling out asynchronous but the fact it took me this long to debug this issue indicates to me that this is a terrible piece of code. Will re-write. --- src/vector/rageshake.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 3cc15886..a17c829a 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -201,13 +201,11 @@ class IndexedDBLogStore { if (!this.db) { // not connected yet or user rejected access for us to r/w to // the db. - this.flushPromise = null; reject(new Error("No connected database")); return; } const lines = this.logger.flush(); if (lines.length === 0) { - this.flushPromise = null; resolve(); return; } @@ -217,18 +215,20 @@ class IndexedDBLogStore { let lastModStore = txn.objectStore("logslastmod"); lastModStore.put(this._generateLastModifiedTime()); txn.oncomplete = (event) => { - this.flushPromise = null; resolve(); }; txn.onerror = (event) => { console.error( "Failed to flush logs : ", event ); - this.flushPromise = null; reject( new Error("Failed to write logs: " + event.target.errorCode) ); } + }).then(() => { + this.flushPromise = null; + }, (err) => { + this.flushPromise = null; }); return this.flushPromise; } From 706ffbec3c2eb41d67587ace8e01dd3c7df0e03b Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Mon, 23 Jan 2017 19:01:22 +0100 Subject: [PATCH 0126/1951] add riot logos --- src/skins/vector/img/logos/riot-logo-1.svg | 1 + src/skins/vector/img/logos/riot-logo-2.svg | 1 + src/skins/vector/img/logos/riot-logo-3.svg | 1 + src/skins/vector/img/logos/riot-logo-4.svg | 1 + src/skins/vector/img/logos/riot-logo-5.svg | 1 + src/skins/vector/img/logos/riot-logo-bw.svg | 1 + src/skins/vector/img/logos/riot-logo.svg | 32 +++++++++++++++++++++ 7 files changed, 38 insertions(+) create mode 100644 src/skins/vector/img/logos/riot-logo-1.svg create mode 100644 src/skins/vector/img/logos/riot-logo-2.svg create mode 100644 src/skins/vector/img/logos/riot-logo-3.svg create mode 100644 src/skins/vector/img/logos/riot-logo-4.svg create mode 100644 src/skins/vector/img/logos/riot-logo-5.svg create mode 100644 src/skins/vector/img/logos/riot-logo-bw.svg create mode 100644 src/skins/vector/img/logos/riot-logo.svg diff --git a/src/skins/vector/img/logos/riot-logo-1.svg b/src/skins/vector/img/logos/riot-logo-1.svg new file mode 100644 index 00000000..6b79dd9e --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-1.svg @@ -0,0 +1 @@ +Asset 4 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo-2.svg b/src/skins/vector/img/logos/riot-logo-2.svg new file mode 100644 index 00000000..96e0bbb1 --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-2.svg @@ -0,0 +1 @@ +Asset 2 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo-3.svg b/src/skins/vector/img/logos/riot-logo-3.svg new file mode 100644 index 00000000..985b9c9f --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-3.svg @@ -0,0 +1 @@ +Asset 5 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo-4.svg b/src/skins/vector/img/logos/riot-logo-4.svg new file mode 100644 index 00000000..24a7ddab --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-4.svg @@ -0,0 +1 @@ +Asset 3 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo-5.svg b/src/skins/vector/img/logos/riot-logo-5.svg new file mode 100644 index 00000000..6a2c61df --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-5.svg @@ -0,0 +1 @@ +Asset 1 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo-bw.svg b/src/skins/vector/img/logos/riot-logo-bw.svg new file mode 100644 index 00000000..cb6d77d6 --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo-bw.svg @@ -0,0 +1 @@ +Asset 6 \ No newline at end of file diff --git a/src/skins/vector/img/logos/riot-logo.svg b/src/skins/vector/img/logos/riot-logo.svg new file mode 100644 index 00000000..cbfaa625 --- /dev/null +++ b/src/skins/vector/img/logos/riot-logo.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + From 597705716b30e0efa9cca8e5ac744bda36bf503e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 24 Jan 2017 11:26:09 +0000 Subject: [PATCH 0127/1951] Hopefully, fix intermittent test failure This seeks to fix the intermittent failure of the "MatrixClient rehydrated from stored credentials" tests. The problem appears to be that the 'load_completed' is sometimes taking a while to come through from the dispatcher - or rather, it is taking a long time to get *sent* to the dispatcher: the chain of `q().then().catch().done()` in componentDidMount can take a while to happen. As a workaround, give the test a few goes when waiting for us to start syncing. It's not ideal to be poking into the internal state of MatrixChat like this, but it'll do for now. --- test/app-tests/loading.js | 69 +++++++++++++++++++++++---------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js index 03a4d11e..2fe82e51 100644 --- a/test/app-tests/loading.js +++ b/test/app-tests/loading.js @@ -94,13 +94,13 @@ describe('loading:', function () { loadCompletePromise = loadCompleteDefer.promise; function onNewScreen(screen) { - console.log("newscreen "+screen); + console.log(Date.now() + " newscreen "+screen); if (!appLoaded) { lastLoadedScreen = screen; } else { var hash = '#/' + screen; windowLocation.hash = hash; - console.log("browser URI now "+ windowLocation); + console.log(Date.now() + " browser URI now "+ windowLocation); } } @@ -122,22 +122,22 @@ describe('loading:', function () { ); function routeUrl(location, matrixChat) { - console.log("Routing URL "+location); + console.log(Date.now() + " Routing URL "+location); var fragparts = parseQsFromFragment(location); matrixChat.showScreen(fragparts.location.substring(1), fragparts.params); } // pause for a cycle, then simulate the window.onload handler - q.delay(0).then(() => { - console.log("simulating window.onload"); + window.setTimeout(() => { + console.log(Date.now() + " simulating window.onload"); routeUrl(windowLocation, matrixChat); appLoaded = true; if (lastLoadedScreen) { onNewScreen(lastLoadedScreen); lastLoadedScreen = null; } - }).done(); + }, 0); } describe("Clean load with no stored credentials:", function() { @@ -243,10 +243,8 @@ describe('loading:', function () { loadApp(); - q.delay(1).then(() => { - // we expect a spinner - assertAtSyncingSpinner(matrixChat); - + return awaitSyncingSpinner(matrixChat).then(() => { + // we got a sync spinner - let the sync complete return httpBackend.flush(); }).then(() => { // once the sync completes, we should have a directory @@ -266,10 +264,8 @@ describe('loading:', function () { uriFragment: "#/room/!room:id", }); - q.delay(1).then(() => { - // we expect a spinner - assertAtSyncingSpinner(matrixChat); - + return awaitSyncingSpinner(matrixChat).then(() => { + // we got a sync spinner - let the sync complete return httpBackend.flush(); }).then(() => { // once the sync completes, we should have a room view @@ -300,12 +296,9 @@ describe('loading:', function () { return httpBackend.flush(); }).then(() => { - // Wait for another trip around the event loop for the UI to update - return q.delay(1); + return awaitSyncingSpinner(matrixChat); }).then(() => { - // now we should have a spinner with a logout link - assertAtSyncingSpinner(matrixChat); - + // we got a sync spinner - let the sync complete httpBackend.when('GET', '/sync').respond(200, {}); return httpBackend.flush(); }).then(() => { @@ -338,12 +331,8 @@ describe('loading:', function () { return httpBackend.flush(); }).then(() => { - // Wait for another trip around the event loop for the UI to update - return q.delay(1); + return awaitSyncingSpinner(matrixChat); }).then(() => { - // now we should have a spinner with a logout link - assertAtSyncingSpinner(matrixChat); - httpBackend.when('GET', '/sync').check(function(req) { expect(req.path).toMatch(new RegExp("^https://homeserver/")); }).respond(200, {}); @@ -377,12 +366,8 @@ describe('loading:', function () { return httpBackend.flush(); }).then(() => { - // Wait for another trip around the event loop for the UI to update - return q.delay(1); + return awaitSyncingSpinner(matrixChat); }).then(() => { - // now we should have a spinner with a logout link - assertAtSyncingSpinner(matrixChat); - httpBackend.when('GET', '/sync').respond(200, {}); return httpBackend.flush(); }).then(() => { @@ -448,6 +433,32 @@ function assertAtLoadingSpinner(matrixChat) { // we've got login creds, and are waiting for the sync to finish. // the page includes a logout link. +function awaitSyncingSpinner(matrixChat, retryLimit, retryCount) { + if (retryLimit === undefined) { + retryLimit = 5; + } + if (retryCount === undefined) { + retryCount = 0; + } + + if (matrixChat.state.loading) { + console.log(Date.now() + " Awaiting sync spinner: still loading."); + if (retryCount >= retryLimit) { + throw new Error("MatrixChat still not loaded after " + + retryCount + " tries"); + } + return q.delay(0).then(() => { + return awaitSyncingSpinner(matrixChat, retryLimit, retryCount + 1); + }); + } + + console.log(Date.now() + " Awaiting sync spinner: load complete."); + + // state looks good, check the rendered output + assertAtSyncingSpinner(matrixChat); + return q(); +} + function assertAtSyncingSpinner(matrixChat) { var domComponent = ReactDOM.findDOMNode(matrixChat); expect(domComponent.className).toEqual("mx_MatrixChat_splash"); From 6f3b70dbb02bb6edb614209592546fb3ba28854b Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 24 Jan 2017 12:43:18 +0000 Subject: [PATCH 0128/1951] Use Q promises and isPending to make logic simpler --- src/vector/rageshake.js | 52 ++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index a17c829a..28f901a3 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -16,6 +16,7 @@ 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 // disk and submit bug reports. Rationale is as follows: @@ -105,9 +106,6 @@ class IndexedDBLogStore { this.db = null; // Promise is not null when a flush is IN PROGRESS this.flushPromise = null; - // Promise is not null when flush() is called when one is already in - // progress. - this.flushAgainPromise = null; } /** @@ -115,7 +113,7 @@ class IndexedDBLogStore { */ connect() { let req = this.indexedDB.open("logs"); - return new Promise((resolve, reject) => { + return q.Promise((resolve, reject) => { req.onsuccess = (event) => { this.db = event.target.result; // Periodically flush logs to local storage / indexeddb @@ -167,13 +165,10 @@ class IndexedDBLogStore { * - If B doesn't wait for A's flush to complete, B will be missing the * contents of A's flush. * To protect against this, we set 'flushPromise' when a flush is ongoing. - * Subsequent calls to flush() during this period return a new promise - * 'flushAgainPromise' which is chained off the current 'flushPromise'. - * Subsequent calls to flush() when the first flush hasn't completed will - * return the same 'flushAgainPromise' as we can guarantee that we WILL - * do a brand new flush at some point in the future. Once the first flush - * has completed, 'flushAgainPromise' becomes 'flushPromise' and can be - * chained again. + * Subsequent calls to flush() during this period will chain another flush. + * This guarantees that we WILL do a brand new flush at some point in the + * future. Once the flushes have finished, it's safe to clobber the promise + * with a new one to prevent very deep promise chains from building up. * * This guarantees that we will always eventually do a flush when flush() is * called. @@ -181,23 +176,20 @@ class IndexedDBLogStore { * @return {Promise} Resolved when the logs have been flushed. */ flush() { - if (this.flushPromise) { // a flush is ongoing - if (this.flushAgainPromise) { // a flush is queued up, return that. - return this.flushAgainPromise; - } - // queue up a new flush - this.flushAgainPromise = this.flushPromise.then(() => { - // the current flush has completed, so shuffle the promises - // around: - // flushAgainPromise => flushPromise and null flushAgainPromise. - // flushPromise has already nulled itself. - this.flushAgainPromise = null; + // check if a flush() operation is ongoing + if (this.flushPromise && this.flushPromise.isPending()) { + // chain a flush operation after this one has completed to guarantee + // that a complete flush() is done. This does mean that if there are + // 3 calls to flush() in one go, the 2nd and 3rd promises will run + // concurrently, but this is fine since they can safely race when + // collecting logs. + return this.flushPromise.then(() => { return this.flush(); }); - return this.flushAgainPromise; } - - this.flushPromise = new Promise((resolve, reject) => { + // there is no flush promise or there was but it has finished, so do + // a brand new one, destroying the chain which may have been built up. + this.flushPromise = q.Promise((resolve, reject) => { if (!this.db) { // not connected yet or user rejected access for us to r/w to // the db. @@ -225,10 +217,6 @@ class IndexedDBLogStore { new Error("Failed to write logs: " + event.target.errorCode) ); } - }).then(() => { - this.flushPromise = null; - }, (err) => { - this.flushPromise = null; }); return this.flushPromise; } @@ -286,7 +274,7 @@ class IndexedDBLogStore { } function deleteLogs(id) { - return new Promise((resolve, reject) => { + return q.Promise((resolve, reject) => { const txn = db.transaction( ["logs", "logslastmod"], "readwrite" ); @@ -377,7 +365,7 @@ class IndexedDBLogStore { */ function selectQuery(store, keyRange, resultMapper) { const query = store.openCursor(keyRange); - return new Promise((resolve, reject) => { + return q.Promise((resolve, reject) => { let results = []; query.onerror = (event) => { reject(new Error("Query failed: " + event.target.errorCode)); @@ -479,7 +467,7 @@ module.exports = { }); } - await new Promise((resolve, reject) => { + await q.Promise((resolve, reject) => { request({ method: "POST", url: bugReportEndpoint, From 31878d8a448ddab39c17306a648bded4176b22c3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Jan 2017 13:07:34 +0000 Subject: [PATCH 0129/1951] Also need eslint-config-google --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f14fe8e7..2aff1859 100644 --- a/package.json +++ b/package.json @@ -98,6 +98,7 @@ "electron-builder-squirrel-windows": "^11.2.1", "emojione": "^2.2.3", "eslint": "^3.14.0", + "eslint-config-google": "^0.7.1", "eslint-plugin-flowtype": "^2.30.0", "eslint-plugin-react": "^6.9.0", "expect": "^1.16.0", From fe64b043390cbbe135f795d2337ae44093c84474 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 24 Jan 2017 13:35:41 +0000 Subject: [PATCH 0130/1951] More test resilience Give the tests more than one chance for the roomview to load. --- test/app-tests/loading.js | 45 ++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/test/app-tests/loading.js b/test/app-tests/loading.js index 2fe82e51..4504d00b 100644 --- a/test/app-tests/loading.js +++ b/test/app-tests/loading.js @@ -209,14 +209,11 @@ describe('loading:', function () { httpBackend.when('POST', '/filter').respond(200, { filter_id: 'fid' }); httpBackend.when('GET', '/sync').respond(200, {}); return httpBackend.flush(); - }).then(() => { - // Wait for another trip around the event loop for the UI to update - return q.delay(1); }).then(() => { // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }).then(() => { httpBackend.verifyNoOutstandingExpectation(); - ReactTestUtils.findRenderedComponentWithType( - matrixChat, sdk.getComponent('structures.RoomView')); expect(windowLocation.hash).toEqual("#/room/!room:id"); // and the localstorage should have been updated @@ -269,9 +266,9 @@ describe('loading:', function () { return httpBackend.flush(); }).then(() => { // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }).then(() => { httpBackend.verifyNoOutstandingExpectation(); - ReactTestUtils.findRenderedComponentWithType( - matrixChat, sdk.getComponent('structures.RoomView')); expect(windowLocation.hash).toEqual("#/room/!room:id"); }).done(done, done); @@ -370,14 +367,11 @@ describe('loading:', function () { }).then(() => { httpBackend.when('GET', '/sync').respond(200, {}); return httpBackend.flush(); - }).then(() => { - // Wait for another trip around the event loop for the UI to update - return q.delay(1); }).then(() => { // once the sync completes, we should have a room view + return awaitRoomView(matrixChat); + }).then(() => { httpBackend.verifyNoOutstandingExpectation(); - ReactTestUtils.findRenderedComponentWithType( - matrixChat, sdk.getComponent('structures.RoomView')); expect(windowLocation.hash).toEqual("#/room/!room:id"); }).done(done, done); }); @@ -469,3 +463,30 @@ function assertAtSyncingSpinner(matrixChat) { matrixChat, 'a'); expect(logoutLink.text).toEqual("Logout"); } + +function awaitRoomView(matrixChat, retryLimit, retryCount) { + if (retryLimit === undefined) { + retryLimit = 5; + } + if (retryCount === undefined) { + retryCount = 0; + } + + if (!matrixChat.state.ready) { + console.log(Date.now() + " Awaiting room view: not ready yet."); + if (retryCount >= retryLimit) { + throw new Error("MatrixChat still not ready after " + + retryCount + " tries"); + } + return q.delay(0).then(() => { + return awaitRoomView(matrixChat, retryLimit, retryCount + 1); + }); + } + + console.log(Date.now() + " Awaiting room view: now ready."); + + // state looks good, check the rendered output + ReactTestUtils.findRenderedComponentWithType( + matrixChat, sdk.getComponent('structures.RoomView')); + return q(); +} From 307c4f3dd152c47ecc9aca9164db403ba0df623e Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Tue, 24 Jan 2017 14:02:44 +0000 Subject: [PATCH 0131/1951] Increase the max-height of the expanded status bar This will ensure that errors, unsent messages, etc. will be allowed enough height to display their contents from within the status bar without being chopped. Fixes #3041 --- src/skins/vector/css/matrix-react-sdk/structures/_RoomView.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8623e032..55771f79 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 { } .mx_RoomView_statusArea_expanded { - max-height: 50px; + max-height: 100px; } .mx_RoomView_statusAreaBox { From efbea0e942a40ab38389740fb42dd8cba1e441de Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 24 Jan 2017 14:45:16 +0000 Subject: [PATCH 0132/1951] Add css for bug report dialog --- src/skins/vector/css/_components.scss | 1 + .../views/dialogs/_BugReportDialog.scss | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 src/skins/vector/css/matrix-react-sdk/views/dialogs/_BugReportDialog.scss diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss index 51dd0ada..323c22e0 100644 --- a/src/skins/vector/css/_components.scss +++ b/src/skins/vector/css/_components.scss @@ -12,6 +12,7 @@ @import "./matrix-react-sdk/structures/_UserSettings.scss"; @import "./matrix-react-sdk/structures/login/_Login.scss"; @import "./matrix-react-sdk/views/avatars/_BaseAvatar.scss"; +@import "./matrix-react-sdk/views/dialogs/_BugReportDialog.scss"; @import "./matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss"; @import "./matrix-react-sdk/views/dialogs/_EncryptedEventDialog.scss"; @import "./matrix-react-sdk/views/dialogs/_SetDisplayNameDialog.scss"; diff --git a/src/skins/vector/css/matrix-react-sdk/views/dialogs/_BugReportDialog.scss b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_BugReportDialog.scss new file mode 100644 index 00000000..0f47e974 --- /dev/null +++ b/src/skins/vector/css/matrix-react-sdk/views/dialogs/_BugReportDialog.scss @@ -0,0 +1,20 @@ +/* +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. +*/ + +.mx_BugReportDialog_input { + width: 100%; + box-sizing: border-box; +} From e225d3e3708bccf6cd458453206d076841011630 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Tue, 24 Jan 2017 17:05:01 +0000 Subject: [PATCH 0133/1951] Preserve ordering of flush()es by not letting subsequent flush()es race --- src/vector/rageshake.js | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vector/rageshake.js b/src/vector/rageshake.js index 28f901a3..086ab155 100644 --- a/src/vector/rageshake.js +++ b/src/vector/rageshake.js @@ -104,8 +104,9 @@ class IndexedDBLogStore { this.id = "instance-" + Math.random() + Date.now(); this.index = 0; this.db = null; - // Promise is not null when a flush is IN PROGRESS this.flushPromise = null; + // set if flush() is called whilst one is ongoing + this.flushAgainPromise = null; } /** @@ -165,10 +166,8 @@ class IndexedDBLogStore { * - If B doesn't wait for A's flush to complete, B will be missing the * contents of A's flush. * To protect against this, we set 'flushPromise' when a flush is ongoing. - * Subsequent calls to flush() during this period will chain another flush. - * This guarantees that we WILL do a brand new flush at some point in the - * future. Once the flushes have finished, it's safe to clobber the promise - * with a new one to prevent very deep promise chains from building up. + * Subsequent calls to flush() during this period will chain another flush, + * then keep returning that same chained flush. * * This guarantees that we will always eventually do a flush when flush() is * called. @@ -178,14 +177,17 @@ class IndexedDBLogStore { flush() { // check if a flush() operation is ongoing if (this.flushPromise && this.flushPromise.isPending()) { - // chain a flush operation after this one has completed to guarantee - // that a complete flush() is done. This does mean that if there are - // 3 calls to flush() in one go, the 2nd and 3rd promises will run - // concurrently, but this is fine since they can safely race when - // collecting logs. - return this.flushPromise.then(() => { + if (this.flushAgainPromise && this.flushAgainPromise.isPending()) { + // this is the 3rd+ time we've called flush() : return the same + // promise. + return this.flushAgainPromise; + } + // queue up a flush to occur immediately after the pending one + // completes. + this.flushAgainPromise = this.flushPromise.then(() => { return this.flush(); }); + return this.flushAgainPromise; } // there is no flush promise or there was but it has finished, so do // a brand new one, destroying the chain which may have been built up. From 2ca871a0286358cb6f7708b4e849cbd4ea19832e Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Jan 2017 18:18:56 +0000 Subject: [PATCH 0134/1951] Don't use hash-named directory for dev server Otherwise Chrome thinks you're working on a new file every time you refresh and therefore closes source tabs and removes breakpoints which is very annoying. It also allegedly makes webpack-dev-server run out of memory because it has to remember all the different files. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2aff1859..5528c7c7 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle:dev", "dist": "scripts/package.sh", "start:res": "node scripts/copy-res.js -w", - "start:js": "webpack-dev-server -w --progress", + "start:js": "webpack-dev-server --output-filename=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:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"", From eaff0abfb07fddc1f657a1c942c72968e7b47619 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 25 Jan 2017 10:28:04 +0000 Subject: [PATCH 0135/1951] Add link to Chrome issue status for fixing line numbers --- src/vector/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vector/index.js b/src/vector/index.js index d1d85e06..c40edd7e 100644 --- a/src/vector/index.js +++ b/src/vector/index.js @@ -38,7 +38,7 @@ require('draft-js/dist/Draft.css'); const rageshake = require("./rageshake"); rageshake.init().then(() => { - console.log("Initialised rageshake"); + console.log("Initialised rageshake: See https://bugs.chromium.org/p/chromium/issues/detail?id=583193 to fix line numbers on Chrome."); rageshake.cleanup(); }, (err) => { console.error("Failed to initialise rageshake: " + err); From 1c926941d34ea328d4505122fe7e72b1a87f2e4c Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 25 Jan 2017 11:28:59 +0000 Subject: [PATCH 0136/1951] Add --output-chunk-file too --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5528c7c7..915056e6 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "build:dev": "node scripts/babelcheck.js && npm run build:res && npm run build:bundle:dev", "dist": "scripts/package.sh", "start:res": "node scripts/copy-res.js -w", - "start:js": "webpack-dev-server --output-filename=bundles/_dev_/[name].js -w --progress", + "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:prod": "parallelshell \"npm run start:res\" \"npm run start:js:prod\"", From 0021fb37f3c26692a7906a6503cc26a0409a1a0b Mon Sep 17 00:00:00 2001 From: Luke Barnard Date: Wed, 25 Jan 2017 14:27:25 +0000 Subject: [PATCH 0137/1951] Set BaseAvatar_image bg colour = #fff --- .../vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss b/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss index d481e5c1..f23b929b 100644 --- a/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss +++ b/src/skins/vector/css/matrix-react-sdk/views/avatars/_BaseAvatar.scss @@ -31,4 +31,5 @@ limitations under the License. .mx_BaseAvatar_image { border-radius: 40px; vertical-align: top; + background-color: #fff; } From 0a9f02abcc527d64f2f7bbdd1d296008d4e94ca6 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 25 Jan 2017 16:51:26 +0000 Subject: [PATCH 0138/1951] Glue the dialog to rageshake: honour sendLogs flag. --- src/component-index.js | 60 +++++---- .../views/dialogs/BugReportDialog.js | 126 ++++++++++++++++++ src/vector/rageshake.js | 25 ++-- 3 files changed, 171 insertions(+), 40 deletions(-) create mode 100644 src/components/views/dialogs/BugReportDialog.js diff --git a/src/component-index.js b/src/component-index.js index 3141087c..456f8176 100644 --- a/src/component-index.js +++ b/src/component-index.js @@ -27,60 +27,62 @@ limitations under the License. module.exports.components = require('matrix-react-sdk/lib/component-index').components; import structures$BottomLeftMenu from './components/structures/BottomLeftMenu'; -module.exports.components['structures.BottomLeftMenu'] = structures$BottomLeftMenu; +structures$BottomLeftMenu && (module.exports.components['structures.BottomLeftMenu'] = structures$BottomLeftMenu); import structures$CompatibilityPage from './components/structures/CompatibilityPage'; -module.exports.components['structures.CompatibilityPage'] = structures$CompatibilityPage; +structures$CompatibilityPage && (module.exports.components['structures.CompatibilityPage'] = structures$CompatibilityPage); import structures$LeftPanel from './components/structures/LeftPanel'; -module.exports.components['structures.LeftPanel'] = structures$LeftPanel; +structures$LeftPanel && (module.exports.components['structures.LeftPanel'] = structures$LeftPanel); import structures$RightPanel from './components/structures/RightPanel'; -module.exports.components['structures.RightPanel'] = structures$RightPanel; +structures$RightPanel && (module.exports.components['structures.RightPanel'] = structures$RightPanel); import structures$RoomDirectory from './components/structures/RoomDirectory'; -module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory; +structures$RoomDirectory && (module.exports.components['structures.RoomDirectory'] = structures$RoomDirectory); import structures$RoomSubList from './components/structures/RoomSubList'; -module.exports.components['structures.RoomSubList'] = structures$RoomSubList; +structures$RoomSubList && (module.exports.components['structures.RoomSubList'] = structures$RoomSubList); import structures$SearchBox from './components/structures/SearchBox'; -module.exports.components['structures.SearchBox'] = structures$SearchBox; +structures$SearchBox && (module.exports.components['structures.SearchBox'] = structures$SearchBox); import structures$ViewSource from './components/structures/ViewSource'; -module.exports.components['structures.ViewSource'] = structures$ViewSource; +structures$ViewSource && (module.exports.components['structures.ViewSource'] = structures$ViewSource); import views$context_menus$MessageContextMenu from './components/views/context_menus/MessageContextMenu'; -module.exports.components['views.context_menus.MessageContextMenu'] = views$context_menus$MessageContextMenu; +views$context_menus$MessageContextMenu && (module.exports.components['views.context_menus.MessageContextMenu'] = views$context_menus$MessageContextMenu); import views$context_menus$NotificationStateContextMenu from './components/views/context_menus/NotificationStateContextMenu'; -module.exports.components['views.context_menus.NotificationStateContextMenu'] = views$context_menus$NotificationStateContextMenu; +views$context_menus$NotificationStateContextMenu && (module.exports.components['views.context_menus.NotificationStateContextMenu'] = views$context_menus$NotificationStateContextMenu); import views$context_menus$RoomTagContextMenu from './components/views/context_menus/RoomTagContextMenu'; -module.exports.components['views.context_menus.RoomTagContextMenu'] = views$context_menus$RoomTagContextMenu; +views$context_menus$RoomTagContextMenu && (module.exports.components['views.context_menus.RoomTagContextMenu'] = views$context_menus$RoomTagContextMenu); +import views$dialogs$BugReportDialog from './components/views/dialogs/BugReportDialog'; +views$dialogs$BugReportDialog && (module.exports.components['views.dialogs.BugReportDialog'] = views$dialogs$BugReportDialog); import views$dialogs$ChangelogDialog from './components/views/dialogs/ChangelogDialog'; -module.exports.components['views.dialogs.ChangelogDialog'] = views$dialogs$ChangelogDialog; +views$dialogs$ChangelogDialog && (module.exports.components['views.dialogs.ChangelogDialog'] = views$dialogs$ChangelogDialog); import views$directory$NetworkDropdown from './components/views/directory/NetworkDropdown'; -module.exports.components['views.directory.NetworkDropdown'] = views$directory$NetworkDropdown; +views$directory$NetworkDropdown && (module.exports.components['views.directory.NetworkDropdown'] = views$directory$NetworkDropdown); import views$elements$ImageView from './components/views/elements/ImageView'; -module.exports.components['views.elements.ImageView'] = views$elements$ImageView; +views$elements$ImageView && (module.exports.components['views.elements.ImageView'] = views$elements$ImageView); import views$elements$Spinner from './components/views/elements/Spinner'; -module.exports.components['views.elements.Spinner'] = views$elements$Spinner; +views$elements$Spinner && (module.exports.components['views.elements.Spinner'] = views$elements$Spinner); import views$globals$GuestWarningBar from './components/views/globals/GuestWarningBar'; -module.exports.components['views.globals.GuestWarningBar'] = views$globals$GuestWarningBar; +views$globals$GuestWarningBar && (module.exports.components['views.globals.GuestWarningBar'] = views$globals$GuestWarningBar); import views$globals$MatrixToolbar from './components/views/globals/MatrixToolbar'; -module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar; +views$globals$MatrixToolbar && (module.exports.components['views.globals.MatrixToolbar'] = views$globals$MatrixToolbar); import views$globals$NewVersionBar from './components/views/globals/NewVersionBar'; -module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar; +views$globals$NewVersionBar && (module.exports.components['views.globals.NewVersionBar'] = views$globals$NewVersionBar); import views$login$VectorCustomServerDialog from './components/views/login/VectorCustomServerDialog'; -module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog; +views$login$VectorCustomServerDialog && (module.exports.components['views.login.VectorCustomServerDialog'] = views$login$VectorCustomServerDialog); import views$login$VectorLoginFooter from './components/views/login/VectorLoginFooter'; -module.exports.components['views.login.VectorLoginFooter'] = views$login$VectorLoginFooter; +views$login$VectorLoginFooter && (module.exports.components['views.login.VectorLoginFooter'] = views$login$VectorLoginFooter); import views$login$VectorLoginHeader from './components/views/login/VectorLoginHeader'; -module.exports.components['views.login.VectorLoginHeader'] = views$login$VectorLoginHeader; +views$login$VectorLoginHeader && (module.exports.components['views.login.VectorLoginHeader'] = views$login$VectorLoginHeader); import views$messages$DateSeparator from './components/views/messages/DateSeparator'; -module.exports.components['views.messages.DateSeparator'] = views$messages$DateSeparator; +views$messages$DateSeparator && (module.exports.components['views.messages.DateSeparator'] = views$messages$DateSeparator); import views$messages$MessageTimestamp from './components/views/messages/MessageTimestamp'; -module.exports.components['views.messages.MessageTimestamp'] = views$messages$MessageTimestamp; +views$messages$MessageTimestamp && (module.exports.components['views.messages.MessageTimestamp'] = views$messages$MessageTimestamp); import views$rooms$DNDRoomTile from './components/views/rooms/DNDRoomTile'; -module.exports.components['views.rooms.DNDRoomTile'] = views$rooms$DNDRoomTile; +views$rooms$DNDRoomTile && (module.exports.components['views.rooms.DNDRoomTile'] = views$rooms$DNDRoomTile); import views$rooms$RoomDropTarget from './components/views/rooms/RoomDropTarget'; -module.exports.components['views.rooms.RoomDropTarget'] = views$rooms$RoomDropTarget; +views$rooms$RoomDropTarget && (module.exports.components['views.rooms.RoomDropTarget'] = views$rooms$RoomDropTarget); import views$rooms$RoomTooltip from './components/views/rooms/RoomTooltip'; -module.exports.components['views.rooms.RoomTooltip'] = views$rooms$RoomTooltip; +views$rooms$RoomTooltip && (module.exports.components['views.rooms.RoomTooltip'] = views$rooms$RoomTooltip); import views$rooms$SearchBar from './components/views/rooms/SearchBar'; -module.exports.components['views.rooms.SearchBar'] = views$rooms$SearchBar; +views$rooms$SearchBar && (module.exports.components['views.rooms.SearchBar'] = views$rooms$SearchBar); import views$settings$IntegrationsManager from './components/views/settings/IntegrationsManager'; -module.exports.components['views.settings.IntegrationsManager'] = views$settings$IntegrationsManager; +views$settings$IntegrationsManager && (module.exports.components['views.settings.IntegrationsManager'] = views$settings$IntegrationsManager); import views$settings$Notifications from './components/views/settings/Notifications'; -module.exports.components['views.settings.Notifications'] = views$settings$Notifications; +views$settings$Notifications && (module.exports.components['views.settings.Notifications'] = views$settings$Notifications); diff --git a/src/components/views/dialogs/BugReportDialog.js b/src/components/views/dialogs/BugReportDialog.js new file mode 100644 index 00000000..e3c8945a --- /dev/null +++ b/src/components/views/dialogs/BugReportDialog.js @@ -0,0 +1,126 @@ +/* +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 React from 'react'; +import sdk from 'matrix-react-sdk'; +import rageshake from '../../../vector/rageshake'; + +export default class BugReportDialog extends React.Component { + constructor(props, context) { + super(props, context); + this.state = { + sendLogs: true, + busy: false, + err: null, + text: "", + }; + this._onSubmit = this._onSubmit.bind(this); + this._onCancel = this._onCancel.bind(this); + this._onTextChange = this._onTextChange.bind(this); + this._onSendLogsChange = this._onSendLogsChange.bind(this); + } + + _onCancel(ev) { + this.props.onFinished(false); + } + + _onSubmit(ev) { + const sendLogs = this.state.sendLogs; + const userText = this.state.text; + if (!sendLogs && userText.trim().length === 0) { + this.setState({ + err: "Please describe the bug and/or send logs.", + }); + return; + } + this.setState({ busy: true, err: null }); + rageshake.sendBugReport(userText, sendLogs).then(() => { + this.setState({ busy: false }); + }, (err) => { + this.setState({ busy: false, err: `Failed: ${err.message}` }); + }); + } + + _onTextChange(ev) { + this.setState({ text: ev.target.value }); + } + + _onSendLogsChange(ev) { + this.setState({ sendLogs: ev.target.checked }); + } + + render() { + const Loader = sdk.getComponent("elements.Spinner"); + + let error = null; + if (this.state.err) { + error =
    + {this.state.err} +
    ; + } + + const okLabel = this.state.busy ? : 'Send'; + + let cancelButton = null; + if (!this.state.busy) { + cancelButton = ; + } + + return ( +
    +
    + Report a bug +
    +
    +

    Please describe the bug. What did you do? + What did you expect to happen? + What actually happened?

    +