From 7df8951efa0a11415237f1bb35a8fa3fcba24a94 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 17 Jan 2019 16:30:55 +0100 Subject: [PATCH 01/74] wait to hide typing bar until event arrives or 5s timeout --- src/components/views/rooms/WhoIsTypingTile.js | 89 +++++++++++++++++-- 1 file changed, 84 insertions(+), 5 deletions(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 9d49c35d83..144057dfd6 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -19,6 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; import WhoIsTyping from '../../../WhoIsTyping'; +import Timer from '../../../utils/Timer'; import MatrixClientPeg from '../../../MatrixClientPeg'; import MemberAvatar from '../avatars/MemberAvatar'; @@ -43,11 +44,13 @@ module.exports = React.createClass({ getInitialState: function() { return { usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room), + userTimers: {}, }; }, componentWillMount: function() { MatrixClientPeg.get().on("RoomMember.typing", this.onRoomMemberTyping); + MatrixClientPeg.get().on("Room.timeline", this.onRoomTimeline); }, componentDidUpdate: function(_, prevState) { @@ -64,18 +67,89 @@ module.exports = React.createClass({ const client = MatrixClientPeg.get(); if (client) { client.removeListener("RoomMember.typing", this.onRoomMemberTyping); + client.removeListener("Room.timeline", this.onRoomTimeline); + } + Object.values(this.state.userTimers).forEach((t) => t.abort()); + }, + + onRoomTimeline: function(event, room) { + if (room.roomId === this.props.room.roomId) { + console.log(`WhoIsTypingTile: incoming timeline event for ${event.getSender()}`); + this._abortUserTimer(event.getSender(), "timeline event"); } }, onRoomMemberTyping: function(ev, member) { + console.log(`WhoIsTypingTile: incoming typing event for`, ev.getContent().user_ids); + const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room); this.setState({ - usersTyping: WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room), + userTimers: this._updateUserTimers(usersTyping), + usersTyping, }); }, - _renderTypingIndicatorAvatars: function(limit) { - let users = this.state.usersTyping; + _updateUserTimers(usersTyping) { + const usersThatStoppedTyping = this.state.usersTyping.filter((a) => { + return !usersTyping.some((b) => a.userId === b.userId); + }); + const usersThatStartedTyping = usersTyping.filter((a) => { + return !this.state.usersTyping.some((b) => a.userId === b.userId); + }); + // abort all the timers for the users that started typing again + usersThatStartedTyping.forEach((m) => { + const timer = this.state.userTimers[m.userId]; + timer && timer.abort(); + }); + // prepare new userTimers object to update state with + let userTimers = Object.assign({}, this.state.userTimers); + // remove members that started typing again + userTimers = usersThatStartedTyping.reduce((userTimers, m) => { + if (userTimers[m.userId]) { + console.log(`WhoIsTypingTile: stopping timer for ${m.userId} because started typing again`); + } + delete userTimers[m.userId]; + return userTimers; + }, userTimers); + // start timer for members that stopped typing + userTimers = usersThatStoppedTyping.reduce((userTimers, m) => { + if (!userTimers[m.userId]) { + console.log(`WhoIsTypingTile: starting 5s timer for ${m.userId}`); + const timer = new Timer(5000); + userTimers[m.userId] = timer; + timer.start(); + timer.finished().then( + () => { + console.log(`WhoIsTypingTile: elapsed 5s timer for ${m.userId}`); + this._removeUserTimer(m.userId); + }, //on elapsed + () => {/* aborted */}, + ); + } + return userTimers; + }, userTimers); + return userTimers; + }, + + _abortUserTimer: function(userId, reason) { + const timer = this.state.userTimers[userId]; + if (timer) { + console.log(`WhoIsTypingTile: aborting timer for ${userId} because ${reason}`); + timer.abort(); + this._removeUserTimer(userId); + } + }, + + _removeUserTimer: function(userId) { + const timer = this.state.userTimers[userId]; + if (timer) { + const userTimers = Object.assign({}, this.state.userTimers); + delete userTimers[userId]; + this.setState({userTimers}); + } + }, + + _renderTypingIndicatorAvatars: function(users, limit) { let othersCount = 0; if (users.length > limit) { othersCount = users.length - limit + 1; @@ -106,8 +180,13 @@ module.exports = React.createClass({ }, render: function() { + let usersTyping = this.state.usersTyping; + const stoppedUsersOnTimer = Object.keys(this.state.userTimers) + .map((userId) => this.props.room.getMember(userId)); + usersTyping = usersTyping.concat(stoppedUsersOnTimer); + const typingString = WhoIsTyping.whoIsTypingString( - this.state.usersTyping, + usersTyping, this.props.whoIsTypingLimit, ); if (!typingString) { @@ -119,7 +198,7 @@ module.exports = React.createClass({ return (
  • - { this._renderTypingIndicatorAvatars(this.props.whoIsTypingLimit) } + { this._renderTypingIndicatorAvatars(usersTyping, this.props.whoIsTypingLimit) }
    { typingString } From d787d3d821d704683b411802dee0462a69c7a8de Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 17 Jan 2019 18:26:11 +0100 Subject: [PATCH 02/74] clear typing bar when receiving event from user --- src/components/views/rooms/WhoIsTypingTile.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 144057dfd6..2097f7e99d 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -74,12 +74,23 @@ module.exports = React.createClass({ onRoomTimeline: function(event, room) { if (room.roomId === this.props.room.roomId) { - console.log(`WhoIsTypingTile: incoming timeline event for ${event.getSender()}`); - this._abortUserTimer(event.getSender(), "timeline event"); + const userId = event.getSender(); + const userWasTyping = this.state.usersTyping.some((m) => m.userId === userId); + if (userWasTyping) { + console.log(`WhoIsTypingTile: remove ${userId} from usersTyping`); + } + const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId); + this.setState({usersTyping}); + + if (this.state.userTimers[userId]) { + console.log(`WhoIsTypingTile: incoming timeline event for ${userId}`); + } + this._abortUserTimer(userId, "timeline event"); } }, onRoomMemberTyping: function(ev, member) { + //TODO: don't we need to check the roomId here? console.log(`WhoIsTypingTile: incoming typing event for`, ev.getContent().user_ids); const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room); this.setState({ From 1db6f1b652494741b0c84e954f22303737cb83a6 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 17 Jan 2019 18:32:46 +0100 Subject: [PATCH 03/74] remove logging --- src/components/views/rooms/WhoIsTypingTile.js | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 2097f7e99d..c704f4fd11 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -75,23 +75,14 @@ module.exports = React.createClass({ onRoomTimeline: function(event, room) { if (room.roomId === this.props.room.roomId) { const userId = event.getSender(); - const userWasTyping = this.state.usersTyping.some((m) => m.userId === userId); - if (userWasTyping) { - console.log(`WhoIsTypingTile: remove ${userId} from usersTyping`); - } + // remove user from usersTyping const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId); this.setState({usersTyping}); - - if (this.state.userTimers[userId]) { - console.log(`WhoIsTypingTile: incoming timeline event for ${userId}`); - } - this._abortUserTimer(userId, "timeline event"); + this._abortUserTimer(userId); } }, onRoomMemberTyping: function(ev, member) { - //TODO: don't we need to check the roomId here? - console.log(`WhoIsTypingTile: incoming typing event for`, ev.getContent().user_ids); const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room); this.setState({ userTimers: this._updateUserTimers(usersTyping), @@ -115,24 +106,17 @@ module.exports = React.createClass({ let userTimers = Object.assign({}, this.state.userTimers); // remove members that started typing again userTimers = usersThatStartedTyping.reduce((userTimers, m) => { - if (userTimers[m.userId]) { - console.log(`WhoIsTypingTile: stopping timer for ${m.userId} because started typing again`); - } delete userTimers[m.userId]; return userTimers; }, userTimers); // start timer for members that stopped typing userTimers = usersThatStoppedTyping.reduce((userTimers, m) => { if (!userTimers[m.userId]) { - console.log(`WhoIsTypingTile: starting 5s timer for ${m.userId}`); const timer = new Timer(5000); userTimers[m.userId] = timer; timer.start(); timer.finished().then( - () => { - console.log(`WhoIsTypingTile: elapsed 5s timer for ${m.userId}`); - this._removeUserTimer(m.userId); - }, //on elapsed + () => this._removeUserTimer(m.userId), //on elapsed () => {/* aborted */}, ); } @@ -142,10 +126,9 @@ module.exports = React.createClass({ return userTimers; }, - _abortUserTimer: function(userId, reason) { + _abortUserTimer: function(userId) { const timer = this.state.userTimers[userId]; if (timer) { - console.log(`WhoIsTypingTile: aborting timer for ${userId} because ${reason}`); timer.abort(); this._removeUserTimer(userId); } From 146e651daec47d2e50450beea0c2026154ac70cb Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 17 Jan 2019 18:32:53 +0100 Subject: [PATCH 04/74] add comments --- src/components/views/rooms/WhoIsTypingTile.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index c704f4fd11..f15e2c8e52 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -45,6 +45,11 @@ module.exports = React.createClass({ return { usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room), userTimers: {}, + // a map with userid => Timer to delay + // hiding the "x is typing" message for a + // user so hiding it can coincide + // with the sent message by the other side + // resulting in less timeline jumpiness }; }, @@ -78,6 +83,7 @@ module.exports = React.createClass({ // remove user from usersTyping const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId); this.setState({usersTyping}); + // abort timer if any this._abortUserTimer(userId); } }, @@ -177,6 +183,9 @@ module.exports = React.createClass({ let usersTyping = this.state.usersTyping; const stoppedUsersOnTimer = Object.keys(this.state.userTimers) .map((userId) => this.props.room.getMember(userId)); + // append the users that have been reported not typing anymore + // but have a timeout timer running so they can disappear + // when a message comes in usersTyping = usersTyping.concat(stoppedUsersOnTimer); const typingString = WhoIsTyping.whoIsTypingString( From 2920deaefe10a102cf431f56c809f51aa3b0e5f9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Thu, 17 Jan 2019 18:52:22 +0100 Subject: [PATCH 05/74] better naming --- src/components/views/rooms/WhoIsTypingTile.js | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index f15e2c8e52..5a2b6afc96 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -44,12 +44,12 @@ module.exports = React.createClass({ getInitialState: function() { return { usersTyping: WhoIsTyping.usersTypingApartFromMe(this.props.room), - userTimers: {}, // a map with userid => Timer to delay // hiding the "x is typing" message for a // user so hiding it can coincide // with the sent message by the other side // resulting in less timeline jumpiness + delayedStopTypingTimers: {}, }; }, @@ -74,7 +74,7 @@ module.exports = React.createClass({ client.removeListener("RoomMember.typing", this.onRoomMemberTyping); client.removeListener("Room.timeline", this.onRoomTimeline); } - Object.values(this.state.userTimers).forEach((t) => t.abort()); + Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort()); }, onRoomTimeline: function(event, room) { @@ -91,12 +91,12 @@ module.exports = React.createClass({ onRoomMemberTyping: function(ev, member) { const usersTyping = WhoIsTyping.usersTypingApartFromMeAndIgnored(this.props.room); this.setState({ - userTimers: this._updateUserTimers(usersTyping), + delayedStopTypingTimers: this._updateDelayedStopTypingTimers(usersTyping), usersTyping, }); }, - _updateUserTimers(usersTyping) { + _updateDelayedStopTypingTimers(usersTyping) { const usersThatStoppedTyping = this.state.usersTyping.filter((a) => { return !usersTyping.some((b) => a.userId === b.userId); }); @@ -105,35 +105,35 @@ module.exports = React.createClass({ }); // abort all the timers for the users that started typing again usersThatStartedTyping.forEach((m) => { - const timer = this.state.userTimers[m.userId]; + const timer = this.state.delayedStopTypingTimers[m.userId]; timer && timer.abort(); }); - // prepare new userTimers object to update state with - let userTimers = Object.assign({}, this.state.userTimers); + // prepare new delayedStopTypingTimers object to update state with + let delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers); // remove members that started typing again - userTimers = usersThatStartedTyping.reduce((userTimers, m) => { - delete userTimers[m.userId]; - return userTimers; - }, userTimers); + delayedStopTypingTimers = usersThatStartedTyping.reduce((delayedStopTypingTimers, m) => { + delete delayedStopTypingTimers[m.userId]; + return delayedStopTypingTimers; + }, delayedStopTypingTimers); // start timer for members that stopped typing - userTimers = usersThatStoppedTyping.reduce((userTimers, m) => { - if (!userTimers[m.userId]) { + delayedStopTypingTimers = usersThatStoppedTyping.reduce((delayedStopTypingTimers, m) => { + if (!delayedStopTypingTimers[m.userId]) { const timer = new Timer(5000); - userTimers[m.userId] = timer; + delayedStopTypingTimers[m.userId] = timer; timer.start(); timer.finished().then( () => this._removeUserTimer(m.userId), //on elapsed () => {/* aborted */}, ); } - return userTimers; - }, userTimers); + return delayedStopTypingTimers; + }, delayedStopTypingTimers); - return userTimers; + return delayedStopTypingTimers; }, _abortUserTimer: function(userId) { - const timer = this.state.userTimers[userId]; + const timer = this.state.delayedStopTypingTimers[userId]; if (timer) { timer.abort(); this._removeUserTimer(userId); @@ -141,11 +141,11 @@ module.exports = React.createClass({ }, _removeUserTimer: function(userId) { - const timer = this.state.userTimers[userId]; + const timer = this.state.delayedStopTypingTimers[userId]; if (timer) { - const userTimers = Object.assign({}, this.state.userTimers); - delete userTimers[userId]; - this.setState({userTimers}); + const delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers); + delete delayedStopTypingTimers[userId]; + this.setState({delayedStopTypingTimers}); } }, @@ -181,7 +181,7 @@ module.exports = React.createClass({ render: function() { let usersTyping = this.state.usersTyping; - const stoppedUsersOnTimer = Object.keys(this.state.userTimers) + const stoppedUsersOnTimer = Object.keys(this.state.delayedStopTypingTimers) .map((userId) => this.props.room.getMember(userId)); // append the users that have been reported not typing anymore // but have a timeout timer running so they can disappear From 9a7a8da6c06b22b0acc9323c19d63ed03f08bbdc Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Jan 2019 14:46:31 -0600 Subject: [PATCH 06/74] Use font-family from theme in all components --- res/css/structures/_UserSettings.scss | 2 +- res/css/views/dialogs/_DevtoolsDialog.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_UserSettings.scss b/res/css/structures/_UserSettings.scss index 6fae8d6c1a..74d8c2c718 100644 --- a/res/css/structures/_UserSettings.scss +++ b/res/css/structures/_UserSettings.scss @@ -173,7 +173,7 @@ limitations under the License. padding: 0px; width: 240px; color: $input-fg-color; - font-family: 'Open Sans', Helvetica, Arial, Sans-Serif; + font-family: $font-family; font-size: 16px; } diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index a4a868bd11..572d6ee8c7 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -61,7 +61,7 @@ limitations under the License. padding: 0; width: 240px; color: $input-fg-color; - font-family: 'Open Sans', Helvetica, Arial, Sans-Serif; + font-family: $font-family; font-size: 16px; } From 7678b9c43b0d0fca601b2b72c3fcea8f0bf42ca1 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Jan 2019 14:59:44 -0600 Subject: [PATCH 07/74] Lift font SCSS partials to theme root This means that themes which include `light/css/_base.scss` (currently Dark and Status) won't be forced to have Light's font-faces included. This only really matters for Status, which uses different fonts throughout. --- res/themes/dark/css/dark.scss | 2 +- res/themes/dharma/css/_dharma.scss | 2 -- res/themes/dharma/css/dharma.scss | 2 +- res/themes/light/css/_base.scss | 2 -- res/themes/light/css/light.scss | 2 +- 5 files changed, 3 insertions(+), 7 deletions(-) diff --git a/res/themes/dark/css/dark.scss b/res/themes/dark/css/dark.scss index b69f096db7..5cc6a0d659 100644 --- a/res/themes/dark/css/dark.scss +++ b/res/themes/dark/css/dark.scss @@ -1,4 +1,4 @@ +@import "../../light/css/_fonts.scss"; @import "../../light/css/_base.scss"; @import "_dark.scss"; @import "../../../../res/css/_components.scss"; - diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index c70d7f020a..2d76bd7da3 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -1,5 +1,3 @@ -@import "_fonts.scss"; - // XXX: check this? /* Nunito lacks combining diacritics, so these will fall through to the next font. Helevetica's diacritics however do not combine diff --git a/res/themes/dharma/css/dharma.scss b/res/themes/dharma/css/dharma.scss index 0f4db55fd2..7e3e69de34 100644 --- a/res/themes/dharma/css/dharma.scss +++ b/res/themes/dharma/css/dharma.scss @@ -1,3 +1,3 @@ +@import "_fonts.scss"; @import "_dharma.scss"; @import "../../../../res/css/_components.scss"; - diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 96c179f6f5..44f9042554 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -1,5 +1,3 @@ -@import "_fonts.scss"; - /* Open Sans lacks combining diacritics, so these will fall through to the next font. Helevetica's diacritics however do not combine nicely with Open Sans (on OSX, at least) and result in a huge diff --git a/res/themes/light/css/light.scss b/res/themes/light/css/light.scss index 2099f41f60..62464316f4 100644 --- a/res/themes/light/css/light.scss +++ b/res/themes/light/css/light.scss @@ -1,3 +1,3 @@ +@import "_fonts.scss"; @import "_base.scss"; @import "../../../../res/css/_components.scss"; - From b2387f4cff314d26b53eb776641dc2b7a3e6b20c Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 31 Dec 2018 17:22:40 -0600 Subject: [PATCH 08/74] Define SCSS variable for resource dir Adds a `$res` SCSS variable set to the path from the root SCSS file to the `res` directory. This is a different base path than previously used in CSS URLs (it goes up 3 directories instead of 2), because Webpack will now be resolving images relative to the root SCSS file, so the path corresponds to a source tree location, instead of a path in the build output tree. Defining this variable has two main goals: * URLs are a bit easier to read * The path can be overridden, which is needed for external (riot-web) themes --- res/css/structures/_MyGroups.scss | 2 +- res/css/structures/_RoomSubList.scss | 4 ++-- res/css/structures/_RoomView.scss | 4 ++-- res/css/structures/_SearchBox.scss | 2 +- res/css/structures/_TagPanel.scss | 4 ++-- .../keybackup/_KeyBackupFailedDialog.scss | 2 +- res/css/views/elements/_DirectorySearchBox.scss | 4 ++-- res/css/views/elements/_ImageView.scss | 2 +- res/css/views/rooms/_EntityTile.scss | 2 +- res/css/views/rooms/_MemberList.scss | 2 +- res/css/views/rooms/_RoomTile.scss | 2 +- res/css/views/rooms/_SearchBar.scss | 4 ++-- res/css/views/rooms/_TopUnreadMessagesBar.scss | 2 +- res/css/views/rooms/_WhoIsTypingTile.scss | 2 +- res/themes/dark/css/_dark.scss | 4 ++-- res/themes/dark/css/dark.scss | 1 + res/themes/dharma/css/_dharma.scss | 6 +++--- res/themes/dharma/css/_fonts.scss | 16 ++++++++-------- res/themes/dharma/css/dharma.scss | 1 + res/themes/light/css/_base.scss | 4 ++-- res/themes/light/css/_fonts.scss | 16 ++++++++-------- res/themes/light/css/_paths.scss | 3 +++ res/themes/light/css/light.scss | 1 + 23 files changed, 48 insertions(+), 42 deletions(-) create mode 100644 res/themes/light/css/_paths.scss diff --git a/res/css/structures/_MyGroups.scss b/res/css/structures/_MyGroups.scss index f9433909a5..b3a5c4f473 100644 --- a/res/css/structures/_MyGroups.scss +++ b/res/css/structures/_MyGroups.scss @@ -54,7 +54,7 @@ limitations under the License. &:before { background-color: $accent-fg-color; - mask: url('../../img/icons-create-room.svg'); + mask: url('$(res)/img/icons-create-room.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 80%; diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index b7fe19ca89..4a60650cef 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -105,7 +105,7 @@ limitations under the License. .mx_RoomSubList_addRoom { background-color: $roomheader-addroom-color; color: $roomsublist-background; - background-image: url('../../img/icons-room-add.svg'); + background-image: url('$(res)/img/icons-room-add.svg'); background-repeat: no-repeat; background-position: center; border-radius: 10px; // 16/2 + 2 padding @@ -120,7 +120,7 @@ limitations under the License. .mx_RoomSubList_chevron { pointer-events: none; - background-image: url('../../img/topleft-chevron.svg'); + background-image: url('$(res)/img/topleft-chevron.svg'); background-repeat: no-repeat; transition: transform 0.2s ease-in; width: 10px; diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 4a84a55cd4..a21d273475 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -116,7 +116,7 @@ limitations under the License. .mx_RoomView_messagePanelSearchSpinner { flex: 1; - background-image: url('../../img/typing-indicator-2x.gif'); + background-image: url('$(res)/img/typing-indicator-2x.gif'); background-position: center 367px; background-size: 25px; background-repeat: no-repeat; @@ -125,7 +125,7 @@ limitations under the License. .mx_RoomView_messagePanelSearchSpinner:before { background-color: $greyed-fg-color; - mask: url('../../img/feather-icons/search-input.svg'); + mask: url('$(res)/img/feather-icons/search-input.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 50px; diff --git a/res/css/structures/_SearchBox.scss b/res/css/structures/_SearchBox.scss index e559236569..9434d93bd2 100644 --- a/res/css/structures/_SearchBox.scss +++ b/res/css/structures/_SearchBox.scss @@ -16,7 +16,7 @@ limitations under the License. .mx_SearchBox_closeButton { cursor: pointer; - background-image: url('../../img/icons-close.svg'); + background-image: url('$(res)/img/icons-close.svg'); background-repeat: no-repeat; width: 16px; height: 16px; diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 77eefc7e10..bf1746bd0e 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -130,12 +130,12 @@ limitations under the License. } .mx_TagPanel_groupsButton > .mx_GroupsButton:before { - mask: url('../../img/feather-icons/users.svg'); + mask: url('$(res)/img/feather-icons/users.svg'); mask-position: center 11px; } .mx_TagPanel_groupsButton > .mx_TagPanel_report:before { - mask: url('../../img/feather-icons/life-buoy.svg'); + mask: url('$(res)/img/feather-icons/life-buoy.svg'); mask-position: center 9px; } diff --git a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss index 4a050b6fc4..bd703badaa 100644 --- a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss +++ b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss @@ -24,7 +24,7 @@ limitations under the License. padding-bottom: 10px; &:before { - mask: url("../../img/e2e/lock-warning.svg"); + mask: url("$(res)/img/e2e/lock-warning.svg"); mask-repeat: no-repeat; background-color: $primary-fg-color; content: ""; diff --git a/res/css/views/elements/_DirectorySearchBox.scss b/res/css/views/elements/_DirectorySearchBox.scss index 94a92b23ce..1a8c2b93de 100644 --- a/res/css/views/elements/_DirectorySearchBox.scss +++ b/res/css/views/elements/_DirectorySearchBox.scss @@ -44,7 +44,7 @@ input[type=text].mx_DirectorySearchBox_input:focus { padding-right: 10px; background-color: $plinth-bg-color; border-radius: 3px; - background-image: url('../../img/icon-return.svg'); + background-image: url('$(res)/img/icon-return.svg'); background-position: 8px 70%; background-repeat: no-repeat; text-indent: 18px; @@ -61,7 +61,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('$(res)/img/icon_context_delete.svg'); background-position: 0 50%; background-repeat: no-repeat; width: 15px; diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 8ed0698a72..9bf23b1025 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -50,7 +50,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('$(res)/img/trans.png'); */ pointer-events: all; } diff --git a/res/css/views/rooms/_EntityTile.scss b/res/css/views/rooms/_EntityTile.scss index c4d4d944a6..80b8126b54 100644 --- a/res/css/views/rooms/_EntityTile.scss +++ b/res/css/views/rooms/_EntityTile.scss @@ -22,7 +22,7 @@ limitations under the License. } .mx_EntityTile:hover { - background-image: url('../../img/member_chevron.png'); + background-image: url('$(res)/img/member_chevron.png'); background-position: center right 10px; background-repeat: no-repeat; padding-right: 30px; diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss index 6f9491b22f..ab3b68b346 100644 --- a/res/css/views/rooms/_MemberList.scss +++ b/res/css/views/rooms/_MemberList.scss @@ -83,7 +83,7 @@ limitations under the License. .mx_MemberList_invite span { margin: 0 auto; - background-image: url('../../img/feather-icons/user-add.svg'); + background-image: url('$(res)/img/feather-icons/user-add.svg'); background-repeat: no-repeat; background-position: center left; padding-left: 25px; diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 56ca715b51..395dcd68d9 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -29,7 +29,7 @@ limitations under the License. display: none; flex: 0 0 16px; height: 16px; - background-image: url('../../img/icon_context.svg'); + background-image: url('$(res)/img/icon_context.svg'); background-repeat: no-repeat; background-position: center; } diff --git a/res/css/views/rooms/_SearchBar.scss b/res/css/views/rooms/_SearchBar.scss index 390d6606c1..b89cb0ce13 100644 --- a/res/css/views/rooms/_SearchBar.scss +++ b/res/css/views/rooms/_SearchBar.scss @@ -32,7 +32,7 @@ limitations under the License. width: 37px; height: 37px; background-color: $accent-color; - mask: url('../../img/feather-icons/search-input.svg'); + mask: url('$(res)/img/feather-icons/search-input.svg'); mask-repeat: no-repeat; mask-position: center; } @@ -55,7 +55,7 @@ limitations under the License. .mx_SearchBar_cancel { background-color: $warning-color; - mask: url('../../img/cancel.svg'); + mask: url('$(res)/img/cancel.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 14px; diff --git a/res/css/views/rooms/_TopUnreadMessagesBar.scss b/res/css/views/rooms/_TopUnreadMessagesBar.scss index 67579552c1..a4b7a6aa51 100644 --- a/res/css/views/rooms/_TopUnreadMessagesBar.scss +++ b/res/css/views/rooms/_TopUnreadMessagesBar.scss @@ -54,7 +54,7 @@ limitations under the License. position: absolute; width: 38px; height: 38px; - mask: url('../../img/icon-jump-to-first-unread.svg'); + mask: url('$(res)/img/icon-jump-to-first-unread.svg'); mask-repeat: no-repeat; mask-position: 9px 13px; background: $roomtile-name-color; diff --git a/res/css/views/rooms/_WhoIsTypingTile.scss b/res/css/views/rooms/_WhoIsTypingTile.scss index 217a10be8d..eb51595858 100644 --- a/res/css/views/rooms/_WhoIsTypingTile.scss +++ b/res/css/views/rooms/_WhoIsTypingTile.scss @@ -61,7 +61,7 @@ limitations under the License. } .mx_WhoIsTypingTile_label > span { - background-image: url('../../img/typing-indicator-2x.gif'); + background-image: url('$(res)/img/typing-indicator-2x.gif'); background-size: 25px; background-position: left bottom; background-repeat: no-repeat; diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index 997a74e6aa..0afa6a91e1 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -149,8 +149,8 @@ $event-redacted-border-color: #000000; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "../../img/icon_context_message_dark.svg"; -$copy-button-url: "../../img/icon_copy_message_dark.svg"; +$edit-button-url: "$(res)/img/icon_context_message_dark.svg"; +$copy-button-url: "$(res)/img/icon_copy_message_dark.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/res/themes/dark/css/dark.scss b/res/themes/dark/css/dark.scss index 5cc6a0d659..a7d18fa1a6 100644 --- a/res/themes/dark/css/dark.scss +++ b/res/themes/dark/css/dark.scss @@ -1,3 +1,4 @@ +@import "../../light/css/_paths.scss"; @import "../../light/css/_fonts.scss"; @import "../../light/css/_base.scss"; @import "_dark.scss"; diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 2d76bd7da3..7de20c1ed4 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -169,8 +169,8 @@ $event-redacted-border-color: #cccccc; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "../../img/icon_context_message.svg"; -$copy-button-url: "../../img/icon_copy_message.svg"; +$edit-button-url: "$(res)/img/icon_context_message.svg"; +$copy-button-url: "$(res)/img/icon_copy_message.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color @@ -273,7 +273,7 @@ input[type=search].mx_textinput_icon { // FIXME THEME - Tint by CSS rather than referencing a duplicate asset input[type=text].mx_textinput_icon.mx_textinput_search, input[type=search].mx_textinput_icon.mx_textinput_search { - background-image: url('../../img/feather-icons/search-input.svg'); + background-image: url('$(res)/img/feather-icons/search-input.svg'); } // dont search UI as not all browsers support it, diff --git a/res/themes/dharma/css/_fonts.scss b/res/themes/dharma/css/_fonts.scss index bb45432262..93fa9afbfa 100644 --- a/res/themes/dharma/css/_fonts.scss +++ b/res/themes/dharma/css/_fonts.scss @@ -11,37 +11,37 @@ font-family: 'Nunito'; font-style: italic; font-weight: 400; - src: url('../../fonts/Nunito/XRXX3I6Li01BKofIMNaDRss.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXX3I6Li01BKofIMNaDRss.ttf') format('truetype'); } @font-face { font-family: 'Nunito'; font-style: italic; font-weight: 600; - src: url('../../fonts/Nunito/XRXQ3I6Li01BKofIMN5cYtvKUTo.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXQ3I6Li01BKofIMN5cYtvKUTo.ttf') format('truetype'); } @font-face { font-family: 'Nunito'; font-style: italic; font-weight: 700; - src: url('../../fonts/Nunito/XRXQ3I6Li01BKofIMN44Y9vKUTo.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXQ3I6Li01BKofIMN44Y9vKUTo.ttf') format('truetype'); } @font-face { font-family: 'Nunito'; font-style: normal; font-weight: 400; - src: url('../../fonts/Nunito/XRXV3I6Li01BKofINeaE.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXV3I6Li01BKofINeaE.ttf') format('truetype'); } @font-face { font-family: 'Nunito'; font-style: normal; font-weight: 600; - src: url('../../fonts/Nunito/XRXW3I6Li01BKofA6sKUYevN.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXW3I6Li01BKofA6sKUYevN.ttf') format('truetype'); } @font-face { font-family: 'Nunito'; font-style: normal; font-weight: 700; - src: url('../../fonts/Nunito/XRXW3I6Li01BKofAjsOUYevN.ttf') format('truetype'); + src: url('$(res)/fonts/Nunito/XRXW3I6Li01BKofAjsOUYevN.ttf') format('truetype'); } /* @@ -51,14 +51,14 @@ @font-face { font-family: 'Fira Mono'; - src: url('../../fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); + src: url('$(res)/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('$(res)/fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } diff --git a/res/themes/dharma/css/dharma.scss b/res/themes/dharma/css/dharma.scss index 7e3e69de34..24dc0ce18d 100644 --- a/res/themes/dharma/css/dharma.scss +++ b/res/themes/dharma/css/dharma.scss @@ -1,3 +1,4 @@ +@import "../../light/css/_paths.scss"; @import "_fonts.scss"; @import "_dharma.scss"; @import "../../../../res/css/_components.scss"; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 44f9042554..4b3501cbaf 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -160,8 +160,8 @@ $event-redacted-border-color: #cccccc; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "../../img/icon_context_message.svg"; -$copy-button-url: "../../img/icon_copy_message.svg"; +$edit-button-url: "$(res)/img/icon_context_message.svg"; +$copy-button-url: "$(res)/img/icon_copy_message.svg"; // e2e $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color diff --git a/res/themes/light/css/_fonts.scss b/res/themes/light/css/_fonts.scss index 52ac95b569..c080663acf 100644 --- a/res/themes/light/css/_fonts.scss +++ b/res/themes/light/css/_fonts.scss @@ -7,42 +7,42 @@ */ @font-face { font-family: 'Open Sans'; - src: url('../../fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); + src: url('$(res)/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('$(res)/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('$(res)/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('$(res)/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('$(res)/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('$(res)/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('$(res)/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('$(res)/fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); font-weight: 700; font-style: normal; } diff --git a/res/themes/light/css/_paths.scss b/res/themes/light/css/_paths.scss new file mode 100644 index 0000000000..0744347826 --- /dev/null +++ b/res/themes/light/css/_paths.scss @@ -0,0 +1,3 @@ +// Path from root SCSS file (such as `light.scss`) to `res` dir in the source tree +// This value is overridden by external themes in `riot-web`. +$res: ../../..; diff --git a/res/themes/light/css/light.scss b/res/themes/light/css/light.scss index 62464316f4..6ac8d0feba 100644 --- a/res/themes/light/css/light.scss +++ b/res/themes/light/css/light.scss @@ -1,3 +1,4 @@ +@import "_paths.scss"; @import "_fonts.scss"; @import "_base.scss"; @import "../../../../res/css/_components.scss"; From 989227d435eced84a8355629b3c20d0c1ae29a09 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 10 Jan 2019 19:18:21 -0600 Subject: [PATCH 09/74] Fix line length lint warnings I am about to run some code replacements that might make lines too long, so with this change I know we're starting from 0 line length warnings. --- package.json | 2 +- src/components/structures/IndicatorScrollbar.js | 6 +++++- src/components/structures/MatrixChat.js | 11 +++++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7b55a09948..15d1eff836 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "start:init": "babel src -d lib --source-maps --copy-files", "lint": "eslint src/", "lintall": "eslint src/ test/", - "lintwithexclusions": "eslint --max-warnings 18 --ignore-path .eslintignore.errorfiles src test", + "lintwithexclusions": "eslint --max-warnings 13 --ignore-path .eslintignore.errorfiles src test", "clean": "rimraf lib", "prepublish": "npm run clean && npm run build && git rev-parse HEAD > git-revision.txt", "test": "karma start --single-run=true --browsers ChromeHeadless", diff --git a/src/components/structures/IndicatorScrollbar.js b/src/components/structures/IndicatorScrollbar.js index 0fe246050c..c3e54ee900 100644 --- a/src/components/structures/IndicatorScrollbar.js +++ b/src/components/structures/IndicatorScrollbar.js @@ -66,7 +66,11 @@ export default class IndicatorScrollbar extends React.Component { } render() { - return ( + return ( { this.props.children } ); } diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 06ec2b5513..e5cf6a986a 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -302,7 +302,10 @@ export default React.createClass({ // will check their settings. this.setState({ defaultServerName: null, // To un-hide any secrets people might be keeping - defaultServerDiscoveryError: _t("Invalid configuration: Cannot supply a default homeserver URL and a default server name"), + defaultServerDiscoveryError: _t( + "Invalid configuration: Cannot supply a default homeserver URL and " + + "a default server name", + ), }); } @@ -1833,7 +1836,11 @@ export default React.createClass({ render: function() { // console.log(`Rendering MatrixChat with view ${this.state.view}`); - if (this.state.view === VIEWS.LOADING || this.state.view === VIEWS.LOGGING_IN || this.state.loadingDefaultHomeserver) { + if ( + this.state.view === VIEWS.LOADING || + this.state.view === VIEWS.LOGGING_IN || + this.state.loadingDefaultHomeserver + ) { const Spinner = sdk.getComponent('elements.Spinner'); return (
    From 20e296b20e4bf27f78ff57f37330a43303645175 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 10 Jan 2019 19:37:28 -0600 Subject: [PATCH 10/74] Convert image URLs in React to `require` calls This allows Webpack to insert the proper image URL after builds steps like adding a hash and so on. The path you supply to `require` is relative to the JS source file, just like any other would be. --- res/css/views/rooms/_EventTile.scss | 4 +-- src/Avatar.js | 2 +- src/components/structures/GroupView.js | 18 +++++------ src/components/structures/HomePage.js | 2 +- src/components/structures/MyGroups.js | 4 +-- src/components/structures/RoomDirectory.js | 2 +- src/components/structures/RoomStatusBar.js | 10 +++--- src/components/structures/RoomView.js | 8 ++--- src/components/structures/TagPanel.js | 2 +- .../structures/TopLeftMenuButton.js | 2 +- src/components/structures/UploadBar.js | 4 +-- src/components/structures/UserSettings.js | 8 ++--- .../GroupInviteTileContextMenu.js | 2 +- .../context_menus/RoomTileContextMenu.js | 32 +++++++++---------- .../views/context_menus/TagTileContextMenu.js | 4 +-- src/components/views/dialogs/BaseDialog.js | 2 +- .../views/dialogs/ChatCreateOrReuseDialog.js | 2 +- src/components/views/dialogs/ShareDialog.js | 12 +++---- .../views/directory/NetworkDropdown.js | 4 +-- .../views/elements/AddressSelector.js | 2 +- src/components/views/elements/AddressTile.js | 4 +-- .../views/elements/AppPermission.js | 2 +- src/components/views/elements/AppTile.js | 16 ++++++---- src/components/views/elements/AppWarning.js | 2 +- .../views/elements/CreateRoomButton.js | 2 +- .../views/elements/EditableItemList.js | 4 +-- src/components/views/elements/HomeButton.js | 2 +- src/components/views/elements/ImageView.js | 2 +- .../views/elements/InlineSpinner.js | 2 +- .../views/elements/ManageIntegsButton.js | 8 +++-- .../views/elements/MessageSpinner.js | 2 +- .../views/elements/RoomDirectoryButton.js | 2 +- .../views/elements/SettingsButton.js | 2 +- src/components/views/elements/Spinner.js | 2 +- .../views/elements/StartChatButton.js | 2 +- src/components/views/globals/CookieBar.js | 4 +-- src/components/views/globals/MatrixToolbar.js | 4 +-- src/components/views/globals/NewVersionBar.js | 2 +- .../views/globals/PasswordNagBar.js | 2 +- .../views/globals/UpdateCheckBar.js | 6 ++-- .../views/groups/GroupMemberInfo.js | 2 +- .../views/groups/GroupMemberList.js | 4 +-- src/components/views/groups/GroupRoomInfo.js | 2 +- src/components/views/groups/GroupRoomList.js | 4 +-- src/components/views/login/CountryDropdown.js | 2 +- src/components/views/messages/MAudioBody.js | 4 +-- src/components/views/messages/MFileBody.js | 10 +++--- src/components/views/messages/MImageBody.js | 9 ++++-- src/components/views/messages/MStickerBody.js | 2 +- src/components/views/messages/MVideoBody.js | 4 +-- src/components/views/messages/RoomCreate.js | 2 +- .../views/right_panel/GroupHeaderButtons.js | 4 +-- .../views/right_panel/RoomHeaderButtons.js | 6 ++-- .../views/room_settings/ColorSettings.js | 2 +- src/components/views/rooms/AuxPanel.js | 2 +- src/components/views/rooms/EntityTile.js | 6 ++-- src/components/views/rooms/EventTile.js | 20 ++++++++---- .../views/rooms/LinkPreviewWidget.js | 2 +- .../views/rooms/MemberDeviceInfo.js | 6 ++-- src/components/views/rooms/MemberInfo.js | 4 +-- src/components/views/rooms/MemberList.js | 2 +- src/components/views/rooms/MessageComposer.js | 26 +++++++-------- .../views/rooms/MessageComposerInput.js | 2 +- src/components/views/rooms/PinnedEventTile.js | 2 +- .../views/rooms/PinnedEventsPanel.js | 2 +- src/components/views/rooms/ReplyPreview.js | 2 +- src/components/views/rooms/RoomHeader.js | 16 +++++----- src/components/views/rooms/RoomPreviewBar.js | 2 +- src/components/views/rooms/RoomSettings.js | 6 ++-- src/components/views/rooms/RoomTile.js | 8 ++++- .../views/rooms/SearchableEntityList.js | 2 +- .../views/rooms/SimpleRoomHeader.js | 2 +- src/components/views/rooms/Stickerpicker.js | 6 ++-- .../views/settings/AddPhoneNumber.js | 2 +- src/components/views/voip/IncomingCallBox.js | 2 +- 75 files changed, 201 insertions(+), 176 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 7fb6812c79..ee9971887e 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -291,8 +291,8 @@ limitations under the License. } /* always override hidden attribute for blocked and warning */ -.mx_EventTile_e2eIcon_hidden[src="img/e2e-blocked.svg"], -.mx_EventTile_e2eIcon_hidden[src="img/e2e-warning.svg"] { +.mx_EventTile_e2eIcon_hidden[src*="img/e2e-blocked.svg"], +.mx_EventTile_e2eIcon_hidden[src*="img/e2e-warning.svg"] { display: block; } diff --git a/src/Avatar.js b/src/Avatar.js index d41a3f6a79..d3df12eb49 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -56,6 +56,6 @@ module.exports = { for (let i = 0; i < s.length; ++i) { total += s.charCodeAt(i); } - return 'img/' + images[total % images.length] + '.png'; + return require('../res/img/' + images[total % images.length] + '.png'); }, }; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index 834fcd2340..8e0e5a3d32 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -125,7 +125,7 @@ const CategoryRoomList = React.createClass({ ( - +
    { _t('Add a Room') }
    @@ -226,7 +226,7 @@ const FeaturedRoom = React.createClass({ const deleteButton = this.props.editing ? Delete - +
    { _t('Add a User') }
    @@ -379,7 +379,7 @@ const FeaturedUser = React.createClass({ const deleteButton = this.props.editing ? Delete
    - +
    { _t('Add rooms to this community') } @@ -1189,7 +1189,7 @@ export default React.createClass({
    @@ -1255,7 +1255,7 @@ export default React.createClass({ ); rightButtons.push( - {_t("Cancel")} , ); @@ -1265,13 +1265,13 @@ export default React.createClass({ - + , ); } rightButtons.push( - + , ); } diff --git a/src/components/structures/HomePage.js b/src/components/structures/HomePage.js index 8f0c270513..aa17e63d73 100644 --- a/src/components/structures/HomePage.js +++ b/src/components/structures/HomePage.js @@ -108,7 +108,7 @@ class HomePage extends React.Component { if (this.context.matrixClient.isGuest()) { guestWarning = (
    - +
    { _t("You are currently using Riot anonymously as a guest.") } diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index c5bf74d561..7411c7e6c1 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -107,7 +107,7 @@ export default withMatrixClient(React.createClass({ } return
    - +
    @@ -124,7 +124,7 @@ export default withMatrixClient(React.createClass({
    {/*
    - +
    diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 762185146c..1ddfa92305 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -571,7 +571,7 @@ module.exports = React.createClass({ const DirectorySearchBox = sdk.getComponent('elements.DirectorySearchBox'); return (
    - +
    -
    ); @@ -209,7 +209,7 @@ module.exports = React.createClass({ return ( - {_t("Scroll @@ -219,7 +219,7 @@ module.exports = React.createClass({ if (this.props.hasActiveCall) { const TintableSvg = sdk.getComponent("elements.TintableSvg"); return ( - + ); } @@ -327,7 +327,7 @@ module.exports = React.createClass({ } return
    - +
    { title } @@ -346,7 +346,7 @@ module.exports = React.createClass({ if (this._shouldShowConnectionError()) { return (
    - /!\ + /!\
    { _t('Connectivity to the server has been lost.') } diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index e93d58a3dc..c34b618645 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1777,20 +1777,20 @@ module.exports = React.createClass({ if (call.type === "video") { zoomButton = (
    - +
    ); videoMuteButton =
    -
    ; } voiceMuteButton =
    -
    ; @@ -1802,7 +1802,7 @@ module.exports = React.createClass({ { videoMuteButton } { zoomButton } { statusBar } - +
    ; } diff --git a/src/components/structures/TagPanel.js b/src/components/structures/TagPanel.js index 7e77a64e62..af463efaf9 100644 --- a/src/components/structures/TagPanel.js +++ b/src/components/structures/TagPanel.js @@ -136,7 +136,7 @@ const TagPanel = React.createClass({ let clearButton; if (itemsSelected) { clearButton = - diff --git a/src/components/structures/TopLeftMenuButton.js b/src/components/structures/TopLeftMenuButton.js index 00e669acdd..592b64634f 100644 --- a/src/components/structures/TopLeftMenuButton.js +++ b/src/components/structures/TopLeftMenuButton.js @@ -89,7 +89,7 @@ export default class TopLeftMenuButton extends React.Component { resizeMethod="crop" /> { nameElement } - + ); } diff --git a/src/components/structures/UploadBar.js b/src/components/structures/UploadBar.js index fed4ff33b3..b54ea00c16 100644 --- a/src/components/structures/UploadBar.js +++ b/src/components/structures/UploadBar.js @@ -91,8 +91,8 @@ module.exports = React.createClass({displayName: 'UploadBar',
    - - +
    diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index d76e1c1716..65e1897137 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -1227,7 +1227,7 @@ module.exports = React.createClass({ />
    -
    @@ -1252,7 +1252,7 @@ module.exports = React.createClass({ onValueChanged={this._onAddEmailEditFinished} />
    - +
    ); @@ -1322,7 +1322,7 @@ module.exports = React.createClass({
    - {_t("Remove
    diff --git a/src/components/views/context_menus/GroupInviteTileContextMenu.js b/src/components/views/context_menus/GroupInviteTileContextMenu.js index e30acca16d..4a8a33614b 100644 --- a/src/components/views/context_menus/GroupInviteTileContextMenu.js +++ b/src/components/views/context_menus/GroupInviteTileContextMenu.js @@ -79,7 +79,7 @@ export default class GroupInviteTileContextMenu extends React.Component { render() { return
    - + { _t('Reject') }
    ; diff --git a/src/components/views/context_menus/RoomTileContextMenu.js b/src/components/views/context_menus/RoomTileContextMenu.js index 8e56c055f9..521282488e 100644 --- a/src/components/views/context_menus/RoomTileContextMenu.js +++ b/src/components/views/context_menus/RoomTileContextMenu.js @@ -245,26 +245,26 @@ module.exports = React.createClass({ return (
    - +
    - - + + { _t('All messages (noisy)') }
    - - + + { _t('All messages') }
    - - + + { _t('Mentions only') }
    - - + + { _t('Mute') }
    @@ -298,7 +298,7 @@ module.exports = React.createClass({ return (
    - + { leaveText }
    @@ -327,18 +327,18 @@ module.exports = React.createClass({ return (
    - - + + { _t('Favourite') }
    - - + + { _t('Low Priority') }
    - - + + { _t('Direct Chat') }
    diff --git a/src/components/views/context_menus/TagTileContextMenu.js b/src/components/views/context_menus/TagTileContextMenu.js index 32f5365b82..8b868e7b11 100644 --- a/src/components/views/context_menus/TagTileContextMenu.js +++ b/src/components/views/context_menus/TagTileContextMenu.js @@ -59,7 +59,7 @@ export default class TagTileContextMenu extends React.Component {
    @@ -67,7 +67,7 @@ export default class TagTileContextMenu extends React.Component {

    - + { _t('Remove') }
    ; diff --git a/src/components/views/dialogs/BaseDialog.js b/src/components/views/dialogs/BaseDialog.js index 3e9052cc34..82ea649db4 100644 --- a/src/components/views/dialogs/BaseDialog.js +++ b/src/components/views/dialogs/BaseDialog.js @@ -111,7 +111,7 @@ export default React.createClass({ let cancelButton; if (this.props.hasCancel) { cancelButton = - + ; } diff --git a/src/components/views/dialogs/ChatCreateOrReuseDialog.js b/src/components/views/dialogs/ChatCreateOrReuseDialog.js index 19b7d83717..3171c2db39 100644 --- a/src/components/views/dialogs/ChatCreateOrReuseDialog.js +++ b/src/components/views/dialogs/ChatCreateOrReuseDialog.js @@ -127,7 +127,7 @@ export default class ChatCreateOrReuseDialog extends React.Component { onClick={this.props.onNewDMClick} >
    - +
    { _t("Start new chat") }
    ; diff --git a/src/components/views/dialogs/ShareDialog.js b/src/components/views/dialogs/ShareDialog.js index f074d9b1fa..3a0b00adf2 100644 --- a/src/components/views/dialogs/ShareDialog.js +++ b/src/components/views/dialogs/ShareDialog.js @@ -26,11 +26,11 @@ import * as ContextualMenu from "../../structures/ContextualMenu"; const socials = [ { name: 'Facebook', - img: 'img/social/facebook.png', + img: require("../../../../res/img/social/facebook.png"), url: (url) => `https://www.facebook.com/sharer/sharer.php?u=${url}`, }, { name: 'Twitter', - img: 'img/social/twitter-2.png', + img: require("../../../../res/img/social/twitter-2.png"), url: (url) => `https://twitter.com/home?status=${url}`, }, /* // icon missing name: 'Google Plus', @@ -38,15 +38,15 @@ const socials = [ url: (url) => `https://plus.google.com/share?url=${url}`, },*/ { name: 'LinkedIn', - img: 'img/social/linkedin.png', + img: require("../../../../res/img/social/linkedin.png"), url: (url) => `https://www.linkedin.com/shareArticle?mini=true&url=${url}`, }, { name: 'Reddit', - img: 'img/social/reddit.png', + img: require("../../../../res/img/social/reddit.png"), url: (url) => `http://www.reddit.com/submit?url=${url}`, }, { name: 'email', - img: 'img/social/email-1.png', + img: require("../../../../res/img/social/email-1.png"), url: (url) => `mailto:?body=${url}`, }, ]; @@ -202,7 +202,7 @@ export default class ShareDialog extends React.Component {
    - +
    { diff --git a/src/components/views/directory/NetworkDropdown.js b/src/components/views/directory/NetworkDropdown.js index ad51f501fb..053849863c 100644 --- a/src/components/views/directory/NetworkDropdown.js +++ b/src/components/views/directory/NetworkDropdown.js @@ -18,7 +18,7 @@ import React from 'react'; import MatrixClientPeg from '../../../MatrixClientPeg'; import {instanceForInstanceId} from '../../../utils/DirectoryUtils'; -const DEFAULT_ICON_URL = "img/network-matrix.svg"; +const DEFAULT_ICON_URL = require("../../../../res/img/network-matrix.svg"); export default class NetworkDropdown extends React.Component { constructor(props) { @@ -191,7 +191,7 @@ export default class NetworkDropdown extends React.Component { } else if (!instance) { key = server + '_all'; name = 'Matrix'; - icon = ; + icon = ; span_class = 'mx_NetworkDropdown_menu_network'; } else { key = server + '_inst_' + instance.instance_id; diff --git a/src/components/views/elements/AddressSelector.js b/src/components/views/elements/AddressSelector.js index b4279c7f70..23e6939a24 100644 --- a/src/components/views/elements/AddressSelector.js +++ b/src/components/views/elements/AddressSelector.js @@ -150,7 +150,7 @@ export default React.createClass({ showAddress={this.props.showAddress} justified={true} networkName="vector" - networkUrl="img/search-icon-vector.svg" + networkUrl={require("../../../../res/img/search-icon-vector.svg")} />
    , ); diff --git a/src/components/views/elements/AddressTile.js b/src/components/views/elements/AddressTile.js index 16e340756a..8011a6c55f 100644 --- a/src/components/views/elements/AddressTile.js +++ b/src/components/views/elements/AddressTile.js @@ -54,7 +54,7 @@ export default React.createClass({ address.avatarMxc, 25, 25, 'crop', )); } else if (address.addressType === 'email') { - imgUrls.push('img/icon-email-user.svg'); + imgUrls.push(require("../../../../res/img/icon-email-user.svg")); } // Removing networks for now as they're not really supported @@ -141,7 +141,7 @@ export default React.createClass({ if (this.props.canDismiss) { dismiss = (
    - +
    ); } diff --git a/src/components/views/elements/AppPermission.js b/src/components/views/elements/AppPermission.js index 6b4536b620..8f4a434df0 100644 --- a/src/components/views/elements/AppPermission.js +++ b/src/components/views/elements/AppPermission.js @@ -47,7 +47,7 @@ export default class AppPermission extends React.Component { return (
    - {_t('Warning!')} + {_t('Warning!')}
    { _t('Do you want to load widget from URL:') } { this.state.curlBase } diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js index f4f929a3c2..85d28baee7 100644 --- a/src/components/views/elements/AppTile.js +++ b/src/components/views/elements/AppTile.js @@ -582,19 +582,21 @@ export default class AppTile extends React.Component { // editing is done in scalar const showEditButton = Boolean(this._scalarClient && this._canUserModify()); const deleteWidgetLabel = this._deleteWidgetLabel(); - let deleteIcon = 'img/cancel_green.svg'; + let deleteIcon = require("../../../../res/img/cancel_green.svg"); let deleteClasses = 'mx_AppTileMenuBarWidget'; if (this._canUserModify()) { - deleteIcon = 'img/icon-delete-pink.svg'; + deleteIcon = require("../../../../res/img/icon-delete-pink.svg"); deleteClasses += ' mx_AppTileMenuBarWidgetDelete'; } // Picture snapshot - only show button when apps are maximised. const showPictureSnapshotButton = this._hasCapability('m.capability.screenshot') && this.props.show; - const showPictureSnapshotIcon = 'img/camera_green.svg'; - const popoutWidgetIcon = 'img/button-new-window.svg'; - const reloadWidgetIcon = 'img/button-refresh.svg'; - const windowStateIcon = (this.props.show ? 'img/minimize.svg' : 'img/maximize.svg'); + const showPictureSnapshotIcon = require("../../../../res/img/camera_green.svg"); + const popoutWidgetIcon = require("../../../../res/img/button-new-window.svg"); + const reloadWidgetIcon = require("../../../../res/img/button-refresh.svg"); + const minimizeIcon = require("../../../../res/img/minimize.svg"); + const maximizeIcon = require("../../../../res/img/maximize.svg"); + const windowStateIcon = (this.props.show ? minimizeIcon : maximizeIcon); let appTileClass; if (this.props.miniMode) { @@ -653,7 +655,7 @@ export default class AppTile extends React.Component { { /* Edit widget */ } { showEditButton && { return (
    - +
    { props.errorMsg } diff --git a/src/components/views/elements/CreateRoomButton.js b/src/components/views/elements/CreateRoomButton.js index 177d033c75..da937be3e1 100644 --- a/src/components/views/elements/CreateRoomButton.js +++ b/src/components/views/elements/CreateRoomButton.js @@ -25,7 +25,7 @@ const CreateRoomButton = function(props) { diff --git a/src/components/views/elements/EditableItemList.js b/src/components/views/elements/EditableItemList.js index 02fdc96a78..f4c016d9f2 100644 --- a/src/components/views/elements/EditableItemList.js +++ b/src/components/views/elements/EditableItemList.js @@ -62,13 +62,13 @@ const EditableItem = React.createClass({ { this.props.onAdd ?
    {_t("Add")}
    :
    {_t("Delete")}
    } diff --git a/src/components/views/elements/HomeButton.js b/src/components/views/elements/HomeButton.js index 05e21487eb..ecfce9b5f2 100644 --- a/src/components/views/elements/HomeButton.js +++ b/src/components/views/elements/HomeButton.js @@ -24,7 +24,7 @@ const HomeButton = function(props) { return ( diff --git a/src/components/views/elements/ImageView.js b/src/components/views/elements/ImageView.js index 4ac5eda170..2c0f4a0d86 100644 --- a/src/components/views/elements/ImageView.js +++ b/src/components/views/elements/ImageView.js @@ -176,7 +176,7 @@ module.exports = React.createClass({
    - { + {
    diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js index cce4705512..f82f309493 100644 --- a/src/components/views/elements/InlineSpinner.js +++ b/src/components/views/elements/InlineSpinner.js @@ -26,7 +26,7 @@ module.exports = React.createClass({ return (
    - +
    ); }, diff --git a/src/components/views/elements/ManageIntegsButton.js b/src/components/views/elements/ManageIntegsButton.js index f45053de44..40d4cf9377 100644 --- a/src/components/views/elements/ManageIntegsButton.js +++ b/src/components/views/elements/ManageIntegsButton.js @@ -80,7 +80,11 @@ export default class ManageIntegsButton extends React.Component { }); if (this.state.scalarError && !this.scalarClient.hasCredentials()) { - integrationsWarningTriangle = ; + integrationsWarningTriangle = ; // Popup shown when hovering over integrationsButton_error (via CSS) integrationsErrorPopup = ( @@ -91,7 +95,7 @@ export default class ManageIntegsButton extends React.Component { integrationsButton = ( - + { integrationsWarningTriangle } { integrationsErrorPopup } diff --git a/src/components/views/elements/MessageSpinner.js b/src/components/views/elements/MessageSpinner.js index 500c919d45..19d804f511 100644 --- a/src/components/views/elements/MessageSpinner.js +++ b/src/components/views/elements/MessageSpinner.js @@ -27,7 +27,7 @@ module.exports = React.createClass({ return (
    { msg }
      - +
    ); }, diff --git a/src/components/views/elements/RoomDirectoryButton.js b/src/components/views/elements/RoomDirectoryButton.js index d8f88034e3..1498157ee4 100644 --- a/src/components/views/elements/RoomDirectoryButton.js +++ b/src/components/views/elements/RoomDirectoryButton.js @@ -25,7 +25,7 @@ const RoomDirectoryButton = function(props) { diff --git a/src/components/views/elements/SettingsButton.js b/src/components/views/elements/SettingsButton.js index 215d757e6c..f63d17d93c 100644 --- a/src/components/views/elements/SettingsButton.js +++ b/src/components/views/elements/SettingsButton.js @@ -24,7 +24,7 @@ const SettingsButton = function(props) { return ( diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js index 67fde4bf66..ca1ee0ef42 100644 --- a/src/components/views/elements/Spinner.js +++ b/src/components/views/elements/Spinner.js @@ -27,7 +27,7 @@ module.exports = React.createClass({ const imgClass = this.props.imgClassName || ""; return (
    - +
    ); }, diff --git a/src/components/views/elements/StartChatButton.js b/src/components/views/elements/StartChatButton.js index 199c5e44a6..2132d63940 100644 --- a/src/components/views/elements/StartChatButton.js +++ b/src/components/views/elements/StartChatButton.js @@ -25,7 +25,7 @@ const StartChatButton = function(props) { diff --git a/src/components/views/globals/CookieBar.js b/src/components/views/globals/CookieBar.js index deb1cbffa8..f2bcd647d7 100644 --- a/src/components/views/globals/CookieBar.js +++ b/src/components/views/globals/CookieBar.js @@ -51,7 +51,7 @@ export default class CookieBar extends React.Component { const toolbarClasses = "mx_MatrixToolbar"; return (
    - +
    { this.props.policyUrl ? _t( "Please help improve Riot.im by sending anonymous usage data. " + @@ -95,7 +95,7 @@ export default class CookieBar extends React.Component { { _t("Yes, I want to help!") } - {_t('Close')} + {_t('Close')}
    ); diff --git a/src/components/views/globals/MatrixToolbar.js b/src/components/views/globals/MatrixToolbar.js index 400d9e0c83..efcbfcba48 100644 --- a/src/components/views/globals/MatrixToolbar.js +++ b/src/components/views/globals/MatrixToolbar.js @@ -35,11 +35,11 @@ module.exports = React.createClass({ render: function() { return (
    - +
    { _t('You are not receiving desktop notifications') } { _t('Enable them now') }
    - {_t('Close')} + {_t('Close')}
    ); }, diff --git a/src/components/views/globals/NewVersionBar.js b/src/components/views/globals/NewVersionBar.js index 47eed4dc59..d802af63d4 100644 --- a/src/components/views/globals/NewVersionBar.js +++ b/src/components/views/globals/NewVersionBar.js @@ -96,7 +96,7 @@ export default React.createClass({ } return (
    - +
    {_t("A new version of Riot is available.")}
    diff --git a/src/components/views/globals/PasswordNagBar.js b/src/components/views/globals/PasswordNagBar.js index 5e3da3ad6d..71901ad922 100644 --- a/src/components/views/globals/PasswordNagBar.js +++ b/src/components/views/globals/PasswordNagBar.js @@ -31,7 +31,7 @@ export default React.createClass({ return (
    ; + image = ; } else { - image = ; + image = ; } return ( @@ -83,7 +83,7 @@ export default React.createClass({ {message}
    - {_t('Close')} + {_t('Close')}
    ); diff --git a/src/components/views/groups/GroupMemberInfo.js b/src/components/views/groups/GroupMemberInfo.js index ca59075912..aa40f1c8b3 100644 --- a/src/components/views/groups/GroupMemberInfo.js +++ b/src/components/views/groups/GroupMemberInfo.js @@ -188,7 +188,7 @@ module.exports = React.createClass({
    - +
    { avatar } diff --git a/src/components/views/groups/GroupMemberList.js b/src/components/views/groups/GroupMemberList.js index 9a8196f12b..9045c92a2e 100644 --- a/src/components/views/groups/GroupMemberList.js +++ b/src/components/views/groups/GroupMemberList.js @@ -87,7 +87,7 @@ export default React.createClass({ const text = _t("and %(count)s others...", { count: overflowCount }); return ( + } name={text} presenceState="online" suppressOnHover={true} onClick={this._showFullMemberList} /> ); @@ -214,7 +214,7 @@ export default React.createClass({ onClick={this.onInviteToGroupButtonClick} >
    - +
    { _t('Invite to this community') }
    ); diff --git a/src/components/views/groups/GroupRoomInfo.js b/src/components/views/groups/GroupRoomInfo.js index 41e5f68736..05c6b9cfd4 100644 --- a/src/components/views/groups/GroupRoomInfo.js +++ b/src/components/views/groups/GroupRoomInfo.js @@ -215,7 +215,7 @@ module.exports = React.createClass({
    - +
    { avatar } diff --git a/src/components/views/groups/GroupRoomList.js b/src/components/views/groups/GroupRoomList.js index 81fcfa8c8b..ec41cd036b 100644 --- a/src/components/views/groups/GroupRoomList.js +++ b/src/components/views/groups/GroupRoomList.js @@ -77,7 +77,7 @@ export default React.createClass({ const text = _t("and %(count)s others...", { count: overflowCount }); return ( + } name={text} presenceState="online" suppressOnHover={true} onClick={this._showFullRoomList} /> ); @@ -137,7 +137,7 @@ export default React.createClass({ onClick={this.onAddRoomToGroupButtonClick} >
    - +
    { _t('Add rooms to this community') }
    diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/login/CountryDropdown.js index 8c4467bb99..5f5dbf41a4 100644 --- a/src/components/views/login/CountryDropdown.js +++ b/src/components/views/login/CountryDropdown.js @@ -70,7 +70,7 @@ export default class CountryDropdown extends React.Component { } _flagImgForIso2(iso2) { - return ; + return ; } _getShortOption(iso2) { diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js index efc6c4a069..b4f26d0cbd 100644 --- a/src/components/views/messages/MAudioBody.js +++ b/src/components/views/messages/MAudioBody.js @@ -81,7 +81,7 @@ export default class MAudioBody extends React.Component { if (this.state.error !== null) { return ( - + { _t("Error decrypting audio") } ); @@ -94,7 +94,7 @@ export default class MAudioBody extends React.Component { // Not sure how tall the audio player is so not sure how tall it should actually be. return ( - {content.body} + {content.body} ); } diff --git a/src/components/views/messages/MFileBody.js b/src/components/views/messages/MFileBody.js index 292ac25d42..781d340252 100644 --- a/src/components/views/messages/MFileBody.js +++ b/src/components/views/messages/MFileBody.js @@ -29,15 +29,15 @@ import request from 'browser-request'; import Modal from '../../../Modal'; -// A cached tinted copy of "img/download.svg" +// A cached tinted copy of require("../../../../res/img/download.svg") let tintedDownloadImageURL; // Track a list of mounted MFileBody instances so that we can update -// the "img/download.svg" when the tint changes. +// the require("../../../../res/img/download.svg") when the tint changes. let nextMountId = 0; const mounts = {}; /** - * Updates the tinted copy of "img/download.svg" when the tint changes. + * Updates the tinted copy of require("../../../../res/img/download.svg") when the tint changes. */ function updateTintedDownloadImage() { // Download the svg as an XML document. @@ -46,7 +46,7 @@ function updateTintedDownloadImage() { // Also note that we can't use fetch here because fetch doesn't support // file URLs, which the download image will be if we're running from // the filesystem (like in an Electron wrapper). - request({uri: "img/download.svg"}, (err, response, body) => { + request({uri: require("../../../../res/img/download.svg")}, (err, response, body) => { if (err) return; const svg = new DOMParser().parseFromString(body, "image/svg+xml"); @@ -254,7 +254,7 @@ module.exports = React.createClass({ }, tint: function() { - // Update our tinted copy of "img/download.svg" + // Update our tinted copy of require("../../../../res/img/download.svg") if (this.refs.downloadImage) { this.refs.downloadImage.src = tintedDownloadImageURL; } diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js index dc891b86ff..dd1165cf58 100644 --- a/src/components/views/messages/MImageBody.js +++ b/src/components/views/messages/MImageBody.js @@ -282,7 +282,12 @@ export default class MImageBody extends React.Component { // e2e image hasn't been decrypted yet if (content.file !== undefined && this.state.decryptedUrl === null) { - placeholder = {content.body}; + placeholder = {content.body}; } else if (!this.state.imgLoaded) { // Deliberately, getSpinner is left unimplemented here, MStickerBody overides placeholder = this.getPlaceholder(); @@ -363,7 +368,7 @@ export default class MImageBody extends React.Component { if (this.state.error !== null) { return ( - + { _t("Error decrypting image") } ); diff --git a/src/components/views/messages/MStickerBody.js b/src/components/views/messages/MStickerBody.js index 82a530d503..55263ef7b7 100644 --- a/src/components/views/messages/MStickerBody.js +++ b/src/components/views/messages/MStickerBody.js @@ -35,7 +35,7 @@ export default class MStickerBody extends MImageBody { // img onLoad hasn't fired yet. getPlaceholder() { const TintableSVG = sdk.getComponent('elements.TintableSvg'); - return ; + return ; } // Tooltip to show on mouse over diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js index 37fc94d1ed..f1199263e7 100644 --- a/src/components/views/messages/MVideoBody.js +++ b/src/components/views/messages/MVideoBody.js @@ -135,7 +135,7 @@ module.exports = React.createClass({ if (this.state.error !== null) { return ( - + { _t("Error decrypting video") } ); @@ -148,7 +148,7 @@ module.exports = React.createClass({ return (
    - {content.body} + {content.body}
    ); diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.js index 0bb832f8ea..592afe984a 100644 --- a/src/components/views/messages/RoomCreate.js +++ b/src/components/views/messages/RoomCreate.js @@ -48,7 +48,7 @@ module.exports = React.createClass({ return
    ; // We should never have been instaniated in this case } return
    - +
    {_t("This room is a continuation of another conversation.")}
    diff --git a/src/components/views/right_panel/GroupHeaderButtons.js b/src/components/views/right_panel/GroupHeaderButtons.js index 6fcba1d815..6867b0bb9d 100644 --- a/src/components/views/right_panel/GroupHeaderButtons.js +++ b/src/components/views/right_panel/GroupHeaderButtons.js @@ -65,12 +65,12 @@ export default class GroupHeaderButtons extends HeaderButtons { ]; return [ - , - , - , - - ./ + ./
    ); } diff --git a/src/components/views/rooms/AuxPanel.js b/src/components/views/rooms/AuxPanel.js index 5370b4d8b5..f52a3c52ab 100644 --- a/src/components/views/rooms/AuxPanel.js +++ b/src/components/views/rooms/AuxPanel.js @@ -147,7 +147,7 @@ module.exports = React.createClass({
    - +
    { _t("Drop file here to upload") }
    diff --git a/src/components/views/rooms/EntityTile.js b/src/components/views/rooms/EntityTile.js index 3f3bdbf47a..009da0ebd4 100644 --- a/src/components/views/rooms/EntityTile.js +++ b/src/components/views/rooms/EntityTile.js @@ -160,7 +160,7 @@ const EntityTile = React.createClass({ if (this.props.showInviteButton) { inviteButton = (
    - +
    ); } @@ -169,8 +169,8 @@ const EntityTile = React.createClass({ const powerStatus = this.props.powerStatus; if (powerStatus) { const src = { - [EntityTile.POWER_STATUS_MODERATOR]: "img/mod.svg", - [EntityTile.POWER_STATUS_ADMIN]: "img/admin.svg", + [EntityTile.POWER_STATUS_MODERATOR]: require("../../../../res/img/mod.svg"), + [EntityTile.POWER_STATUS_ADMIN]: require("../../../../res/img/admin.svg"), }[powerStatus]; const alt = { [EntityTile.POWER_STATUS_MODERATOR]: _t("Moderator"), diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 692111361a..acb122ad4e 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -768,23 +768,31 @@ module.exports.haveTileForEvent = function(e) { function E2ePadlockUndecryptable(props) { return ( ); } function E2ePadlockEncrypting(props) { - return ; + return ( + + ); } function E2ePadlockNotSent(props) { - return ; + return ( + + ); } function E2ePadlockVerified(props) { return ( ); } @@ -792,7 +800,7 @@ function E2ePadlockVerified(props) { function E2ePadlockUnverified(props) { return ( ); } @@ -800,7 +808,7 @@ function E2ePadlockUnverified(props) { function E2ePadlockUnencrypted(props) { return ( ); } diff --git a/src/components/views/rooms/LinkPreviewWidget.js b/src/components/views/rooms/LinkPreviewWidget.js index 7f74176878..1483fbffae 100644 --- a/src/components/views/rooms/LinkPreviewWidget.js +++ b/src/components/views/rooms/LinkPreviewWidget.js @@ -135,7 +135,7 @@ module.exports = React.createClass({
    ); diff --git a/src/components/views/rooms/MemberDeviceInfo.js b/src/components/views/rooms/MemberDeviceInfo.js index 41ed995ffb..b9c276f0d1 100644 --- a/src/components/views/rooms/MemberDeviceInfo.js +++ b/src/components/views/rooms/MemberDeviceInfo.js @@ -27,19 +27,19 @@ export default class MemberDeviceInfo extends React.Component { if (this.props.device.isBlocked()) { indicator = (
    - {_t("Blacklisted")} + {_t("Blacklisted")}
    ); } else if (this.props.device.isVerified()) { indicator = (
    - {_t("Verified")} + {_t("Verified")}
    ); } else { indicator = (
    - {_t("Unverified")} + {_t("Unverified")}
    ); } diff --git a/src/components/views/rooms/MemberInfo.js b/src/components/views/rooms/MemberInfo.js index 6a796bc160..9859861870 100644 --- a/src/components/views/rooms/MemberInfo.js +++ b/src/components/views/rooms/MemberInfo.js @@ -815,7 +815,7 @@ module.exports = withMatrixClient(React.createClass({ onClick={this.onNewDMClick} >
    - +
    { _t("Start a chat") }
    ; @@ -963,7 +963,7 @@ module.exports = withMatrixClient(React.createClass({
    - {_t('Close')} + {_t('Close')} { memberName }
    diff --git a/src/components/views/rooms/MemberList.js b/src/components/views/rooms/MemberList.js index 0924a5ec38..f01ef3e3ec 100644 --- a/src/components/views/rooms/MemberList.js +++ b/src/components/views/rooms/MemberList.js @@ -253,7 +253,7 @@ module.exports = React.createClass({ const text = _t("and %(count)s others...", { count: overflowCount }); return ( + } name={text} presenceState="online" suppressOnHover={true} onClick={onClick} /> ); diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 91ef549185..7681c2dc13 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -155,12 +155,12 @@ export default class MessageComposer extends React.Component { const fileAcceptedOrError = this.props.uploadAllowed(files[i]); if (fileAcceptedOrError === true) { acceptedFiles.push(
  • - { files[i].name || _t('Attachment') } + { files[i].name || _t('Attachment') }
  • ); fileList.push(files[i]); } else { failedFiles.push(
  • - { files[i].name || _t('Attachment') }

    { _t('Reason') + ": " + fileAcceptedOrError}

    + { files[i].name || _t('Attachment') }

    { _t('Reason') + ": " + fileAcceptedOrError}

  • ); } } @@ -320,11 +320,11 @@ export default class MessageComposer extends React.Component { const roomIsEncrypted = MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); if (roomIsEncrypted) { // FIXME: show a /!\ if there are untrusted devices in the room... - e2eImg = 'img/e2e-verified.svg'; + e2eImg = require("../../../../res/img/e2e-verified.svg"); e2eTitle = _t('Encrypted room'); e2eClass = 'mx_MessageComposer_e2eIcon'; } else { - e2eImg = 'img/e2e-unencrypted.svg'; + e2eImg = require("../../../../res/img/e2e-unencrypted.svg"); e2eTitle = _t('Unencrypted room'); e2eClass = 'mx_MessageComposer_e2eIcon mx_filterFlipColor'; } @@ -344,16 +344,16 @@ export default class MessageComposer extends React.Component { if (this.props.callState && this.props.callState !== 'ended') { hangupButton = - {_t('Hangup')} + {_t('Hangup')} ; } else { callButton = - + ; videoCallButton = - + ; } @@ -395,7 +395,7 @@ export default class MessageComposer extends React.Component { const uploadButton = ( - + @@ -451,7 +451,7 @@ export default class MessageComposer extends React.Component { controls.push(
    - + {_t("This room has been replaced and is no longer active.")}
    @@ -487,7 +487,7 @@ export default class MessageComposer extends React.Component { title={_t(name)} onMouseDown={onFormatButtonClicked} key={name} - src={`img/button-text-${name}${suffix}.svg`} + src={require(`../../../../res/img/button-text-${name}${suffix}.svg`)} height="17" />; }, ); @@ -500,11 +500,11 @@ export default class MessageComposer extends React.Component { + src={require(`../../../../res/img/button-md-${!this.state.inputState.isRichTextEnabled}.png`)} /> + src={require("../../../../res/img/icon-text-cancel.svg")} />
    ; } diff --git a/src/components/views/rooms/MessageComposerInput.js b/src/components/views/rooms/MessageComposerInput.js index ff7cfcbd6d..22bc965813 100644 --- a/src/components/views/rooms/MessageComposerInput.js +++ b/src/components/views/rooms/MessageComposerInput.js @@ -1599,7 +1599,7 @@ export default class MessageComposerInput extends React.Component { + src={require(`../../../../res/img/button-md-${!this.state.isRichTextEnabled}.png`)} /> - {_t('Unpin + {_t('Unpin
    ); } diff --git a/src/components/views/rooms/PinnedEventsPanel.js b/src/components/views/rooms/PinnedEventsPanel.js index 50c40142da..3a0bc0e326 100644 --- a/src/components/views/rooms/PinnedEventsPanel.js +++ b/src/components/views/rooms/PinnedEventsPanel.js @@ -130,7 +130,7 @@ module.exports = React.createClass({
    - +

    { _t("Pinned Messages") }

    { tiles } diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 46e2826634..7656d21832 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -68,7 +68,7 @@ export default class ReplyPreview extends React.Component { { '💬 ' + _t('Replying') }
    -
    diff --git a/src/components/views/rooms/RoomHeader.js b/src/components/views/rooms/RoomHeader.js index 4292fa6a4d..3cdb9237be 100644 --- a/src/components/views/rooms/RoomHeader.js +++ b/src/components/views/rooms/RoomHeader.js @@ -312,14 +312,14 @@ module.exports = React.createClass({
    - {_t("Remove - + ; } @@ -353,7 +353,7 @@ module.exports = React.createClass({ { pinsIndicator } - + ; } @@ -361,7 +361,7 @@ module.exports = React.createClass({ // if (this.props.onLeaveClick) { // leave_button = //
    -// +// //
    ; // } @@ -369,7 +369,7 @@ module.exports = React.createClass({ if (this.props.onForgetClick) { forgetButton = - + ; } @@ -377,7 +377,7 @@ module.exports = React.createClass({ if (this.props.onSearchClick && this.props.inRoom) { searchButton = - + ; } @@ -385,7 +385,7 @@ module.exports = React.createClass({ if (this.props.inRoom) { shareRoomButton = - + ; } diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 3c767b726a..dbe409d6d7 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -124,7 +124,7 @@ module.exports = React.createClass({ emailMatchBlock =
    - /!\\ + /!\\
    { _t("This invitation was sent to an email address which is not associated with this account:") } diff --git a/src/components/views/rooms/RoomSettings.js b/src/components/views/rooms/RoomSettings.js index 8cd559f2ea..9287eb3fae 100644 --- a/src/components/views/rooms/RoomSettings.js +++ b/src/components/views/rooms/RoomSettings.js @@ -616,7 +616,7 @@ module.exports = React.createClass({
    { settings } @@ -627,8 +627,8 @@ module.exports = React.createClass({
    diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 1a17fcf5ea..ed214812b5 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -392,7 +392,13 @@ module.exports = React.createClass({ let dmIndicator; if (this._isDirectMessageRoom(this.props.room.roomId)) { - dmIndicator = dm; + dmIndicator = dm; } return + } name={text} presenceState="online" suppressOnHover={true} onClick={this._showAll} /> ); diff --git a/src/components/views/rooms/SimpleRoomHeader.js b/src/components/views/rooms/SimpleRoomHeader.js index 249ba8b93a..4ced9fb351 100644 --- a/src/components/views/rooms/SimpleRoomHeader.js +++ b/src/components/views/rooms/SimpleRoomHeader.js @@ -26,7 +26,7 @@ export function CancelButton(props) { return ( - {_t("Cancel")} ); diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index c7d9f890a7..b8fe3afb97 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -152,7 +152,7 @@ export default class Stickerpicker extends React.Component { className='mx_Stickers_contentPlaceholder'>

    { _t("You don't currently have any stickerpacks enabled") }

    { _t("Add some now") }

    - +
    ); } @@ -351,7 +351,7 @@ export default class Stickerpicker extends React.Component { onClick={this._onHideStickersClick} ref='target' title={_t("Hide Stickers")}> - + ; } else { // Show show-stickers button @@ -362,7 +362,7 @@ export default class Stickerpicker extends React.Component { className="mx_MessageComposer_stickers" onClick={this._onShowStickersClick} title={_t("Show Stickers")}> - + ; } return
    diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js index 64822ace5f..82169c9868 100644 --- a/src/components/views/settings/AddPhoneNumber.js +++ b/src/components/views/settings/AddPhoneNumber.js @@ -166,7 +166,7 @@ export default withMatrixClient(React.createClass({
    - +
    ); diff --git a/src/components/views/voip/IncomingCallBox.js b/src/components/views/voip/IncomingCallBox.js index 6cbaabe602..43c339d182 100644 --- a/src/components/views/voip/IncomingCallBox.js +++ b/src/components/views/voip/IncomingCallBox.js @@ -66,7 +66,7 @@ module.exports = React.createClass({ const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); return (
    - +
    { incomingCallText }
    From 8b05cca7d29678d7aabc45764648b10db7890fd5 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 18 Jan 2019 10:55:00 -0600 Subject: [PATCH 11/74] Add file-loader to Karma so assets will resolve in tests --- karma.conf.js | 4 ++++ package.json | 1 + 2 files changed, 5 insertions(+) diff --git a/karma.conf.js b/karma.conf.js index 4d699599cb..93063dbc04 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -168,6 +168,10 @@ module.exports = function (config) { path.resolve('./test'), ] }, + { + test: /\.(gif|png|svg|ttf)$/, + loader: 'file-loader', + }, ], noParse: [ // for cross platform compatibility use [\\\/] as the path separator diff --git a/package.json b/package.json index 15d1eff836..dc46d437ec 100644 --- a/package.json +++ b/package.json @@ -125,6 +125,7 @@ "eslint-plugin-react": "^7.7.0", "estree-walker": "^0.5.0", "expect": "^23.6.0", + "file-loader": "^3.0.1", "flow-parser": "^0.57.3", "jest-mock": "^23.2.0", "karma": "^3.0.0", From c9d5c4903b3215b870a52dab4508e6b7e688cbf1 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Fri, 18 Jan 2019 18:53:54 +0100 Subject: [PATCH 12/74] set min-height of messagelist to current height when showing typing bar this ensures the timeline never shrinks, and avoids jumpiness when typing bar disappears again. --- src/components/structures/MessagePanel.js | 8 +++++--- src/components/structures/ScrollPanel.js | 25 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 5fe2aae471..5383cf15dc 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -631,9 +631,11 @@ module.exports = React.createClass({ } }, - _scrollDownIfAtBottom: function() { + _onTypingVisible: function() { const scrollPanel = this.refs.scrollPanel; - if (scrollPanel) { + if (scrollPanel && scrollPanel.getScrollState().stuckAtBottom) { + scrollPanel.blockShrinking(); + // scroll down if at bottom scrollPanel.checkScroll(); } }, @@ -666,7 +668,7 @@ module.exports = React.createClass({ let whoIsTyping; if (this.props.room) { - whoIsTyping = (); + whoIsTyping = (); } return ( diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 0fdbc9a349..91a9f1ddfa 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -223,6 +223,8 @@ module.exports = React.createClass({ onResize: function() { this.props.onResize(); + // clear min-height as the height might have changed + this.clearBlockShrinking(); this.checkScroll(); if (this._gemScroll) this._gemScroll.forceUpdate(); }, @@ -372,6 +374,8 @@ module.exports = React.createClass({ } this._unfillDebouncer = setTimeout(() => { this._unfillDebouncer = null; + // if timeline shrinks, min-height should be cleared + this.clearBlockShrinking(); this.props.onUnfillRequest(backwards, markerScrollToken); }, UNFILL_REQUEST_DEBOUNCE_MS); } @@ -677,6 +681,27 @@ module.exports = React.createClass({ _collectGeminiScroll: function(gemScroll) { this._gemScroll = gemScroll; }, + /** + * Set the current height as the min height for the message list + * so the timeline cannot shrink. This is used to avoid + * jumping when the typing indicator gets replaced by a smaller message. + */ + blockShrinking: function() { + const messageList = this.refs.itemlist; + if (messageList) { + const currentHeight = messageList.clientHeight - 18; + messageList.style.minHeight = `${currentHeight}px`; + } + }, + /** + * Clear the previously set min height + */ + clearBlockShrinking: function() { + const messageList = this.refs.itemlist; + if (messageList) { + messageList.style.minHeight = null; + } + }, render: function() { const GeminiScrollbarWrapper = sdk.getComponent("elements.GeminiScrollbarWrapper"); From efd5405267cc3e1110658e5e2d1d9ea2df86df06 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Sat, 19 Jan 2019 22:11:20 -0600 Subject: [PATCH 13/74] Add a form field component for the redesign The label moves into the border on focus and after being filled. A valid color is applied to the label and input border. Other states like invalid can be added later as needed. Adapted from @ara4n's experiment into a React component with a CSS only approach. --- res/css/_components.scss | 1 + res/css/views/elements/_Field.scss | 77 ++++++++++++++++++++++++++ res/themes/dharma/css/_dharma.scss | 7 ++- res/themes/light/css/_base.scss | 3 + src/components/views/elements/Field.js | 51 +++++++++++++++++ 5 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 res/css/views/elements/_Field.scss create mode 100644 src/components/views/elements/Field.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 8f6f4d5936..6b353ba72b 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -59,6 +59,7 @@ @import "./views/elements/_DirectorySearchBox.scss"; @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; +@import "./views/elements/_Field.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; @import "./views/elements/_MemberEventListSummary.scss"; diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss new file mode 100644 index 0000000000..1b5b0b37c2 --- /dev/null +++ b/res/css/views/elements/_Field.scss @@ -0,0 +1,77 @@ +/* +Copyright 2019 New Vector 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. +*/ + +/* TODO: Consider unifying with general input styles in _dharma.scss */ + +.mx_Field { + position: relative; + margin: 1em 0; +} + +.mx_Field input { + font-weight: normal; + border-radius: 4px; + transition: border-color 0.25s; + border: 1px solid $input-border-color; + padding: 8px 10px; +} + +.mx_Field input:focus { + outline: 0; + border-color: $input-valid-border-color; +} + +.mx_Field input::placeholder { + transition: color 0.25s ease-in 0s; + color: transparent; +} + +.mx_Field input:placeholder-shown:focus::placeholder { + transition: color 0.25s ease-in 0.1s; + color: $greyed-fg-color; +} + +.mx_Field label { + transition: + font-size 0.25s ease-out 0.1s, + color 0.25s ease-out 0.1s, + top 0.25s ease-out 0.1s, + background-color 0.25s ease-out 0.1s; + color: $primary-fg-color; + background-color: transparent; + font-size: 14px; + position: absolute; + left: 0px; + top: 0px; + margin: 6px 8px; + padding: 2px; +} + +.mx_Field input:focus + label, +.mx_Field input:not(:placeholder-shown) + label { + transition: + font-size 0.25s ease-out 0s, + color 0.25s ease-out 0s, + top 0.25s ease-out 0s, + background-color 0.25s ease-out 0s; + font-size: 10px; + top: -14px; + background-color: $field-focused-label-bg-color; +} + +.mx_Field input:focus + label { + color: $input-valid-border-color; +} diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 7de20c1ed4..4a2bfec8e4 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -71,6 +71,9 @@ $input-darker-bg-color: rgba(193, 201, 214, 0.29); $input-darker-fg-color: #9fa9ba; $input-lighter-bg-color: #f2f5f8; $input-lighter-fg-color: $input-darker-fg-color; +$input-valid-border-color: #7ac9a1; + +$field-focused-label-bg-color: #ffffff; $button-bg-color: #7ac9a1; $button-fg-color: white; @@ -198,8 +201,8 @@ $memberstatus-placeholder-color: $roomtile-name-color; .mx_MatrixChat { - :not(.mx_textinput) > input[type=text], - :not(.mx_textinput) > input[type=search], + :not(.mx_textinput):not(.mx_Field) > input[type=text], + :not(.mx_textinput):not(.mx_Field) > input[type=search], .mx_textinput { display: block; margin: 9px; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 4b3501cbaf..9a3b3727a9 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -73,6 +73,9 @@ $button-bg-color: #7ac9a1; $button-fg-color: white; // apart from login forms, which have stronger border $strong-input-border-color: #c7c7c7; +$input-valid-border-color: #7ac9a1; + +$field-focused-label-bg-color: #ffffff; // used for UserSettings EditableText $input-underline-color: rgba(151, 151, 151, 0.5); diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js new file mode 100644 index 0000000000..b74e28023d --- /dev/null +++ b/src/components/views/elements/Field.js @@ -0,0 +1,51 @@ +/* +Copyright 2019 New Vector 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 PropTypes from 'prop-types'; + +export default class Field extends React.PureComponent { + static propTypes = { + // The field's id, which binds the input and label together. + id: PropTypes.string.isRequired, + // The field's type. Defaults to "text". + type: PropTypes.string, + // The field's label string. + label: PropTypes.string, + // The field's placeholder string. + placeholder: PropTypes.string, + // All other props pass through to the . + } + + render() { + const extraProps = Object.assign({}, this.props); + + // Remove explicit props + delete extraProps.id; + delete extraProps.type; + delete extraProps.placeholder; + delete extraProps.label; + + return
    + + +
    ; + } +} From 36ffbef010db6d60a4dbc9b6c4eab9ad4ebbcf7b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Sat, 19 Jan 2019 22:29:57 -0600 Subject: [PATCH 14/74] Tweak comment about field ID Co-Authored-By: jryans --- src/components/views/elements/Field.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index b74e28023d..428c7f1d6c 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; export default class Field extends React.PureComponent { static propTypes = { - // The field's id, which binds the input and label together. + // The field's ID, which binds the input and label together. id: PropTypes.string.isRequired, // The field's type. Defaults to "text". type: PropTypes.string, From 728830e2fd19a66f8de6b8488bfc2211a7635af9 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Sat, 19 Jan 2019 20:31:06 -0600 Subject: [PATCH 15/74] Add rethemendex as script for easier access --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index dc46d437ec..aca3d433ad 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "scripts": { "reskindex": "node scripts/reskindex.js -h header", "reskindex:watch": "node scripts/reskindex.js -h header -w", + "rethemendex": "res/css/rethemendex.sh", "i18n": "matrix-gen-i18n", "prunei18n": "matrix-prune-i18n", "build": "npm run reskindex && npm run start:init", From efcfaed335bc66ceb6487f766bf832f667048b40 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Sun, 20 Jan 2019 14:31:06 +0000 Subject: [PATCH 16/74] switch to 35px high gradients --- res/css/structures/_RoomSubList.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index 4a60650cef..faaf1cf462 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -155,7 +155,7 @@ limitations under the License. position: sticky; left: 0; right: 0; - height: 30px; + height: 35px; content: ""; display: block; z-index: 100; @@ -163,10 +163,10 @@ limitations under the License. } &.mx_IndicatorScrollbar_topOverflow > .mx_AutoHideScrollbar_offset { - margin-top: -30px; + margin-top: -35px; } &.mx_IndicatorScrollbar_bottomOverflow > .mx_AutoHideScrollbar_offset { - margin-bottom: -30px; + margin-bottom: -35px; } &.mx_IndicatorScrollbar_topOverflow::before { From 4c381c32ee43ba37dc475088e6ee4e3bd1c199ee Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 11:34:13 +0000 Subject: [PATCH 17/74] Show verify button when we have a device to verify rather than when we don't --- src/components/views/settings/KeyBackupPanel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/KeyBackupPanel.js b/src/components/views/settings/KeyBackupPanel.js index 473b52ff12..8dc817faad 100644 --- a/src/components/views/settings/KeyBackupPanel.js +++ b/src/components/views/settings/KeyBackupPanel.js @@ -234,7 +234,7 @@ export default class KeyBackupPanel extends React.PureComponent { } let verifyButton; - if (!sig.device || !sig.device.isVerified()) { + if (sig.device && !sig.device.isVerified()) { verifyButton =

    { _t("Verify...") } From 7e0f60505e65377a216a1a1adf06d4f4f44fa8e3 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 16:02:25 +0000 Subject: [PATCH 18/74] Fix settings direct chat We were expecting this function to return a string user ID, but it returned a user object Make it return a user ID and add jsdoc to say what it returns. Fixes https://github.com/vector-im/riot-web/issues/8180 --- src/Rooms.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Rooms.js b/src/Rooms.js index 6f73ea0659..c8f90ec39a 100644 --- a/src/Rooms.js +++ b/src/Rooms.js @@ -159,6 +159,10 @@ export function setDMRoom(roomId, userId) { /** * Given a room, estimate which of its members is likely to * be the target if the room were a DM room and return that user. + * + * @param {Object} room Target room + * @param {string} myUserId User ID of the current user + * @returns {string} User ID of the user that the room is probably a DM with */ function guessDMRoomTargetId(room, myUserId) { let oldestTs; @@ -173,7 +177,7 @@ function guessDMRoomTargetId(room, myUserId) { oldestTs = user.events.member.getTs(); } } - if (oldestUser) return oldestUser; + if (oldestUser) return oldestUser.userId; // if there are no joined members other than us, use the oldest member for (const user of room.currentState.getMembers()) { @@ -186,5 +190,5 @@ function guessDMRoomTargetId(room, myUserId) { } if (oldestUser === undefined) return myUserId; - return oldestUser; + return oldestUser.userId; } From 018f3d2a5ce7c501b61b1c5a9348ba437938b487 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 21 Jan 2019 17:30:02 +0100 Subject: [PATCH 19/74] use box-sizing: border-box to make clientHeight === actual height --- res/css/structures/_RoomView.scss | 4 ++++ src/components/structures/ScrollPanel.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index d8926c68e4..02d5cc67b0 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -171,6 +171,10 @@ limitations under the License. .mx_RoomView_MessageList { list-style-type: none; padding: 18px; + margin: 0; + /* needed as min-height is set to clientHeight in ScrollPanel + to prevent shrinking when WhoIsTypingTile is hidden */ + box-sizing: border-box; } .mx_RoomView_MessageList li { diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 91a9f1ddfa..96318d64e1 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -689,7 +689,7 @@ module.exports = React.createClass({ blockShrinking: function() { const messageList = this.refs.itemlist; if (messageList) { - const currentHeight = messageList.clientHeight - 18; + const currentHeight = messageList.clientHeight; messageList.style.minHeight = `${currentHeight}px`; } }, From 25aa58f29f526baba9bb6b212fd1f67e2b385284 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 21 Jan 2019 17:35:40 +0100 Subject: [PATCH 20/74] increase/clear min-height on timeline on new message(s) depending on whether the typing bar is visible --- src/components/structures/MessagePanel.js | 16 +++++++++++++++- src/components/structures/TimelinePanel.js | 11 ++++++++--- src/components/views/rooms/WhoIsTypingTile.js | 4 ++++ 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 5383cf15dc..fc3b421e89 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -640,6 +640,20 @@ module.exports = React.createClass({ } }, + updateTimelineMinHeight: function() { + const scrollPanel = this.refs.scrollPanel; + const whoIsTyping = this.refs.whoIsTyping; + const isTypingVisible = whoIsTyping && whoIsTyping.isVisible(); + + if (scrollPanel) { + if (isTypingVisible) { + scrollPanel.blockShrinking(); + } else { + scrollPanel.clearBlockShrinking(); + } + } + }, + onResize: function() { dis.dispatch({ action: 'timeline_resize' }, true); }, @@ -668,7 +682,7 @@ module.exports = React.createClass({ let whoIsTyping; if (this.props.room) { - whoIsTyping = (); + whoIsTyping = (); } return ( diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index ab10ec4aca..9fe83c2c2d 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -455,7 +455,7 @@ var TimelinePanel = React.createClass({ // const myUserId = MatrixClientPeg.get().credentials.userId; const sender = ev.sender ? ev.sender.userId : null; - var callback = null; + var callRMUpdated = false; if (sender != myUserId && !UserActivity.userCurrentlyActive()) { updatedState.readMarkerVisible = true; } else if (lastEv && this.getReadMarkerPosition() === 0) { @@ -465,11 +465,16 @@ var TimelinePanel = React.createClass({ this._setReadMarker(lastEv.getId(), lastEv.getTs(), true); updatedState.readMarkerVisible = false; updatedState.readMarkerEventId = lastEv.getId(); - callback = this.props.onReadMarkerUpdated; + callRMUpdated = true; } } - this.setState(updatedState, callback); + this.setState(updatedState, () => { + this.refs.messagePanel.updateTimelineMinHeight(); + if (callRMUpdated) { + this.props.onReadMarkerUpdated(); + } + }); }); }, diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 5a2b6afc96..bef8aca0c4 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -77,6 +77,10 @@ module.exports = React.createClass({ Object.values(this.state.delayedStopTypingTimers).forEach((t) => t.abort()); }, + isVisible: function() { + return this.state.usersTyping.length !== 0 || Object.keys(this.state.delayedStopTypingTimers) !== 0; + }, + onRoomTimeline: function(event, room) { if (room.roomId === this.props.room.roomId) { const userId = event.getSender(); From 86357fde511fdae5158c38c749a1a8eba5e95499 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 21 Jan 2019 17:49:46 +0100 Subject: [PATCH 21/74] sort combined typing users by name so order is stable --- src/components/views/rooms/WhoIsTypingTile.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index bef8aca0c4..4c97110797 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -191,6 +191,9 @@ module.exports = React.createClass({ // but have a timeout timer running so they can disappear // when a message comes in usersTyping = usersTyping.concat(stoppedUsersOnTimer); + // sort them so the typing members don't change order when + // moved to delayedStopTypingTimers + usersTyping.sort((a, b) => a.name.localeCompare(b.name)); const typingString = WhoIsTyping.whoIsTypingString( usersTyping, From 0a2b79b62d90376a9b1fd11d77a393a46498c980 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 16:51:30 +0000 Subject: [PATCH 22/74] Error if no sessions decrypted Tell the user if they entered the wrong recovery key Fixes https://github.com/vector-im/riot-web/issues/8143 --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 11 ++++++++++- src/i18n/strings/en_EN.json | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index fba9f0b714..d614168eaa 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -1,5 +1,5 @@ /* -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -184,6 +184,15 @@ export default React.createClass({ } else if (this.state.backupInfo === null) { title = _t("Error"); content = _t("No backup found!"); + } else if (this.state.recoverInfo && this.state.recoverInfo.imported === 0) { + title = _t("Error Restoring Backup"); + let failedToDecrypt; + content =
    +

    {_t( + "Failed to decrypt any sessions: check that the recovery key " + + "used is the correct one for this backup." + )}

    +
    ; } else if (this.state.recoverInfo) { title = _t("Backup Restored"); let failedToDecrypt; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 19c6bf5d28..fd9d9b3eef 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -114,8 +114,6 @@ "Failed to invite": "Failed to invite", "Failed to invite users to the room:": "Failed to invite users to the room:", "Failed to invite the following users to the %(roomName)s room:": "Failed to invite the following users to the %(roomName)s room:", - "Waiting for %(userId)s to accept...": "Waiting for %(userId)s to accept...", - "Waiting for %(userId)s to confirm...": "Waiting for %(userId)s to confirm...", "You need to be logged in.": "You need to be logged in.", "You need to be able to invite users to do that.": "You need to be able to invite users to do that.", "Unable to create widget.": "Unable to create widget.", @@ -1048,6 +1046,8 @@ "Unable to load backup status": "Unable to load backup status", "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", + "Error Restoring Backup": "Error Restoring Backup", + "Failed to decrypt any sessions: check that the recovery key used is the correct one for this backup.": "Failed to decrypt any sessions: check that the recovery key used is the correct one for this backup.", "Backup Restored": "Backup Restored", "Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!", "Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys", From 1a9c3f1ac7b8aeb39a5741bd6eaf535deeedc4b6 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 17:41:55 +0000 Subject: [PATCH 23/74] Make error string less technical --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 3 +-- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index d614168eaa..89d41803d5 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -189,8 +189,7 @@ export default React.createClass({ let failedToDecrypt; content =

    {_t( - "Failed to decrypt any sessions: check that the recovery key " + - "used is the correct one for this backup." + "Backup restore failed: please verify that you entered the correct recovery key.", )}

    ; } else if (this.state.recoverInfo) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index fd9d9b3eef..7b701cc863 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1047,7 +1047,7 @@ "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", "Error Restoring Backup": "Error Restoring Backup", - "Failed to decrypt any sessions: check that the recovery key used is the correct one for this backup.": "Failed to decrypt any sessions: check that the recovery key used is the correct one for this backup.", + "Backup restore failed: please verify that you entered the correct recovery key.": "Backup restore failed: please verify that you entered the correct recovery key.", "Backup Restored": "Backup Restored", "Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!", "Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys", From e42b0a9972d870f29a54abe9d0942fc3c9355ae2 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 18:02:09 +0000 Subject: [PATCH 24/74] Make error string less redundant --- .../views/dialogs/keybackup/RestoreKeyBackupDialog.js | 3 ++- src/i18n/strings/en_EN.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index 89d41803d5..d47eca010f 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -189,7 +189,8 @@ export default React.createClass({ let failedToDecrypt; content =

    {_t( - "Backup restore failed: please verify that you entered the correct recovery key.", + "Backup could not be decrypted with this key: " + + "please verify that you entered the correct recovery key.", )}

    ; } else if (this.state.recoverInfo) { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 7b701cc863..16c40460e2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1047,7 +1047,7 @@ "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", "Error Restoring Backup": "Error Restoring Backup", - "Backup restore failed: please verify that you entered the correct recovery key.": "Backup restore failed: please verify that you entered the correct recovery key.", + "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.": "Backup could not be decrypted with this key: please verify that you entered the correct recovery key.", "Backup Restored": "Backup Restored", "Failed to decrypt %(failedCount)s sessions!": "Failed to decrypt %(failedCount)s sessions!", "Restored %(sessionCount)s session keys": "Restored %(sessionCount)s session keys", From 31889ecfad6a92704590309201d6e1867a694f6f Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 19:07:44 +0000 Subject: [PATCH 25/74] Lint --- src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js index d47eca010f..fdb4f0a226 100644 --- a/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/keybackup/RestoreKeyBackupDialog.js @@ -186,7 +186,6 @@ export default React.createClass({ content = _t("No backup found!"); } else if (this.state.recoverInfo && this.state.recoverInfo.imported === 0) { title = _t("Error Restoring Backup"); - let failedToDecrypt; content =

    {_t( "Backup could not be decrypted with this key: " + From ce412280000659b6ea9e7f3a23a7b79c4d06fc96 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 19:20:11 +0000 Subject: [PATCH 26/74] Token encouragement if zxcvbn gives no feedback zxcvbn is hardcoded to give no suggedtions if the score is greater than 2, but our threshold is 4, so give some generic feedback. Fixes https://github.com/vector-im/riot-web/issues/7860 --- .../views/dialogs/keybackup/CreateKeyBackupDialog.js | 2 +- src/i18n/strings/en_EN.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 4fb5df214b..fd3d6776eb 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -254,7 +254,7 @@ export default React.createClass({ } const suggestionBlock = suggestions.length > 0 ?

    {suggestions} -
    : null; +
    :
    {_t("Keep going...")}
    ; helpText =
    {this.state.zxcvbnResult.feedback.warning} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 16c40460e2..1c4dbeea65 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1381,6 +1381,7 @@ "File to import": "File to import", "Import": "Import", "Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.", + "Keep going...": "Keep going...", "Secure your encrypted message history with a Recovery Passphrase.": "Secure your encrypted message history with a Recovery Passphrase.", "You'll need it if you log out or lose access to this device.": "You'll need it if you log out or lose access to this device.", "Enter a passphrase...": "Enter a passphrase...", From bd011de1e5352041513c7895941437b1f07fa684 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 21 Jan 2019 19:32:57 +0000 Subject: [PATCH 27/74] De-lint CompatabilityPage & LoggedInView and remove from errorfiles --- .eslintignore.errorfiles | 2 - .../structures/CompatibilityPage.js | 16 ++++++-- src/components/structures/LoggedInView.js | 40 ++++++++++++------- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 30cc55377c..4d4db0722d 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -2,9 +2,7 @@ src/component-index.js src/components/structures/BottomLeftMenu.js -src/components/structures/CompatibilityPage.js src/components/structures/CreateRoom.js -src/components/structures/LoggedInView.js src/components/structures/login/ForgotPassword.js src/components/structures/MessagePanel.js src/components/structures/NotificationPanel.js diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js index 3c5005c053..28521cb1b7 100644 --- a/src/components/structures/CompatibilityPage.js +++ b/src/components/structures/CompatibilityPage.js @@ -41,10 +41,15 @@ module.exports = React.createClass({

    { _t("Sorry, your browser is not able to run Riot.", {}, { 'b': (sub) => {sub} }) }

    - { _t("Riot uses many advanced browser features, some of which are not available or experimental in your current browser.") } + { _t( + "Riot uses many advanced browser features, some of which are not available " + + "or experimental in your current browser.", + ) }

    - { _t('Please install Chrome or Firefox for the best experience.', + { _t( + 'Please install Chrome or Firefox ' + + 'for the best experience.', {}, { 'chromeLink': (sub) => {sub}, @@ -60,7 +65,12 @@ module.exports = React.createClass({ )}

    - { _t("With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!") } + { _t( + "With your current browser, the look and feel of the application may be " + + "completely incorrect, and some or all features may not function. " + + "If you want to try it anyway you can continue, but you are on your own in terms " + + "of any issues you may encounter!", + ) }

    From 1906cfd700a289d2045bea1adf031e3b40899aa2 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 10:09:33 -0600 Subject: [PATCH 28/74] Tweak field label to be vertically centered --- res/css/views/elements/_Field.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 1b5b0b37c2..e28ae91761 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -56,7 +56,7 @@ limitations under the License. position: absolute; left: 0px; top: 0px; - margin: 6px 8px; + margin: 7px 8px; padding: 2px; } From 352107352edd46a63182c57e9a7cb539cb34fb2b Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 10:16:20 -0600 Subject: [PATCH 29/74] Tweak field input padding to match label position --- res/css/views/elements/_Field.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index e28ae91761..f8090eaaf7 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -26,7 +26,7 @@ limitations under the License. border-radius: 4px; transition: border-color 0.25s; border: 1px solid $input-border-color; - padding: 8px 10px; + padding: 8px 9px; } .mx_Field input:focus { From 23b3e74c1c3037e687620ea609dda8f95ce943cb Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 10:24:04 -0600 Subject: [PATCH 30/74] Change to new field focused color --- res/css/views/elements/_Field.scss | 4 ++-- res/themes/dharma/css/_dharma.scss | 1 + res/themes/light/css/_base.scss | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index f8090eaaf7..c728f4bcd4 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -31,7 +31,7 @@ limitations under the License. .mx_Field input:focus { outline: 0; - border-color: $input-valid-border-color; + border-color: $input-focused-border-color; } .mx_Field input::placeholder { @@ -73,5 +73,5 @@ limitations under the License. } .mx_Field input:focus + label { - color: $input-valid-border-color; + color: $input-focused-border-color; } diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 4a2bfec8e4..5f6a69abe3 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -71,6 +71,7 @@ $input-darker-bg-color: rgba(193, 201, 214, 0.29); $input-darker-fg-color: #9fa9ba; $input-lighter-bg-color: #f2f5f8; $input-lighter-fg-color: $input-darker-fg-color; +$input-focused-border-color: #238cf5; $input-valid-border-color: #7ac9a1; $field-focused-label-bg-color: #ffffff; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 9a3b3727a9..dbab909f13 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -73,6 +73,7 @@ $button-bg-color: #7ac9a1; $button-fg-color: white; // apart from login forms, which have stronger border $strong-input-border-color: #c7c7c7; +$input-focused-border-color: #238cf5; $input-valid-border-color: #7ac9a1; $field-focused-label-bg-color: #ffffff; From 82dee37afeb045c511e6ad2748198164178929c4 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 08:56:58 -0600 Subject: [PATCH 31/74] Fix styling regression in member list filter --- res/themes/dharma/css/_dharma.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 5f6a69abe3..1436328047 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -247,8 +247,8 @@ input[type=password] { } .dark-panel { - :not(.mx_textinput) > input[type=text], - :not(.mx_textinput) > input[type=search], + :not(.mx_textinput):not(.mx_Field) > input[type=text], + :not(.mx_textinput):not(.mx_Field) > input[type=search], .mx_textinput { color: $input-darker-fg-color; background-color: $input-darker-bg-color; @@ -257,8 +257,8 @@ input[type=password] { } .light-panel { - :not(.mx_textinput) > input[type=text], - :not(.mx_textinput) > input[type=search], + :not(.mx_textinput):not(.mx_Field) > input[type=text], + :not(.mx_textinput):not(.mx_Field) > input[type=search], .mx_textinput { color: $input-lighter-fg-color; background-color: $input-lighter-bg-color; From e714b2aed2a000c0c159948a6e19290294d6ee5a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 12:05:07 -0600 Subject: [PATCH 32/74] Remove Status theme-specific hacks --- src/components/structures/login/Login.js | 14 +------ .../structures/login/Registration.js | 19 +++------ src/components/views/login/LoginPage.js | 39 +++---------------- 3 files changed, 13 insertions(+), 59 deletions(-) diff --git a/src/components/structures/login/Login.js b/src/components/structures/login/Login.js index 321084389b..6206b7660d 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/login/Login.js @@ -1,7 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,7 +24,6 @@ import { _t, _td } from '../../../languageHandler'; import sdk from '../../../index'; import Login from '../../../Login'; import SdkConfig from '../../../SdkConfig'; -import SettingsStore from "../../../settings/SettingsStore"; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; import { AutoDiscovery } from "matrix-js-sdk"; @@ -533,7 +532,6 @@ module.exports = React.createClass({ } let serverConfig; - let header; if (!SdkConfig.get()['disable_custom_urls']) { serverConfig = ; } - // FIXME: remove status.im theme tweaks - const theme = SettingsStore.getValue("theme"); - if (theme !== "status") { - header =

    { _t('Sign in') } { loader }

    ; - } else { - if (!errorText) { - header =

    { _t('Sign in to get started') } { loader }

    ; - } - } + const header =

    { _t('Sign in') } { loader }

    ; let errorTextSection; if (errorText) { diff --git a/src/components/structures/login/Registration.js b/src/components/structures/login/Registration.js index fa5a02e881..74e3685bb5 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/login/Registration.js @@ -1,7 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,6 @@ import RegistrationForm from '../../views/login/RegistrationForm'; import RtsClient from '../../../RtsClient'; import { _t, _td } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; -import SettingsStore from "../../../settings/SettingsStore"; import { messageForResourceLimitError } from '../../../utils/ErrorUtils'; const MIN_PASSWORD_LENGTH = 6; @@ -404,8 +403,6 @@ module.exports = React.createClass({ const Spinner = sdk.getComponent("elements.Spinner"); const ServerConfig = sdk.getComponent('views.login.ServerConfig'); - const theme = SettingsStore.getValue("theme"); - let registerBody; if (this.state.doingUIAuth) { registerBody = ( @@ -458,24 +455,18 @@ module.exports = React.createClass({ ); } - let header; let errorText; - // FIXME: remove hardcoded Status team tweaks at some point const err = this.state.errorText || this.props.defaultServerDiscoveryError; - if (theme === 'status' && err) { - header =
    { err }
    ; - } else { - header =

    { _t('Create an account') }

    ; - if (err) { - errorText =
    { err }
    ; - } + const header =

    { _t('Create an account') }

    ; + if (err) { + errorText =
    { err }
    ; } let signIn; if (!this.state.doingUIAuth) { signIn = ( - { theme === 'status' ? _t('Sign in') : _t('I already have an account') } + { _t('I already have an account') } ); } diff --git a/src/components/views/login/LoginPage.js b/src/components/views/login/LoginPage.js index 9eba53188e..83da8fa60b 100644 --- a/src/components/views/login/LoginPage.js +++ b/src/components/views/login/LoginPage.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,44 +17,16 @@ limitations under the License. 'use strict'; -import SettingsStore from "../../../settings/SettingsStore"; - const React = require('react'); module.exports = React.createClass({ displayName: 'LoginPage', render: function() { - // FIXME: this should be turned into a proper skin with a StatusLoginPage component - if (SettingsStore.getValue("theme") === 'status') { - return ( -
    -
    - Status -
    -
    -
    -

    Status Community Chat

    -
    - A safer, decentralised communication - platform powered by Riot -
    -
    - { this.props.children } -
    -

    This channel is for our development community.

    -

    Interested in SNT and discussions on the cryptocurrency market?

    -

    Join Telegram Chat

    -
    -
    -
    - ); - } else { - return ( -
    - { this.props.children } -
    - ); - } + return ( +
    + { this.props.children } +
    + ); }, }); From 31808c23a864991b0b6c9b6bd7d8944a58f03fd8 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 14:31:51 -0600 Subject: [PATCH 33/74] Override Firefox UA style for placeholders --- res/themes/dharma/css/_dharma.scss | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 1436328047..c9f62fbe6b 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -295,6 +295,13 @@ input[type=search]::-webkit-search-results-decoration { .input[type=search]::-moz-placeholder { color: #a5aab2; } + +// Override Firefox's UA style so we get a consistent look across browsers +input::placeholder, +textarea::placeholder { + opacity: initial; +} + // ***** Mixins! ***** @define-mixin mx_DialogButton { From cd8c74a7c48014458405c35480bf0ceec826dca5 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 15:03:55 -0600 Subject: [PATCH 34/74] Rebuild strings --- src/i18n/strings/en_EN.json | 1 - 1 file changed, 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 16c40460e2..cfd5168152 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1328,7 +1328,6 @@ "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.": "Can't connect to homeserver - please check your connectivity, ensure your homeserver's SSL certificate is trusted, and that a browser extension is not blocking requests.", "Sign in with single sign-on": "Sign in with single sign-on", "Try the app first": "Try the app first", - "Sign in to get started": "Sign in to get started", "Failed to fetch avatar URL": "Failed to fetch avatar URL", "Set a display name:": "Set a display name:", "Upload an avatar:": "Upload an avatar:", From 29be3ee4b5aa0fd7ddb10936545344d091dfe340 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 16:11:10 -0600 Subject: [PATCH 35/74] Rename login directory to auth --- .eslintignore.errorfiles | 16 ++++++++-------- res/css/_components.scss | 6 +++--- res/css/structures/{login => auth}/_Login.scss | 0 .../_InteractiveAuthEntryComponents.scss | 0 res/css/views/{login => auth}/_ServerConfig.scss | 0 src/components/structures/InteractiveAuth.js | 2 +- src/components/structures/MatrixChat.js | 10 +++++----- .../structures/{login => auth}/ForgotPassword.js | 10 +++++----- .../{login => auth}/LanguageSelector.js | 0 .../structures/{login => auth}/Login.js | 12 ++++++------ .../{login => auth}/PostRegistration.js | 4 ++-- .../structures/{login => auth}/Registration.js | 12 ++++++------ .../views/{login => auth}/CaptchaForm.js | 0 .../views/{login => auth}/CountryDropdown.js | 0 .../views/{login => auth}/CustomServerDialog.js | 0 .../InteractiveAuthEntryComponents.js | 2 +- .../views/{login => auth}/LoginFooter.js | 0 .../views/{login => auth}/LoginHeader.js | 0 .../views/{login => auth}/LoginPage.js | 0 .../views/{login => auth}/PasswordLogin.js | 2 +- .../views/{login => auth}/RegistrationForm.js | 2 +- .../views/{login => auth}/ServerConfig.js | 2 +- src/components/views/settings/AddPhoneNumber.js | 2 +- src/stores/LifecycleStore.js | 4 ++-- .../{login => auth}/Registration-test.js | 2 +- .../{login => auth}/RegistrationForm-test.js | 2 +- 26 files changed, 45 insertions(+), 45 deletions(-) rename res/css/structures/{login => auth}/_Login.scss (100%) rename res/css/views/{login => auth}/_InteractiveAuthEntryComponents.scss (100%) rename res/css/views/{login => auth}/_ServerConfig.scss (100%) rename src/components/structures/{login => auth}/ForgotPassword.js (97%) rename src/components/structures/{login => auth}/LanguageSelector.js (100%) rename src/components/structures/{login => auth}/Login.js (98%) rename src/components/structures/{login => auth}/PostRegistration.js (95%) rename src/components/structures/{login => auth}/Registration.js (97%) rename src/components/views/{login => auth}/CaptchaForm.js (100%) rename src/components/views/{login => auth}/CountryDropdown.js (100%) rename src/components/views/{login => auth}/CustomServerDialog.js (100%) rename src/components/views/{login => auth}/InteractiveAuthEntryComponents.js (99%) rename src/components/views/{login => auth}/LoginFooter.js (100%) rename src/components/views/{login => auth}/LoginHeader.js (100%) rename src/components/views/{login => auth}/LoginPage.js (100%) rename src/components/views/{login => auth}/PasswordLogin.js (99%) rename src/components/views/{login => auth}/RegistrationForm.js (99%) rename src/components/views/{login => auth}/ServerConfig.js (98%) rename test/components/structures/{login => auth}/Registration-test.js (98%) rename test/components/views/{login => auth}/RegistrationForm-test.js (97%) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 30cc55377c..42dc3b4810 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -1,11 +1,11 @@ # autogenerated file: run scripts/generate-eslint-error-ignore-file to update. src/component-index.js +src/components/structures/auth/ForgotPassword.js src/components/structures/BottomLeftMenu.js src/components/structures/CompatibilityPage.js src/components/structures/CreateRoom.js src/components/structures/LoggedInView.js -src/components/structures/login/ForgotPassword.js src/components/structures/MessagePanel.js src/components/structures/NotificationPanel.js src/components/structures/RoomDirectory.js @@ -15,6 +15,11 @@ src/components/structures/ScrollPanel.js src/components/structures/SearchBox.js src/components/structures/TimelinePanel.js src/components/structures/UploadBar.js +src/components/views/auth/CountryDropdown.js +src/components/views/auth/InteractiveAuthEntryComponents.js +src/components/views/auth/PasswordLogin.js +src/components/views/auth/RegistrationForm.js +src/components/views/auth/ServerConfig.js src/components/views/avatars/BaseAvatar.js src/components/views/avatars/MemberAvatar.js src/components/views/create_room/RoomAlias.js @@ -31,11 +36,6 @@ src/components/views/elements/UserSelector.js src/components/views/globals/MatrixToolbar.js src/components/views/globals/NewVersionBar.js src/components/views/globals/UpdateCheckBar.js -src/components/views/login/CountryDropdown.js -src/components/views/login/InteractiveAuthEntryComponents.js -src/components/views/login/PasswordLogin.js -src/components/views/login/RegistrationForm.js -src/components/views/login/ServerConfig.js src/components/views/messages/MFileBody.js src/components/views/messages/RoomAvatarEvent.js src/components/views/messages/TextualBody.js @@ -98,12 +98,12 @@ src/VectorConferenceHandler.js src/Velociraptor.js src/WhoIsTyping.js src/wrappers/withMatrixClient.js -test/components/structures/login/Registration-test.js +test/components/structures/auth/Registration-test.js test/components/structures/MessagePanel-test.js test/components/structures/ScrollPanel-test.js test/components/structures/TimelinePanel-test.js +test/components/views/auth/RegistrationForm-test.js test/components/views/dialogs/InteractiveAuthDialog-test.js -test/components/views/login/RegistrationForm-test.js test/components/views/rooms/MessageComposerInput-test.js test/components/views/rooms/RoomSettings-test.js test/mock-clock.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 6b353ba72b..a1b575a0a1 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -23,7 +23,9 @@ @import "./structures/_UploadBar.scss"; @import "./structures/_UserSettings.scss"; @import "./structures/_ViewSource.scss"; -@import "./structures/login/_Login.scss"; +@import "./structures/auth/_Login.scss"; +@import "./views/auth/_InteractiveAuthEntryComponents.scss"; +@import "./views/auth/_ServerConfig.scss"; @import "./views/avatars/_BaseAvatar.scss"; @import "./views/avatars/_MemberStatusMessageAvatar.scss"; @import "./views/context_menus/_MessageContextMenu.scss"; @@ -75,8 +77,6 @@ @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @import "./views/groups/_GroupUserSettings.scss"; -@import "./views/login/_InteractiveAuthEntryComponents.scss"; -@import "./views/login/_ServerConfig.scss"; @import "./views/messages/_CreateEvent.scss"; @import "./views/messages/_DateSeparator.scss"; @import "./views/messages/_MEmoteBody.scss"; diff --git a/res/css/structures/login/_Login.scss b/res/css/structures/auth/_Login.scss similarity index 100% rename from res/css/structures/login/_Login.scss rename to res/css/structures/auth/_Login.scss diff --git a/res/css/views/login/_InteractiveAuthEntryComponents.scss b/res/css/views/auth/_InteractiveAuthEntryComponents.scss similarity index 100% rename from res/css/views/login/_InteractiveAuthEntryComponents.scss rename to res/css/views/auth/_InteractiveAuthEntryComponents.scss diff --git a/res/css/views/login/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss similarity index 100% rename from res/css/views/login/_ServerConfig.scss rename to res/css/views/auth/_ServerConfig.scss diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index 2883c8cca4..89ff4d43a3 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -20,7 +20,7 @@ const InteractiveAuth = Matrix.InteractiveAuth; import React from 'react'; import PropTypes from 'prop-types'; -import {getEntryComponentForLoginType} from '../views/login/InteractiveAuthEntryComponents'; +import {getEntryComponentForLoginType} from '../views/auth/InteractiveAuthEntryComponents'; export default React.createClass({ displayName: 'InteractiveAuth', diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index e5cf6a986a..7acceb4bb7 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -183,7 +183,7 @@ export default React.createClass({ register_is_url: null, register_id_sid: null, - // Parameters used for setting up the login/registration views + // Parameters used for setting up the authentication views defaultServerName: this.props.config.default_server_name, defaultHsUrl: this.props.config.default_hs_url, defaultIsUrl: this.props.config.default_is_url, @@ -1851,7 +1851,7 @@ export default React.createClass({ // needs to be before normal PageTypes as you are logged in technically if (this.state.view === VIEWS.POST_REGISTRATION) { - const PostRegistration = sdk.getComponent('structures.login.PostRegistration'); + const PostRegistration = sdk.getComponent('structures.auth.PostRegistration'); return ( @@ -1906,7 +1906,7 @@ export default React.createClass({ } if (this.state.view === VIEWS.REGISTER) { - const Registration = sdk.getComponent('structures.login.Registration'); + const Registration = sdk.getComponent('structures.auth.Registration'); return ( { err }
    ; } - const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector'); + const LanguageSelector = sdk.getComponent('structures.auth.LanguageSelector'); resetPasswordJsx = (
    diff --git a/src/components/structures/login/LanguageSelector.js b/src/components/structures/auth/LanguageSelector.js similarity index 100% rename from src/components/structures/login/LanguageSelector.js rename to src/components/structures/auth/LanguageSelector.js diff --git a/src/components/structures/login/Login.js b/src/components/structures/auth/Login.js similarity index 98% rename from src/components/structures/login/Login.js rename to src/components/structures/auth/Login.js index 6206b7660d..a1f0327835 100644 --- a/src/components/structures/login/Login.js +++ b/src/components/structures/auth/Login.js @@ -486,7 +486,7 @@ module.exports = React.createClass({ }, _renderPasswordStep: function() { - const PasswordLogin = sdk.getComponent('login.PasswordLogin'); + const PasswordLogin = sdk.getComponent('auth.PasswordLogin'); return (
    : null; const errorText = this.props.defaultServerDiscoveryError || this.state.discoveryError || this.state.errorText; @@ -555,7 +555,7 @@ module.exports = React.createClass({ ); } - const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector'); + const LanguageSelector = sdk.getComponent('structures.auth.LanguageSelector'); return ( diff --git a/src/components/structures/login/PostRegistration.js b/src/components/structures/auth/PostRegistration.js similarity index 95% rename from src/components/structures/login/PostRegistration.js rename to src/components/structures/auth/PostRegistration.js index f6165348bd..826db89b93 100644 --- a/src/components/structures/login/PostRegistration.js +++ b/src/components/structures/auth/PostRegistration.js @@ -60,8 +60,8 @@ module.exports = React.createClass({ render: function() { const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName'); const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); - const LoginPage = sdk.getComponent('login.LoginPage'); - const LoginHeader = sdk.getComponent('login.LoginHeader'); + const LoginPage = sdk.getComponent('auth.LoginPage'); + const LoginHeader = sdk.getComponent('auth.LoginHeader'); return (
    diff --git a/src/components/structures/login/Registration.js b/src/components/structures/auth/Registration.js similarity index 97% rename from src/components/structures/login/Registration.js rename to src/components/structures/auth/Registration.js index 74e3685bb5..617c9d6983 100644 --- a/src/components/structures/login/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -24,7 +24,7 @@ import PropTypes from 'prop-types'; import sdk from '../../../index'; import MatrixClientPeg from '../../../MatrixClientPeg'; -import RegistrationForm from '../../views/login/RegistrationForm'; +import RegistrationForm from '../../views/auth/RegistrationForm'; import RtsClient from '../../../RtsClient'; import { _t, _td } from '../../../languageHandler'; import SdkConfig from '../../../SdkConfig'; @@ -396,12 +396,12 @@ module.exports = React.createClass({ }, render: function() { - const LoginHeader = sdk.getComponent('login.LoginHeader'); - const LoginFooter = sdk.getComponent('login.LoginFooter'); - const LoginPage = sdk.getComponent('login.LoginPage'); + const LoginHeader = sdk.getComponent('auth.LoginHeader'); + const LoginFooter = sdk.getComponent('auth.LoginFooter'); + const LoginPage = sdk.getComponent('auth.LoginPage'); const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth'); const Spinner = sdk.getComponent("elements.Spinner"); - const ServerConfig = sdk.getComponent('views.login.ServerConfig'); + const ServerConfig = sdk.getComponent('views.auth.ServerConfig'); let registerBody; if (this.state.doingUIAuth) { @@ -471,7 +471,7 @@ module.exports = React.createClass({ ); } - const LanguageSelector = sdk.getComponent('structures.login.LanguageSelector'); + const LanguageSelector = sdk.getComponent('structures.auth.LanguageSelector'); return ( diff --git a/src/components/views/login/CaptchaForm.js b/src/components/views/auth/CaptchaForm.js similarity index 100% rename from src/components/views/login/CaptchaForm.js rename to src/components/views/auth/CaptchaForm.js diff --git a/src/components/views/login/CountryDropdown.js b/src/components/views/auth/CountryDropdown.js similarity index 100% rename from src/components/views/login/CountryDropdown.js rename to src/components/views/auth/CountryDropdown.js diff --git a/src/components/views/login/CustomServerDialog.js b/src/components/views/auth/CustomServerDialog.js similarity index 100% rename from src/components/views/login/CustomServerDialog.js rename to src/components/views/auth/CustomServerDialog.js diff --git a/src/components/views/login/InteractiveAuthEntryComponents.js b/src/components/views/auth/InteractiveAuthEntryComponents.js similarity index 99% rename from src/components/views/login/InteractiveAuthEntryComponents.js rename to src/components/views/auth/InteractiveAuthEntryComponents.js index 73b46959b0..6a78898179 100644 --- a/src/components/views/login/InteractiveAuthEntryComponents.js +++ b/src/components/views/auth/InteractiveAuthEntryComponents.js @@ -187,7 +187,7 @@ export const RecaptchaAuthEntry = React.createClass({ return ; } - const CaptchaForm = sdk.getComponent("views.login.CaptchaForm"); + const CaptchaForm = sdk.getComponent("views.auth.CaptchaForm"); const sitePublicKey = this.props.stageParams.public_key; let errorSection; diff --git a/src/components/views/login/LoginFooter.js b/src/components/views/auth/LoginFooter.js similarity index 100% rename from src/components/views/login/LoginFooter.js rename to src/components/views/auth/LoginFooter.js diff --git a/src/components/views/login/LoginHeader.js b/src/components/views/auth/LoginHeader.js similarity index 100% rename from src/components/views/login/LoginHeader.js rename to src/components/views/auth/LoginHeader.js diff --git a/src/components/views/login/LoginPage.js b/src/components/views/auth/LoginPage.js similarity index 100% rename from src/components/views/login/LoginPage.js rename to src/components/views/auth/LoginPage.js diff --git a/src/components/views/login/PasswordLogin.js b/src/components/views/auth/PasswordLogin.js similarity index 99% rename from src/components/views/login/PasswordLogin.js rename to src/components/views/auth/PasswordLogin.js index 59d4db379c..1d36c52a24 100644 --- a/src/components/views/login/PasswordLogin.js +++ b/src/components/views/auth/PasswordLogin.js @@ -201,7 +201,7 @@ class PasswordLogin extends React.Component { disabled={disabled} />; case PasswordLogin.LOGIN_FIELD_PHONE: { - const CountryDropdown = sdk.getComponent('views.login.CountryDropdown'); + const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown'); classes.mx_Login_phoneNumberField = true; classes.mx_Login_field_has_prefix = true; classes.error = this.props.loginIncorrect && !this.state.phoneNumber; diff --git a/src/components/views/login/RegistrationForm.js b/src/components/views/auth/RegistrationForm.js similarity index 99% rename from src/components/views/login/RegistrationForm.js rename to src/components/views/auth/RegistrationForm.js index 137aeada91..a0fc79b947 100644 --- a/src/components/views/login/RegistrationForm.js +++ b/src/components/views/auth/RegistrationForm.js @@ -323,7 +323,7 @@ module.exports = React.createClass({ } } - const CountryDropdown = sdk.getComponent('views.login.CountryDropdown'); + const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown'); let phoneSection; if (!SdkConfig.get().disable_3pid_login) { const phonePlaceholder = this._authStepIsRequired('m.login.msisdn') ? _t("Mobile phone number") : _t("Mobile phone number (optional)"); diff --git a/src/components/views/login/ServerConfig.js b/src/components/views/auth/ServerConfig.js similarity index 98% rename from src/components/views/login/ServerConfig.js rename to src/components/views/auth/ServerConfig.js index 2f04011273..57535e80d8 100644 --- a/src/components/views/login/ServerConfig.js +++ b/src/components/views/auth/ServerConfig.js @@ -138,7 +138,7 @@ module.exports = React.createClass({ }, showHelpPopup: function() { - const CustomServerDialog = sdk.getComponent('login.CustomServerDialog'); + const CustomServerDialog = sdk.getComponent('auth.CustomServerDialog'); Modal.createTrackedDialog('Custom Server Dialog', '', CustomServerDialog); }, diff --git a/src/components/views/settings/AddPhoneNumber.js b/src/components/views/settings/AddPhoneNumber.js index 82169c9868..27164e6517 100644 --- a/src/components/views/settings/AddPhoneNumber.js +++ b/src/components/views/settings/AddPhoneNumber.js @@ -141,7 +141,7 @@ export default withMatrixClient(React.createClass({ return
    ; } - const CountryDropdown = sdk.getComponent('views.login.CountryDropdown'); + const CountryDropdown = sdk.getComponent('views.auth.CountryDropdown'); // XXX: This CSS relies on the CSS surrounding it in UserSettings as its in // a tabular format to align the submit buttons return ( diff --git a/src/stores/LifecycleStore.js b/src/stores/LifecycleStore.js index 2ce3be5a33..fcdfe93cf9 100644 --- a/src/stores/LifecycleStore.js +++ b/src/stores/LifecycleStore.js @@ -22,8 +22,8 @@ const INITIAL_STATE = { }; /** - * A class for storing application state to do with login/registration. This is a simple - * flux store that listens for actions and updates its state accordingly, informing any + * A class for storing application state to do with authentication. This is a simple flux + * store that listens for actions and updates its state accordingly, informing any * listeners (views) of state changes. */ class LifecycleStore extends Store { diff --git a/test/components/structures/login/Registration-test.js b/test/components/structures/auth/Registration-test.js similarity index 98% rename from test/components/structures/login/Registration-test.js rename to test/components/structures/auth/Registration-test.js index 7287bb0d95..4827bf4c12 100644 --- a/test/components/structures/login/Registration-test.js +++ b/test/components/structures/auth/Registration-test.js @@ -23,7 +23,7 @@ const expect = require('expect'); const testUtils = require('test-utils'); const sdk = require('matrix-react-sdk'); -const Registration = sdk.getComponent('structures.login.Registration'); +const Registration = sdk.getComponent('structures.auth.Registration'); let rtsClient; let client; diff --git a/test/components/views/login/RegistrationForm-test.js b/test/components/views/auth/RegistrationForm-test.js similarity index 97% rename from test/components/views/login/RegistrationForm-test.js rename to test/components/views/auth/RegistrationForm-test.js index 2d1c1be026..265f158bb9 100644 --- a/test/components/views/login/RegistrationForm-test.js +++ b/test/components/views/auth/RegistrationForm-test.js @@ -23,7 +23,7 @@ const expect = require('expect'); const testUtils = require('test-utils'); const sdk = require('matrix-react-sdk'); -const RegistrationForm = sdk.getComponent('views.login.RegistrationForm'); +const RegistrationForm = sdk.getComponent('views.auth.RegistrationForm'); const TEAM_CONFIG = { supportEmail: "support@some.domain", From 0e42c0892e046ce202871604eee3b546e247d026 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 13:09:17 -0700 Subject: [PATCH 36/74] Early modalization of UserSettings Basically just shove it into a modal and call it good. --- src/components/structures/MatrixChat.js | 8 ++- .../views/dialogs/UserSettingsDialog.js | 49 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/components/views/dialogs/UserSettingsDialog.js diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 7acceb4bb7..25ba980bcf 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -611,8 +611,12 @@ export default React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': - this._setPage(PageTypes.UserSettings); - this.notifyNewScreen('settings'); + const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, { + title: _t("Settings"), + }); + //this._setPage(PageTypes.UserSettings); + //this.notifyNewScreen('settings'); break; case 'close_settings': this.setState({ diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js new file mode 100644 index 0000000000..d12895010c --- /dev/null +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -0,0 +1,49 @@ +/* +Copyright 2019 New Vector 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 PropTypes from 'prop-types'; +import sdk from '../../../index'; +import {_t} from '../../../languageHandler'; +import SdkConfig from "../../../SdkConfig"; + +export default React.createClass({ + propTypes: { + onFinished: PropTypes.func.isRequired, + }, + + render: function () { + const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); + const UserSettings = sdk.getComponent('structures.UserSettings'); + + return ( + +
    + +
    +
    + ); + }, +}); From 5adfc09237cb0eead02a8b2a9cf8fb2f2332a5fc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 13:43:17 -0700 Subject: [PATCH 37/74] Bring in TabbedView nearly verbatim from prior work Sourced from https://github.com/matrix-org/matrix-react-sdk/pull/1644 and related PRs. --- res/css/_components.scss | 1 + res/css/structures/_TabbedView.scss | 76 ++++++++ res/themes/dharma/css/_dharma.scss | 8 + res/themes/light/css/_base.scss | 9 + src/components/structures/MatrixChat.js | 4 +- src/components/structures/TabbedView.js | 165 ++++++++++++++++++ .../views/dialogs/UserSettingsDialog.js | 45 +++-- src/i18n/strings/en_EN.json | 4 +- 8 files changed, 284 insertions(+), 28 deletions(-) create mode 100644 res/css/structures/_TabbedView.scss create mode 100644 src/components/structures/TabbedView.js diff --git a/res/css/_components.scss b/res/css/_components.scss index a1b575a0a1..1b35332711 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -18,6 +18,7 @@ @import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; @import "./structures/_SearchBox.scss"; +@import "./structures/_TabbedView.scss"; @import "./structures/_TagPanel.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss new file mode 100644 index 0000000000..7d42823d17 --- /dev/null +++ b/res/css/structures/_TabbedView.scss @@ -0,0 +1,76 @@ +/* +Copyright 2017 Travis Ralston + +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_TabbedView { + margin: 0; + padding: 0; + display: flex; + width: 100%; + height: 100%; + background-color: $tab-panel-bg-color; +} + +.mx_TabbedView_tabLabels { + width: 300px; + height: 100%; + background-color: $tab-list-bg-color; + color: $tab-list-fg-color; + border-right: 1px solid $tab-border-color; + border-left: 1px solid $tab-border-color; +} + +.mx_TabbedView_tabPanels { + width: calc(100% - 320px); + display: inline-block; + height: 100%; + padding-left: 20px; + scroll-snap-type: block; +} + +.mx_TabbedView_tabLabel { + text-align: center; + vertical-align: middle; + text-transform: uppercase; + cursor: pointer; + display: block; + padding: 20px; + width: calc(100% - 40px); + border-bottom: 1px solid $tab-border-color; +} + +.mx_TabbedView_exit { + padding-top: 10px; + padding-bottom: 10px; +} + +.mx_TabbedView_tabLabel:hover { + font-weight: 700; +} + +.mx_TabbedView_tabLabel_active { + font-weight: 700; + background-color: $tab-list-active-bg-color; + color: $tab-list-active-fg-color; +} + +.mx_TabbedView_tabPanel { + height: 100vh; // 100% of viewport height + scroll-snap-align: start; +} + +.mx_TabbedView_tabPanelContent { + width: 600px; +} \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index c9f62fbe6b..8f369a3b33 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -186,6 +186,14 @@ $lightbox-bg-color: #454545; $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; +// Tabbed views +$tab-list-bg-color: $secondary-accent-color; +$tab-list-fg-color: $accent-color; +$tab-list-active-bg-color: $tertiary-accent-color; +$tab-list-active-fg-color: $accent-color; +$tab-border-color: $tertiary-accent-color; +$tab-panel-bg-color: #fff; + // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index dbab909f13..996fe965cf 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -66,6 +66,7 @@ $primary-hairline-color: #e5e5e5; // used for the border of input text fields $input-border-color: #f0f0f0; +$input-border-dark-color: #b8b8b8; $input-darker-bg-color: #c1c9d6; $input-darker-fg-color: #9fa9ba; @@ -181,6 +182,14 @@ $imagebody-giflabel: rgba(0, 0, 0, 0.7); $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); $imagebody-giflabel-color: rgba(255, 255, 255, 1); +// Tabbed views +$tab-list-bg-color: $secondary-accent-color; +$tab-list-fg-color: $accent-color; +$tab-list-active-bg-color: $tertiary-accent-color; +$tab-list-active-fg-color: $accent-color; +$tab-border-color: $tertiary-accent-color; +$tab-panel-bg-color: #fff; + // unused? $progressbar-color: #000; diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 25ba980bcf..9733576bd0 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -612,9 +612,7 @@ export default React.createClass({ break; case 'view_user_settings': const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, { - title: _t("Settings"), - }); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js new file mode 100644 index 0000000000..44ecee7a95 --- /dev/null +++ b/src/components/structures/TabbedView.js @@ -0,0 +1,165 @@ +/* +Copyright 2017 Travis Ralston +Copyright 2019 New Vector 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 * as React from "react"; +import {_t, _td} from '../../languageHandler'; +import GeminiScrollbar from 'react-gemini-scrollbar'; +import PropTypes from "prop-types"; +//import scrollSnapPolyfill from 'css-scroll-snap-polyfill'; + +const DEFAULT_EXIT_STRING = _td("Return to app"); + +/** + * Represents a tab for the TabbedView + */ +export class Tab { + /** + * Creates a new tab + * @param {string} tabLabel The untranslated tab label + * @param {string} tabJsx The JSX for the tab container. + */ + constructor(tabLabel, tabJsx) { + this.label = tabLabel; + this.body = tabJsx; + } +} + +export class TabbedView extends React.Component { + constructor() { + super(); + + // This is used to track when the user has scrolled all the way up or down so we + // don't immediately start flipping between tabs. + this._reachedEndAt = 0; + } + + getInitialState() { + return { + activeTabIndex: 0, + }; + } + + _getActiveTabIndex() { + return this.state ? this.state.activeTabIndex : 0; + } + + /** + * Shows the given tab + * @param {Tab} tab the tab to show + * @private + */ + _setActiveTab(tab) { + const idx = this.props.tabs.indexOf(tab); + if (idx !== -1) { + this.setState({activeTabIndex: idx}); + this._reachedEndAt = 0; // reset scroll timer + } + else console.error("Could not find tab " + tab.label + " in tabs"); + } + + _nextTab() { + let targetIndex = this._getActiveTabIndex() + 1; + if (targetIndex < this.props.tabs.length) { + this.setState({activeTabIndex: targetIndex}); + this._reachedEndAt = 0; // reset scroll timer + } + } + + _previousTab() { + let targetIndex = this._getActiveTabIndex() - 1; + if (targetIndex >= 0) { + this.setState({activeTabIndex: targetIndex}); + this._reachedEndAt = 0; // reset scroll timer + } + } + + _getTabLabel(tab) { + let classes = "mx_TabbedView_tabLabel "; + + const idx = this.props.tabs.indexOf(tab); + if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + + return ( + this._setActiveTab(tab)}> + {_t(tab.label)} + + ); + } + + _getTabPanel(tab) { + return ( +
    + {tab.body} +
    + ); + } + + componentDidUpdate() { + window.requestAnimationFrame(() => { + console.log("SCROLL SNAP POLYFILL: UPDATE"); + //scrollSnapPolyfill(); + }); + } + + componentDidMount() { + window.requestAnimationFrame(() => { + console.log("SCROLL SNAP POLYFILL: MOUNT"); + //scrollSnapPolyfill(); + }); + } + + render() { + const labels = []; + const tabs = []; + + for (const tab of this.props.tabs) { + labels.push(this._getTabLabel(tab)); + tabs.push(this._getTabPanel(tab)); + } + + const returnToApp = ( + + {_t(this.props.exitLabel || DEFAULT_EXIT_STRING)} + + ); + + return ( +
    +
    + {returnToApp} + {labels} +
    +
    + {tabs} +
    +
    + ); + } +} + +TabbedView.PropTypes = { + // Called when the user clicks the "Exit" or "Return to app" button + onExit: PropTypes.func.isRequired, + + // The untranslated label for the "Return to app" button. + // Default: "Return to app" + exitLabel: PropTypes.string, + + // The tabs to show + tabs: PropTypes.arrayOf(Tab).isRequired, +}; \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d12895010c..f04b92b6cc 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -17,33 +17,30 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import sdk from '../../../index'; -import {_t} from '../../../languageHandler'; -import SdkConfig from "../../../SdkConfig"; +import {Tab, TabbedView} from "../../structures/TabbedView"; +import {_td} from "../../../languageHandler"; -export default React.createClass({ - propTypes: { +export default class UserSettingsDialog extends React.Component { + static propTypes = { onFinished: PropTypes.func.isRequired, - }, + }; - render: function () { - const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); - const UserSettings = sdk.getComponent('structures.UserSettings'); + _getTabs() { + return [ + new Tab(_td("General"),
    General Test
    ), + new Tab(_td("Account"),
    Account Test
    ), + ]; + } + render() { return ( - -
    - -
    -
    + + // ); - }, -}); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index cfd5168152..cc18731ee0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1043,6 +1043,8 @@ "Room contains unknown devices": "Room contains unknown devices", "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", "Unknown devices": "Unknown devices", + "General": "General", + "Account": "Account", "Unable to load backup status": "Unable to load backup status", "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", @@ -1222,6 +1224,7 @@ "Click to unmute audio": "Click to unmute audio", "Click to mute audio": "Click to mute audio", "Filter room names": "Filter room names", + "Return to app": "Return to app", "Clear filter": "Clear filter", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", @@ -1286,7 +1289,6 @@ "Add email address": "Add email address", "Profile": "Profile", "Display name": "Display name", - "Account": "Account", "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", "Logged in as:": "Logged in as:", "Access Token:": "Access Token:", From 15709040e7b5c821ea04fdca76b4a4df05771a28 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 15:07:11 -0700 Subject: [PATCH 38/74] Make tabs be their own panels --- res/css/structures/_TabbedView.scss | 42 +++---- res/themes/dharma/css/_dharma.scss | 10 +- res/themes/light/css/_base.scss | 10 +- src/components/structures/TabbedView.js | 103 ++++-------------- .../views/dialogs/UserSettingsDialog.js | 7 +- 5 files changed, 46 insertions(+), 126 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 7d42823d17..81a3af06a9 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -1,5 +1,6 @@ /* Copyright 2017 Travis Ralston +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,40 +21,22 @@ limitations under the License. display: flex; width: 100%; height: 100%; - background-color: $tab-panel-bg-color; } .mx_TabbedView_tabLabels { - width: 300px; + width: 136px; height: 100%; - background-color: $tab-list-bg-color; - color: $tab-list-fg-color; - border-right: 1px solid $tab-border-color; - border-left: 1px solid $tab-border-color; -} - -.mx_TabbedView_tabPanels { - width: calc(100% - 320px); - display: inline-block; - height: 100%; - padding-left: 20px; - scroll-snap-type: block; + color: $tab-label-fg-color; } .mx_TabbedView_tabLabel { - text-align: center; vertical-align: middle; - text-transform: uppercase; cursor: pointer; display: block; - padding: 20px; - width: calc(100% - 40px); - border-bottom: 1px solid $tab-border-color; -} - -.mx_TabbedView_exit { - padding-top: 10px; - padding-bottom: 10px; + border-radius: 3px; + font-size: 12px; + font-weight: 600; + height: 20px; } .mx_TabbedView_tabLabel:hover { @@ -61,16 +44,17 @@ limitations under the License. } .mx_TabbedView_tabLabel_active { - font-weight: 700; - background-color: $tab-list-active-bg-color; - color: $tab-list-active-fg-color; + background-color: $tab-label-active-bg-color; + color: $tab-label-active-fg-color; } .mx_TabbedView_tabPanel { + width: calc(100% - 320px); + display: inline-block; height: 100vh; // 100% of viewport height - scroll-snap-align: start; + margin-left: 20px; } .mx_TabbedView_tabPanelContent { - width: 600px; + flex-grow: 1; } \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 8f369a3b33..e95ad82daf 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -187,12 +187,10 @@ $lightbox-fg-color: #ffffff; $lightbox-border-color: #ffffff; // Tabbed views -$tab-list-bg-color: $secondary-accent-color; -$tab-list-fg-color: $accent-color; -$tab-list-active-bg-color: $tertiary-accent-color; -$tab-list-active-fg-color: $accent-color; -$tab-border-color: $tertiary-accent-color; -$tab-panel-bg-color: #fff; +$tab-label-fg-color: #45474a; +$tab-label-active-fg-color: #ffffff; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: #7ac9a1; // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 996fe965cf..852442c4d1 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -183,12 +183,10 @@ $imagebody-giflabel-border: rgba(0, 0, 0, 0.2); $imagebody-giflabel-color: rgba(255, 255, 255, 1); // Tabbed views -$tab-list-bg-color: $secondary-accent-color; -$tab-list-fg-color: $accent-color; -$tab-list-active-bg-color: $tertiary-accent-color; -$tab-list-active-fg-color: $accent-color; -$tab-border-color: $tertiary-accent-color; -$tab-panel-bg-color: #fff; +$tab-label-fg-color: #45474a; +$tab-label-active-fg-color: #ffffff; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: #7ac9a1; // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 44ecee7a95..047d48e808 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -16,45 +16,42 @@ limitations under the License. */ import * as React from "react"; -import {_t, _td} from '../../languageHandler'; -import GeminiScrollbar from 'react-gemini-scrollbar'; +import {_t} from '../../languageHandler'; import PropTypes from "prop-types"; -//import scrollSnapPolyfill from 'css-scroll-snap-polyfill'; - -const DEFAULT_EXIT_STRING = _td("Return to app"); /** - * Represents a tab for the TabbedView + * Represents a tab for the TabbedView. */ export class Tab { /** - * Creates a new tab - * @param {string} tabLabel The untranslated tab label + * Creates a new tab. + * @param {string} tabLabel The untranslated tab label. + * @param {string} tabIconRef The relative path to the tab's icon. * @param {string} tabJsx The JSX for the tab container. */ - constructor(tabLabel, tabJsx) { + constructor(tabLabel, tabIconRef, tabJsx) { this.label = tabLabel; this.body = tabJsx; } } export class TabbedView extends React.Component { + static propTypes = { + // The tabs to show + tabs: PropTypes.arrayOf(Tab).isRequired, + }; + constructor() { super(); - // This is used to track when the user has scrolled all the way up or down so we - // don't immediately start flipping between tabs. - this._reachedEndAt = 0; - } - - getInitialState() { - return { + this.state = { activeTabIndex: 0, }; } _getActiveTabIndex() { - return this.state ? this.state.activeTabIndex : 0; + if (!this.state || !this.state.activeTabIndex) return 0; + return this.state.activeTabIndex; } /** @@ -66,28 +63,12 @@ export class TabbedView extends React.Component { const idx = this.props.tabs.indexOf(tab); if (idx !== -1) { this.setState({activeTabIndex: idx}); - this._reachedEndAt = 0; // reset scroll timer - } - else console.error("Could not find tab " + tab.label + " in tabs"); - } - - _nextTab() { - let targetIndex = this._getActiveTabIndex() + 1; - if (targetIndex < this.props.tabs.length) { - this.setState({activeTabIndex: targetIndex}); - this._reachedEndAt = 0; // reset scroll timer + } else { + console.error("Could not find tab " + tab.label + " in tabs"); } } - _previousTab() { - let targetIndex = this._getActiveTabIndex() - 1; - if (targetIndex >= 0) { - this.setState({activeTabIndex: targetIndex}); - this._reachedEndAt = 0; // reset scroll timer - } - } - - _getTabLabel(tab) { + _renderTabLabel(tab) { let classes = "mx_TabbedView_tabLabel "; const idx = this.props.tabs.indexOf(tab); @@ -101,7 +82,7 @@ export class TabbedView extends React.Component { ); } - _getTabPanel(tab) { + _renderTabPanel(tab) { return (
    {tab.body} @@ -109,57 +90,17 @@ export class TabbedView extends React.Component { ); } - componentDidUpdate() { - window.requestAnimationFrame(() => { - console.log("SCROLL SNAP POLYFILL: UPDATE"); - //scrollSnapPolyfill(); - }); - } - - componentDidMount() { - window.requestAnimationFrame(() => { - console.log("SCROLL SNAP POLYFILL: MOUNT"); - //scrollSnapPolyfill(); - }); - } - render() { - const labels = []; - const tabs = []; - - for (const tab of this.props.tabs) { - labels.push(this._getTabLabel(tab)); - tabs.push(this._getTabPanel(tab)); - } - - const returnToApp = ( - - {_t(this.props.exitLabel || DEFAULT_EXIT_STRING)} - - ); + const labels = this.props.tabs.map(tab => this._renderTabLabel(tab)); + const panel = this._renderTabPanel(this.props.tabs[this._getActiveTabIndex()]); return (
    - {returnToApp} {labels}
    -
    - {tabs} -
    + {panel}
    ); } -} - -TabbedView.PropTypes = { - // Called when the user clicks the "Exit" or "Return to app" button - onExit: PropTypes.func.isRequired, - - // The untranslated label for the "Return to app" button. - // Default: "Return to app" - exitLabel: PropTypes.string, - - // The tabs to show - tabs: PropTypes.arrayOf(Tab).isRequired, -}; \ No newline at end of file +} \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index f04b92b6cc..e54bc9c857 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import sdk from '../../../index'; import {Tab, TabbedView} from "../../structures/TabbedView"; import {_td} from "../../../languageHandler"; @@ -27,14 +26,14 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"),
    General Test
    ), - new Tab(_td("Account"),
    Account Test
    ), + new Tab(_td("General"), "",
    General Test
    ), + new Tab(_td("Account"), "",
    Account Test
    ), ]; } render() { return ( - + // Date: Fri, 18 Jan 2019 19:40:21 -0700 Subject: [PATCH 39/74] Make the tabs look like the design --- res/css/_components.scss | 1 + res/css/structures/_TabbedView.scss | 39 +++++++++++++++++-- .../views/dialogs/_UserSettingsDialog.scss | 26 +++++++++++++ res/themes/dharma/css/_dharma.scss | 2 + res/themes/light/css/_base.scss | 2 + src/components/structures/TabbedView.js | 15 +++++-- .../views/dialogs/UserSettingsDialog.js | 20 ++++++++-- src/i18n/strings/en_EN.json | 7 +++- 8 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 res/css/views/dialogs/_UserSettingsDialog.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 1b35332711..e60e74383f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -52,6 +52,7 @@ @import "./views/dialogs/_SetPasswordDialog.scss"; @import "./views/dialogs/_ShareDialog.scss"; @import "./views/dialogs/_UnknownDeviceDialog.scss"; +@import "./views/dialogs/_UserSettingsDialog.scss"; @import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss"; @import "./views/dialogs/keybackup/_KeyBackupFailedDialog.scss"; @import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 81a3af06a9..32174a0ef5 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -39,15 +39,46 @@ limitations under the License. height: 20px; } -.mx_TabbedView_tabLabel:hover { - font-weight: 700; -} - .mx_TabbedView_tabLabel_active { background-color: $tab-label-active-bg-color; color: $tab-label-active-fg-color; } +.mx_TabbedView_tabLabel_icon { + width: 12px; + height: 12px; + margin-left: 6px; + margin-right: 9px; + position: relative; +} + +.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { + width: 12px; + height: 12px; + display: inline-block; +} + +.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { + background-color: $tab-label-icon-bg-color; + mask-repeat: no-repeat; + mask-size: 12px; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.mx_TabbedView_tabLabel_active .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { + background-color: $tab-label-active-icon-bg-color; +} + +.mx_TabbedView_tabLabel_text { + vertical-align: middle; +} + .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss new file mode 100644 index 0000000000..7d8c80e5bd --- /dev/null +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -0,0 +1,26 @@ +.mx_UserSettingsDialog_settingsIcon:before { + mask: url('$(res)/img/feather-icons/settings.svg'); +} + +.mx_UserSettingsDialog_voiceIcon:before { + mask: url('$(res)/img/feather-icons/phone.svg'); +} + +.mx_UserSettingsDialog_bellIcon:before { + mask: url('$(res)/img/feather-icons/notifications.svg'); +} + +.mx_UserSettingsDialog_preferencesIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/paperclip.svg'); +} + +.mx_UserSettingsDialog_securityIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/life-buoy.svg'); +} + +.mx_UserSettingsDialog_helpIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/share.svg'); +} diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index e95ad82daf..5f405e49e9 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -191,6 +191,8 @@ $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; $tab-label-active-bg-color: #7ac9a1; +$tab-label-icon-bg-color: #454545; +$tab-label-active-icon-bg-color: #ffffff; // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 852442c4d1..69f1b61f5f 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -187,6 +187,8 @@ $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; $tab-label-active-bg-color: #7ac9a1; +$tab-label-icon-bg-color: #454545; +$tab-label-active-icon-bg-color: #ffffff; // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 047d48e808..48f8c7a4d8 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -26,11 +26,12 @@ export class Tab { /** * Creates a new tab. * @param {string} tabLabel The untranslated tab label. - * @param {string} tabIconRef The relative path to the tab's icon. + * @param {string} tabIconJsx The JSX for the tab icon. This should be a plain img element or null. * @param {string} tabJsx The JSX for the tab container. */ - constructor(tabLabel, tabIconRef, tabJsx) { + constructor(tabLabel, tabIconJsx, tabJsx) { this.label = tabLabel; + this.icon = tabIconJsx; this.body = tabJsx; } } @@ -74,10 +75,18 @@ export class TabbedView extends React.Component { const idx = this.props.tabs.indexOf(tab); if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + let tabIcon = null; + if (tab.icon) { + tabIcon = {tab.icon}; + } + return ( this._setActiveTab(tab)}> - {_t(tab.label)} + {tabIcon} + + {_t(tab.label)} + ); } diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index e54bc9c857..189d5871e3 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {Tab, TabbedView} from "../../structures/TabbedView"; -import {_td} from "../../../languageHandler"; +import {_t, _td} from "../../../languageHandler"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -26,14 +26,26 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), "",
    General Test
    ), - new Tab(_td("Account"), "",
    Account Test
    ), + new Tab(_td("General"), ,
    General Test
    ), + new Tab(_td("Notifications"), ,
    Notifications Test
    ), + new Tab(_td("Preferences"), ,
    Preferences Test
    ), + new Tab(_td("Voice & Video"), ,
    Voice Test
    ), + new Tab(_td("Security & Privacy"), ,
    Security Test
    ), + new Tab(_td("Help & About"), ,
    Help Test
    ), ]; } render() { return ( - +
    +

    + {_t("Settings")} +

    + + X + + +
    // Date: Fri, 18 Jan 2019 20:09:23 -0700 Subject: [PATCH 40/74] Make the dialog look more like a new dialog --- res/css/_common.scss | 4 +- res/css/structures/_TabbedView.scss | 2 +- .../views/dialogs/_UserSettingsDialog.scss | 43 ++++++++++++++++++- res/themes/dharma/css/_dharma.scss | 5 +++ res/themes/light/css/_base.scss | 5 +++ .../views/dialogs/UserSettingsDialog.js | 11 ++--- 6 files changed, 61 insertions(+), 9 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index bec4c02c18..306834bcde 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -160,7 +160,7 @@ textarea { padding: 0 58px 36px; width: 60%; max-width: 704px; - box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.2); + box-shadow: 2px 15px 30px 0 $dialog-shadow-color; max-height: 80%; overflow-y: auto; } @@ -171,7 +171,7 @@ textarea { left: 0; width: 100%; height: 100%; - background-color: $dialog-background-bg-color; + background-color: $dialog-backdrop-color; opacity: 0.8; } diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 32174a0ef5..957e989f53 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -82,8 +82,8 @@ limitations under the License. .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; - height: 100vh; // 100% of viewport height margin-left: 20px; + flex-grow: 1; } .mx_TabbedView_tabPanelContent { diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 7d8c80e5bd..e98001fea4 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -1,3 +1,44 @@ +.mx_UserSettingsDialog_header { + font-size: 24px; + display: block; + text-align: center; + color: $dialog-title-fg-color; + margin-top: 23px; + margin-bottom: 32px; + padding: 0; +} + +.mx_UserSettingsDialog_close { + position: absolute; + top: 23px; + right: 25px; +} + +.mx_UserSettingsDialog_closeIcon { + width: 14px; + height: 14px; + display: inline-block; +} + +.mx_UserSettingsDialog_closeIcon:before { + // TODO: Use real icon + mask: url('$(res)/img/feather-icons/video.svg'); + background-color: $dialog-close-fg-color; + mask-repeat: no-repeat; + mask-size: 14px; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + + +// ICONS +// ========================================================== + .mx_UserSettingsDialog_settingsIcon:before { mask: url('$(res)/img/feather-icons/settings.svg'); } @@ -23,4 +64,4 @@ .mx_UserSettingsDialog_helpIcon:before { // TODO: Use real icon mask: url('$(res)/img/feather-icons/share.svg'); -} +} \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 5f405e49e9..cf3738a1bb 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -96,6 +96,11 @@ $avatar-bg-color: #ffffff; $h3-color: #3d3b39; +$dialog-title-fg-color: #454545; +$dialog-backdrop-color: rgba(46, 48, 51, 0.38); +$dialog-shadow-color: rgba(0, 0, 0, 0.48); +$dialog-close-fg-color: #9fa9ba; + $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 69f1b61f5f..45b64243c6 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -93,6 +93,11 @@ $avatar-bg-color: #ffffff; $h3-color: #3d3b39; +$dialog-title-fg-color: #454545; +$dialog-backdrop-color: rgba(46, 48, 51, 0.38); +$dialog-shadow-color: rgba(0, 0, 0, 0.48); +$dialog-close-fg-color: #9fa9ba; + $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 189d5871e3..1896bd38e0 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -18,6 +18,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import {Tab, TabbedView} from "../../structures/TabbedView"; import {_t, _td} from "../../../languageHandler"; +import AccessibleButton from "../elements/AccessibleButton"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -38,12 +39,12 @@ export default class UserSettingsDialog extends React.Component { render() { return (
    -

    +
    {_t("Settings")} -

    - - X - + + + +
    // Date: Fri, 18 Jan 2019 20:22:36 -0700 Subject: [PATCH 41/74] Very early work on the "General" tab --- res/css/_components.scss | 2 + res/css/structures/_TabbedView.scss | 2 +- .../views/dialogs/_UserSettingsDialog.scss | 2 +- .../settings/tabs/_GeneralSettingsTab.scss | 6 +++ res/css/views/settings/tabs/_SettingsTab.scss | 13 +++++ .../views/dialogs/UserSettingsDialog.js | 3 +- .../views/settings/tabs/GeneralSettingsTab.js | 53 +++++++++++++++++++ 7 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 res/css/views/settings/tabs/_GeneralSettingsTab.scss create mode 100644 res/css/views/settings/tabs/_SettingsTab.scss create mode 100644 src/components/views/settings/tabs/GeneralSettingsTab.js diff --git a/res/css/_components.scss b/res/css/_components.scss index e60e74383f..c1e2cc29cd 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -124,6 +124,8 @@ @import "./views/settings/_IntegrationsManager.scss"; @import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; +@import "./views/settings/tabs/_GeneralSettingsTab.scss"; +@import "./views/settings/tabs/_SettingsTab.scss"; @import "./views/voip/_CallView.scss"; @import "./views/voip/_IncomingCallbox.scss"; @import "./views/voip/_VideoView.scss"; diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 957e989f53..322e763000 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -82,7 +82,7 @@ limitations under the License. .mx_TabbedView_tabPanel { width: calc(100% - 320px); display: inline-block; - margin-left: 20px; + margin-left: 70px; flex-grow: 1; } diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index e98001fea4..75d89a1d06 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -4,7 +4,7 @@ text-align: center; color: $dialog-title-fg-color; margin-top: 23px; - margin-bottom: 32px; + margin-bottom: 24px; padding: 0; } diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss new file mode 100644 index 0000000000..b04975fc2d --- /dev/null +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -0,0 +1,6 @@ +.mx_GeneralSettingsTab_profile input { + display: block; + font-size: 14px; + padding: 5px; + border-radius: 4px; +} \ No newline at end of file diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss new file mode 100644 index 0000000000..13e8b0ab51 --- /dev/null +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -0,0 +1,13 @@ +.mx_SettingsTab_heading { + font-size: 20px; + font-weight: 600; +} + +.mx_SettingsTab_subheading { + font-size: 14px; + display: block; +} + +.mx_SettingsTab_section { + margin-top: 10px; +} \ No newline at end of file diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index 1896bd38e0..b5450c01b5 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -19,6 +19,7 @@ import PropTypes from 'prop-types'; import {Tab, TabbedView} from "../../structures/TabbedView"; import {_t, _td} from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; +import GeneralSettingsTab from "../settings/tabs/GeneralSettingsTab"; export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -27,7 +28,7 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), ,
    General Test
    ), + new Tab(_td("General"), , ), new Tab(_td("Notifications"), ,
    Notifications Test
    ), new Tab(_td("Preferences"), ,
    Preferences Test
    ), new Tab(_td("Voice & Video"), ,
    Voice Test
    ), diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js new file mode 100644 index 0000000000..60f557d918 --- /dev/null +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -0,0 +1,53 @@ +/* +Copyright 2019 New Vector 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 PropTypes from 'prop-types'; +import {_t} from "../../../../languageHandler"; + +export default class GeneralSettingsTab extends React.Component { + static propTypes = { + onFinished: PropTypes.func.isRequired, + }; + + render() { + return ( +
    +
    {_t("General")}
    +
    + {_t("Profile")} + + +
    +
    + {_t("Profile")} + + +
    +
    + {_t("Profile")} + + +
    +
    + {_t("Profile")} + + +
    +
    + ); + } +} From d907647e61b8d3e5664c2a4c6a8eb6e8964edfbf Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 20:25:44 -0700 Subject: [PATCH 42/74] Spread the tabs out a bit --- res/css/structures/_TabbedView.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 322e763000..ecb7193c18 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -37,6 +37,7 @@ limitations under the License. font-size: 12px; font-weight: 600; height: 20px; + margin-bottom: 6px; } .mx_TabbedView_tabLabel_active { From a8ec40a8b06f6e6472a24b49f6aedab3b5efa4ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 18 Jan 2019 20:36:02 -0700 Subject: [PATCH 43/74] Add a temporary tab for visiting the old settings For debugging purposes --- src/components/structures/MatrixChat.js | 4 ++++ .../views/dialogs/UserSettingsDialog.js | 18 ++++++++++++++++ .../views/settings/tabs/GeneralSettingsTab.js | 21 +++++++------------ src/i18n/strings/en_EN.json | 5 +++-- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 9733576bd0..2b585506ae 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -616,6 +616,10 @@ export default React.createClass({ //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; + case 'view_old_user_settings': + this._setPage(PageTypes.UserSettings); + this.notifyNewScreen('settings'); + break; case 'close_settings': this.setState({ leftDisabled: false, diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index b5450c01b5..d355eb77bc 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -20,6 +20,23 @@ import {Tab, TabbedView} from "../../structures/TabbedView"; import {_t, _td} from "../../../languageHandler"; import AccessibleButton from "../elements/AccessibleButton"; import GeneralSettingsTab from "../settings/tabs/GeneralSettingsTab"; +import dis from '../../../dispatcher'; + +export class TempTab extends React.Component { + // TODO: Ditch this + static propTypes = { + onClose: PropTypes.func.isRequired, + }; + + componentDidMount(): void { + dis.dispatch({action: "view_old_user_settings"}); + this.props.onClose(); + } + + render() { + return
    Hello World
    ; + } +} export default class UserSettingsDialog extends React.Component { static propTypes = { @@ -34,6 +51,7 @@ export default class UserSettingsDialog extends React.Component { new Tab(_td("Voice & Video"), ,
    Voice Test
    ), new Tab(_td("Security & Privacy"), ,
    Security Test
    ), new Tab(_td("Help & About"), ,
    Help Test
    ), + new Tab(_td("Visit old settings"), , ), ]; } diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 60f557d918..433b12a674 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -15,37 +15,32 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import {_t} from "../../../../languageHandler"; export default class GeneralSettingsTab extends React.Component { - static propTypes = { - onFinished: PropTypes.func.isRequired, - }; - render() { return (
    {_t("General")}
    {_t("Profile")} - - + +
    {_t("Profile")} - - + +
    {_t("Profile")} - - + +
    {_t("Profile")} - - + +
    ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 2d547eda0c..6a06811fb3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -401,6 +401,8 @@ "Off": "Off", "On": "On", "Noisy": "Noisy", + "General": "General", + "Profile": "Profile", "Cannot add any more widgets": "Cannot add any more widgets", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "Add a widget": "Add a widget", @@ -1043,11 +1045,11 @@ "Room contains unknown devices": "Room contains unknown devices", "\"%(RoomName)s\" contains devices that you haven't seen before.": "\"%(RoomName)s\" contains devices that you haven't seen before.", "Unknown devices": "Unknown devices", - "General": "General", "Preferences": "Preferences", "Voice & Video": "Voice & Video", "Security & Privacy": "Security & Privacy", "Help & About": "Help & About", + "Visit old settings": "Visit old settings", "Unable to load backup status": "Unable to load backup status", "Unable to restore backup": "Unable to restore backup", "No backup found!": "No backup found!", @@ -1289,7 +1291,6 @@ "VoIP": "VoIP", "Email": "Email", "Add email address": "Add email address", - "Profile": "Profile", "Display name": "Display name", "Account": "Account", "To return to your account in future you need to set a password": "To return to your account in future you need to set a password", From c3692aa9ae7237c1366712c9ff7c58e70ef38681 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 16:03:44 -0700 Subject: [PATCH 44/74] Use the right icons, sizing, and font families for things --- res/css/structures/_TabbedView.scss | 12 +++++------ .../views/dialogs/_UserSettingsDialog.scss | 21 ++++++++----------- res/css/views/settings/tabs/_SettingsTab.scss | 6 +++++- res/img/feather-icons/cancel.svg | 10 +++++++++ res/img/feather-icons/help-circle.svg | 6 ++++++ res/img/feather-icons/lock.svg | 6 ++++++ res/img/feather-icons/sliders.svg | 5 +++++ res/themes/dharma/css/_dharma.scss | 1 + res/themes/light/css/_base.scss | 1 + 9 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 res/img/feather-icons/cancel.svg create mode 100644 res/img/feather-icons/help-circle.svg create mode 100644 res/img/feather-icons/lock.svg create mode 100644 res/img/feather-icons/sliders.svg diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index ecb7193c18..b888aa287b 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -30,7 +30,7 @@ limitations under the License. } .mx_TabbedView_tabLabel { - vertical-align: middle; + vertical-align: text-top; cursor: pointer; display: block; border-radius: 3px; @@ -46,23 +46,23 @@ limitations under the License. } .mx_TabbedView_tabLabel_icon { - width: 12px; - height: 12px; + width: 14px; + height: 14px; margin-left: 6px; margin-right: 9px; position: relative; } .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { - width: 12px; - height: 12px; + width: 14px; + height: 14px; display: inline-block; } .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; - mask-size: 12px; + mask-size: 14px; mask-position: center; content: ''; position: absolute; diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 75d89a1d06..25be25bd56 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -3,29 +3,29 @@ display: block; text-align: center; color: $dialog-title-fg-color; - margin-top: 23px; + margin-top: 16px; margin-bottom: 24px; padding: 0; } .mx_UserSettingsDialog_close { position: absolute; - top: 23px; + top: 16px; right: 25px; } .mx_UserSettingsDialog_closeIcon { - width: 14px; - height: 14px; + width: 16px; + height: 16px; display: inline-block; } .mx_UserSettingsDialog_closeIcon:before { // TODO: Use real icon - mask: url('$(res)/img/feather-icons/video.svg'); + mask: url('$(res)/img/feather-icons/cancel.svg'); background-color: $dialog-close-fg-color; mask-repeat: no-repeat; - mask-size: 14px; + mask-size: 16px; mask-position: center; content: ''; position: absolute; @@ -52,16 +52,13 @@ } .mx_UserSettingsDialog_preferencesIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/paperclip.svg'); + mask: url('$(res)/img/feather-icons/sliders.svg'); } .mx_UserSettingsDialog_securityIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/life-buoy.svg'); + mask: url('$(res)/img/feather-icons/lock.svg'); } .mx_UserSettingsDialog_helpIcon:before { - // TODO: Use real icon - mask: url('$(res)/img/feather-icons/share.svg'); + mask: url('$(res)/img/feather-icons/help-circle.svg'); } \ No newline at end of file diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 13e8b0ab51..0753df56af 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -1,13 +1,17 @@ .mx_SettingsTab_heading { font-size: 20px; font-weight: 600; + color: $primary-fg-color; } .mx_SettingsTab_subheading { font-size: 14px; display: block; + font-family: $font-family-semibold; + color: $primary-fg-color; + margin-bottom: 10px; } .mx_SettingsTab_section { margin-top: 10px; -} \ No newline at end of file +} diff --git a/res/img/feather-icons/cancel.svg b/res/img/feather-icons/cancel.svg new file mode 100644 index 0000000000..6b734e4053 --- /dev/null +++ b/res/img/feather-icons/cancel.svg @@ -0,0 +1,10 @@ + + + + Slice 1 + Created with Sketch. + + + + + \ No newline at end of file diff --git a/res/img/feather-icons/help-circle.svg b/res/img/feather-icons/help-circle.svg new file mode 100644 index 0000000000..7ecb0a8f35 --- /dev/null +++ b/res/img/feather-icons/help-circle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-icons/lock.svg b/res/img/feather-icons/lock.svg new file mode 100644 index 0000000000..1330903b30 --- /dev/null +++ b/res/img/feather-icons/lock.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/res/img/feather-icons/sliders.svg b/res/img/feather-icons/sliders.svg new file mode 100644 index 0000000000..5b5ec8656c --- /dev/null +++ b/res/img/feather-icons/sliders.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index cf3738a1bb..6d907d17be 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -5,6 +5,7 @@ horizontal mess. Arial empirically gets it right, hence prioritising Arial here. */ $font-family: 'Nunito', Arial, Helvetica, Sans-Serif; +$font-family-semibold: 'Nunito SemiBold', Arial, Helvetica, Sans-Serif; // typical text (dark-on-white in light skin) $primary-fg-color: #454545; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 45b64243c6..ec36f15b89 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -4,6 +4,7 @@ horizontal mess. Arial empirically gets it right, hence prioritising Arial here. */ $font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; +$font-family-semibold: 'Open Sans', Arial, Helvetica, Sans-Serif; // typical text (dark-on-white in light skin) $primary-fg-color: #454545; From 15a56fa90b50ec3d3fb64f7cf203e4ee4e41376e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:27:43 -0700 Subject: [PATCH 45/74] Improve the profile section a bit and add a highlight to the temp tab --- res/css/structures/_TabbedView.scss | 6 ++ res/css/views/elements/_AccessibleButton.scss | 21 +++++ .../settings/tabs/_GeneralSettingsTab.scss | 25 +++++- res/themes/dharma/css/_dharma.scss | 6 ++ res/themes/light/css/_base.scss | 6 ++ src/components/structures/TabbedView.js | 3 +- .../views/elements/AccessibleButton.js | 83 ++++++++++------- .../views/settings/tabs/GeneralSettingsTab.js | 90 ++++++++++++++----- src/i18n/strings/en_EN.json | 1 + 9 files changed, 186 insertions(+), 55 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index b888aa287b..86149020b8 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -45,6 +45,11 @@ limitations under the License. color: $tab-label-active-fg-color; } +// TODO: Remove temporary hack alongside "visit old settings" tab +.mx_TabbedView_tabLabel_TEMP_HACK { + background-color: orange; +} + .mx_TabbedView_tabLabel_icon { width: 14px; height: 14px; @@ -89,4 +94,5 @@ limitations under the License. .mx_TabbedView_tabPanelContent { flex-grow: 1; + min-width: 560px; } \ No newline at end of file diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index d6702a232c..23445f5f6f 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -21,3 +21,24 @@ limitations under the License. .mx_AccessibleButton { cursor: pointer; } + +.mx_AccessibleButton_disabled { + cursor: default; +} + +.mx_AccessibleButton_hasKind { + padding: 10px 25px; + text-align: center; + border-radius: 4px; + display: inline-block; +} + +.mx_AccessibleButton_kind_primary { + color: $button-primary-fg-color; + background-color: $button-primary-bg-color; +} + +.mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled { + color: $button-primary-disabled-fg-color; + background-color: $button-primary-disabled-bg-color; +} diff --git a/res/css/views/settings/tabs/_GeneralSettingsTab.scss b/res/css/views/settings/tabs/_GeneralSettingsTab.scss index b04975fc2d..8bc3de8689 100644 --- a/res/css/views/settings/tabs/_GeneralSettingsTab.scss +++ b/res/css/views/settings/tabs/_GeneralSettingsTab.scss @@ -1,6 +1,25 @@ -.mx_GeneralSettingsTab_profile input { +.mx_GeneralSettingsTab_profile { + display: flex; +} + +.mx_GeneralSettingsTab_profileControls { + flex-grow: 1; +} + +.mx_GeneralSettingsTab_profileControls .mx_Field #profileDisplayName { + width: calc(100% - 20px); // subtract 10px padding on left and right +} + +.mx_GeneralSettingsTab_profileAvatar { + width: 88px; + height: 88px; + margin-left: 13px; +} + +.mx_GeneralSettingsTab_profileAvatar div { display: block; - font-size: 14px; - padding: 5px; + width: 88px; + height: 88px; border-radius: 4px; + background-color: #ccc; } \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 6d907d17be..c3f7b661ab 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -200,6 +200,12 @@ $tab-label-active-bg-color: #7ac9a1; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: #7ac9a1; +$button-primary-disabled-fg-color: #ffffff; +$button-primary-disabled-bg-color: #bce4d0; + // unused? $progressbar-color: #000; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index ec36f15b89..7f08b6d1c2 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -196,6 +196,12 @@ $tab-label-active-bg-color: #7ac9a1; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: #7ac9a1; +$button-primary-disabled-fg-color: #ffffff; +$button-primary-disabled-bg-color: #bce4d0; + // unused? $progressbar-color: #000; diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 48f8c7a4d8..ab1f5d76de 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -39,7 +39,7 @@ export class Tab { export class TabbedView extends React.Component { static propTypes = { // The tabs to show - tabs: PropTypes.arrayOf(Tab).isRequired, + tabs: PropTypes.arrayOf(PropTypes.instanceOf(Tab)).isRequired, }; constructor() { @@ -74,6 +74,7 @@ export class TabbedView extends React.Component { const idx = this.props.tabs.indexOf(tab); if (idx === this._getActiveTabIndex()) classes += "mx_TabbedView_tabLabel_active"; + if (tab.label === "Visit old settings") classes += "mx_TabbedView_tabLabel_TEMP_HACK"; let tabIcon = null; if (tab.icon) { diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index e30ceb85fa..938426d5bc 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.js @@ -28,41 +28,56 @@ import { KeyCode } from '../../../Keyboard'; * @returns {Object} rendered react */ export default function AccessibleButton(props) { - const {element, onClick, children, ...restProps} = props; - restProps.onClick = onClick; - // We need to consume enter onKeyDown and space onKeyUp - // otherwise we are risking also activating other keyboard focusable elements - // that might receive focus as a result of the AccessibleButtonClick action - // It's because we are using html buttons at a few places e.g. inside dialogs - // And divs which we report as role button to assistive technologies. - // Browsers handle space and enter keypresses differently and we are only adjusting to the - // inconsistencies here - restProps.onKeyDown = function(e) { - if (e.keyCode === KeyCode.ENTER) { - e.stopPropagation(); - e.preventDefault(); - return onClick(e); - } - if (e.keyCode === KeyCode.SPACE) { - e.stopPropagation(); - e.preventDefault(); - } - }; - restProps.onKeyUp = function(e) { - if (e.keyCode === KeyCode.SPACE) { - e.stopPropagation(); - e.preventDefault(); - return onClick(e); - } - if (e.keyCode === KeyCode.ENTER) { - e.stopPropagation(); - e.preventDefault(); - } - }; + const {element, onClick, children, kind, disabled, ...restProps} = props; + + if (!disabled) { + restProps.onClick = onClick; + // We need to consume enter onKeyDown and space onKeyUp + // otherwise we are risking also activating other keyboard focusable elements + // that might receive focus as a result of the AccessibleButtonClick action + // It's because we are using html buttons at a few places e.g. inside dialogs + // And divs which we report as role button to assistive technologies. + // Browsers handle space and enter keypresses differently and we are only adjusting to the + // inconsistencies here + restProps.onKeyDown = function (e) { + if (e.keyCode === KeyCode.ENTER) { + e.stopPropagation(); + e.preventDefault(); + return onClick(e); + } + if (e.keyCode === KeyCode.SPACE) { + e.stopPropagation(); + e.preventDefault(); + } + }; + restProps.onKeyUp = function (e) { + if (e.keyCode === KeyCode.SPACE) { + e.stopPropagation(); + e.preventDefault(); + return onClick(e); + } + if (e.keyCode === KeyCode.ENTER) { + e.stopPropagation(); + e.preventDefault(); + } + }; + } + restProps.tabIndex = restProps.tabIndex || "0"; restProps.role = "button"; restProps.className = (restProps.className ? restProps.className + " " : "") + "mx_AccessibleButton"; + + if (kind) { + // We apply a hasKind class to maintain backwards compatibility with + // buttons which might not know about kind and break + restProps.className += " mx_AccessibleButton_hasKind mx_AccessibleButton_kind_" + kind; + } + + if (disabled) { + restProps.className += " mx_AccessibleButton_disabled"; + } + return React.createElement(element, restProps, children); } @@ -76,6 +91,12 @@ AccessibleButton.propTypes = { children: PropTypes.node, element: PropTypes.string, onClick: PropTypes.func.isRequired, + + // The kind of button, similar to how Bootstrap works. + // See available classes for AccessibleButton for options. + kind: PropTypes.string, + + disabled: PropTypes.bool, }; AccessibleButton.defaultProps = { diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 433b12a674..513d33fabe 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -16,32 +16,82 @@ limitations under the License. import React from 'react'; import {_t} from "../../../../languageHandler"; +import MatrixClientPeg from "../../../../MatrixClientPeg"; +import Field from "../../elements/Field"; +import AccessibleButton from "../../elements/AccessibleButton"; export default class GeneralSettingsTab extends React.Component { + + constructor() { + super(); + + const client = MatrixClientPeg.get(); + this.state = { + userId: client.getUserId(), + displayName: client.getUser(client.getUserId()).displayName, + enableProfileSave: false, + }; + } + + _saveProfile = async (e) => { + e.stopPropagation(); + e.preventDefault(); + + if (!this.state.enableProfileSave) return; + this.setState({enableProfileSave: false}); + + // TODO: What do we do about errors? + await MatrixClientPeg.get().setDisplayName(this.state.displayName); + + // TODO: Support avatars + + this.setState({enableProfileSave: true}); + }; + + _onDisplayNameChanged = (e) => { + this.setState({ + displayName: e.target.value, + enableProfileSave: true, + }); + }; + + _renderProfileSection() { + const form = ( +
    +
    +
    +

    {this.state.userId}

    + +
    +
    + {/*TODO: Ditch avatar placeholder and use the real thing*/} +
    +
    +
    + + {_t("Save")} + + + ); + + return ( +
    + {_t("Profile")} + {form} +
    + ); + } + render() { return (
    {_t("General")}
    -
    - {_t("Profile")} - - -
    -
    - {_t("Profile")} - - -
    -
    - {_t("Profile")} - - -
    -
    - {_t("Profile")} - - -
    + {this._renderProfileSection()}
    ); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 6a06811fb3..defc77e3dd 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -403,6 +403,7 @@ "Noisy": "Noisy", "General": "General", "Profile": "Profile", + "Display Name": "Display Name", "Cannot add any more widgets": "Cannot add any more widgets", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "Add a widget": "Add a widget", From b678e84272a2e0cbc0ca9a9098948046f6007827 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:32:25 -0700 Subject: [PATCH 46/74] Remove extraneous TODO comment The thing it describes actually happened --- res/css/views/dialogs/_UserSettingsDialog.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 25be25bd56..def5d3f724 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -21,7 +21,6 @@ } .mx_UserSettingsDialog_closeIcon:before { - // TODO: Use real icon mask: url('$(res)/img/feather-icons/cancel.svg'); background-color: $dialog-close-fg-color; mask-repeat: no-repeat; From c7354f483b8d5260e752fad55e01d3482a9ffb72 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 17:49:50 -0600 Subject: [PATCH 47/74] Rename LoginBox to AuthButtons This component displays login and register buttons. It's currently used in the composer when viewing a room as a guest. The name is confusing because the login flow uses are very similarly named `mx_Login_box` as a wrapping around forms, which is totally different than these buttons. Additionally, the components is moved to `views/auth` since it is very simple and auth related. --- res/css/_components.scss | 2 +- .../auth/_AuthButtons.scss} | 9 +++++---- src/components/structures/RoomView.js | 4 ++-- .../LoginBox.js => views/auth/AuthButtons.js} | 18 +++++++++--------- 4 files changed, 17 insertions(+), 16 deletions(-) rename res/css/{structures/_LoginBox.scss => views/auth/_AuthButtons.scss} (87%) rename src/components/{structures/LoginBox.js => views/auth/AuthButtons.js} (67%) diff --git a/res/css/_components.scss b/res/css/_components.scss index a1b575a0a1..998e1754c8 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -8,7 +8,6 @@ @import "./structures/_GroupView.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; -@import "./structures/_LoginBox.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; @import "./structures/_NotificationPanel.scss"; @@ -24,6 +23,7 @@ @import "./structures/_UserSettings.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_Login.scss"; +@import "./views/auth/_AuthButtons.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_ServerConfig.scss"; @import "./views/avatars/_BaseAvatar.scss"; diff --git a/res/css/structures/_LoginBox.scss b/res/css/views/auth/_AuthButtons.scss similarity index 87% rename from res/css/structures/_LoginBox.scss rename to res/css/views/auth/_AuthButtons.scss index 0a3e21a980..553adeee14 100644 --- a/res/css/structures/_LoginBox.scss +++ b/res/css/views/auth/_AuthButtons.scss @@ -1,5 +1,6 @@ /* Copyright 2017 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_LoginBox { +.mx_AuthButtons { min-height: 24px; height: unset !important; padding-top: 13px !important; @@ -22,13 +23,13 @@ limitations under the License. order: 4; } -.mx_LoginBox_loginButton_wrapper { +.mx_AuthButtons_loginButton_wrapper { text-align: center; width: 100%; } -.mx_LoginBox_loginButton, -.mx_LoginBox_registerButton { +.mx_AuthButtons_loginButton, +.mx_AuthButtons_registerButton { margin-top: 3px; height: 40px; border: 0px; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c34b618645..f63e5b3273 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1757,8 +1757,8 @@ module.exports = React.createClass({ } if (MatrixClientPeg.get().isGuest()) { - const LoginBox = sdk.getComponent('structures.LoginBox'); - messageComposer = ; + const AuthButtons = sdk.getComponent('views.auth.AuthButtons'); + messageComposer = ; } // TODO: Why aren't we storing the term/scope/count in this format diff --git a/src/components/structures/LoginBox.js b/src/components/views/auth/AuthButtons.js similarity index 67% rename from src/components/structures/LoginBox.js rename to src/components/views/auth/AuthButtons.js index 168014daa5..35bfabbbca 100644 --- a/src/components/structures/LoginBox.js +++ b/src/components/views/auth/AuthButtons.js @@ -1,6 +1,6 @@ /* Copyright 2017 Vector Creations Ltd -Copyright 2018 New Vector Ltd +Copyright 2018, 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,12 +18,12 @@ limitations under the License. 'use strict'; const React = require('react'); -import { _t } from '../../languageHandler'; -const dis = require('../../dispatcher'); -const AccessibleButton = require('../../components/views/elements/AccessibleButton'); +import { _t } from '../../../languageHandler'; +const dis = require('../../../dispatcher'); +const AccessibleButton = require('../elements/AccessibleButton'); module.exports = React.createClass({ - displayName: 'LoginBox', + displayName: 'AuthButtons', propTypes: { }, @@ -38,18 +38,18 @@ module.exports = React.createClass({ render: function() { const loginButton = ( -
    - +
    + { _t("Login") } - + { _t("Register") }
    ); return ( -
    +
    { loginButton }
    ); From 3bd765f63f985862fe646b59bff597efcfdc547c Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 18:33:17 -0600 Subject: [PATCH 48/74] Rename LoginPage, Header, Footer views to Auth* --- res/css/_components.scss | 3 ++ res/css/structures/auth/_Login.scss | 41 +------------------ res/css/views/auth/_AuthFooter.scss | 28 +++++++++++++ res/css/views/auth/_AuthHeader.scss | 25 +++++++++++ res/css/views/auth/_AuthPage.scss | 32 +++++++++++++++ .../structures/auth/ForgotPassword.js | 14 +++---- src/components/structures/auth/Login.js | 14 +++---- .../structures/auth/PostRegistration.js | 10 ++--- .../structures/auth/Registration.js | 14 +++---- .../auth/{LoginFooter.js => AuthFooter.js} | 5 ++- .../auth/{LoginHeader.js => AuthHeader.js} | 9 ++-- .../views/auth/{LoginPage.js => AuthPage.js} | 4 +- 12 files changed, 126 insertions(+), 73 deletions(-) create mode 100644 res/css/views/auth/_AuthFooter.scss create mode 100644 res/css/views/auth/_AuthHeader.scss create mode 100644 res/css/views/auth/_AuthPage.scss rename src/components/views/auth/{LoginFooter.js => AuthFooter.js} (89%) rename src/components/views/auth/{LoginHeader.js => AuthHeader.js} (77%) rename src/components/views/auth/{LoginPage.js => AuthPage.js} (91%) diff --git a/res/css/_components.scss b/res/css/_components.scss index 998e1754c8..92070483f5 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -24,6 +24,9 @@ @import "./structures/_ViewSource.scss"; @import "./structures/auth/_Login.scss"; @import "./views/auth/_AuthButtons.scss"; +@import "./views/auth/_AuthFooter.scss"; +@import "./views/auth/_AuthHeader.scss"; +@import "./views/auth/_AuthPage.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_ServerConfig.scss"; @import "./views/avatars/_BaseAvatar.scss"; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 1264d2a30f..a5f62089d6 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,23 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_Login { - width: 100%; - height: 100%; - - display: flex; - align-items: center; - justify-content: center; - - overflow: auto; -} - -.mx_Login h2 { - font-weight: 300; - margin-top: 32px; - margin-bottom: 20px; -} - .mx_Login_box { width: 300px; min-height: 450px; @@ -39,16 +23,6 @@ limitations under the License. margin: auto; } -.mx_Login_logo { - text-align: center; - height: 150px; - margin-bottom: 45px; -} - -.mx_Login_logo img { - max-height: 100% -} - .mx_Login_support { text-align: center; font-size: 13px; @@ -115,19 +89,6 @@ limitations under the License. color: $primary-fg-color; } -.mx_Login_links { - display: block; - text-align: center; - margin-top: 15px; - width: 100%; - font-size: 13px; - opacity: 0.8; -} - -.mx_Login_links a:link { - color: $primary-fg-color; -} - .mx_Login_prompt { padding-top: 15px; padding-bottom: 15px; diff --git a/res/css/views/auth/_AuthFooter.scss b/res/css/views/auth/_AuthFooter.scss new file mode 100644 index 0000000000..4737513125 --- /dev/null +++ b/res/css/views/auth/_AuthFooter.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 New Vector 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_AuthFooter { + display: block; + text-align: center; + margin-top: 15px; + width: 100%; + font-size: 13px; + opacity: 0.8; +} + +.mx_AuthFooter a:link { + color: $primary-fg-color; +} diff --git a/res/css/views/auth/_AuthHeader.scss b/res/css/views/auth/_AuthHeader.scss new file mode 100644 index 0000000000..58df178aa9 --- /dev/null +++ b/res/css/views/auth/_AuthHeader.scss @@ -0,0 +1,25 @@ +/* +Copyright 2019 New Vector 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_AuthHeader_logo { + text-align: center; + height: 150px; + margin-bottom: 45px; +} + +.mx_AuthHeader_logo img { + max-height: 100% +} diff --git a/res/css/views/auth/_AuthPage.scss b/res/css/views/auth/_AuthPage.scss new file mode 100644 index 0000000000..d87a906275 --- /dev/null +++ b/res/css/views/auth/_AuthPage.scss @@ -0,0 +1,32 @@ +/* +Copyright 2019 New Vector 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_AuthPage { + width: 100%; + height: 100%; + + display: flex; + align-items: center; + justify-content: center; + + overflow: auto; +} + +.mx_AuthPage h2 { + font-weight: 300; + margin-top: 32px; + margin-bottom: 20px; +} diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 6d5da4f2b7..7c763b0c77 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -183,9 +183,9 @@ module.exports = React.createClass({ }, render: function() { - const LoginPage = sdk.getComponent("auth.LoginPage"); - const LoginHeader = sdk.getComponent("auth.LoginHeader"); - const LoginFooter = sdk.getComponent("auth.LoginFooter"); + const AuthPage = sdk.getComponent("auth.AuthPage"); + const AuthHeader = sdk.getComponent("auth.AuthHeader"); + const AuthFooter = sdk.getComponent("auth.AuthFooter"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const Spinner = sdk.getComponent("elements.Spinner"); @@ -272,7 +272,7 @@ module.exports = React.createClass({ { _t('Create an account') } - +
    ); @@ -280,12 +280,12 @@ module.exports = React.createClass({ return ( - +
    - + { resetPasswordJsx }
    -
    + ); }, }); diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index a1f0327835..069e3aebb6 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -515,9 +515,9 @@ module.exports = React.createClass({ render: function() { const Loader = sdk.getComponent("elements.Spinner"); - const LoginPage = sdk.getComponent("auth.LoginPage"); - const LoginHeader = sdk.getComponent("auth.LoginHeader"); - const LoginFooter = sdk.getComponent("auth.LoginFooter"); + const AuthPage = sdk.getComponent("auth.AuthPage"); + const AuthHeader = sdk.getComponent("auth.AuthHeader"); + const AuthFooter = sdk.getComponent("auth.AuthFooter"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const loader = this.state.busy ?
    : null; @@ -558,9 +558,9 @@ module.exports = React.createClass({ const LanguageSelector = sdk.getComponent('structures.auth.LanguageSelector'); return ( - +
    - +
    { header } { errorTextSection } @@ -571,10 +571,10 @@ module.exports = React.createClass({ { loginAsGuestJsx } - +
    -
    + ); }, }); diff --git a/src/components/structures/auth/PostRegistration.js b/src/components/structures/auth/PostRegistration.js index 826db89b93..78622695cf 100644 --- a/src/components/structures/auth/PostRegistration.js +++ b/src/components/structures/auth/PostRegistration.js @@ -60,12 +60,12 @@ module.exports = React.createClass({ render: function() { const ChangeDisplayName = sdk.getComponent('settings.ChangeDisplayName'); const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); - const LoginPage = sdk.getComponent('auth.LoginPage'); - const LoginHeader = sdk.getComponent('auth.LoginHeader'); + const AuthPage = sdk.getComponent('auth.AuthPage'); + const AuthHeader = sdk.getComponent('auth.AuthHeader'); return ( - +
    - +
    { _t('Set a display name:') } @@ -76,7 +76,7 @@ module.exports = React.createClass({ { this.state.errorString }
    -
    + ); }, }); diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 617c9d6983..bd5a3a3fda 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -396,9 +396,9 @@ module.exports = React.createClass({ }, render: function() { - const LoginHeader = sdk.getComponent('auth.LoginHeader'); - const LoginFooter = sdk.getComponent('auth.LoginFooter'); - const LoginPage = sdk.getComponent('auth.LoginPage'); + const AuthHeader = sdk.getComponent('auth.AuthHeader'); + const AuthFooter = sdk.getComponent('auth.AuthFooter'); + const AuthPage = sdk.getComponent('auth.AuthPage'); const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth'); const Spinner = sdk.getComponent("elements.Spinner"); const ServerConfig = sdk.getComponent('views.auth.ServerConfig'); @@ -474,9 +474,9 @@ module.exports = React.createClass({ const LanguageSelector = sdk.getComponent('structures.auth.LanguageSelector'); return ( - +
    - - +
    -
    + ); }, }); diff --git a/src/components/views/auth/LoginFooter.js b/src/components/views/auth/AuthFooter.js similarity index 89% rename from src/components/views/auth/LoginFooter.js rename to src/components/views/auth/AuthFooter.js index 392d36e288..ea43bf322c 100644 --- a/src/components/views/auth/LoginFooter.js +++ b/src/components/views/auth/AuthFooter.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,11 +21,11 @@ import { _t } from '../../../languageHandler'; import React from 'react'; module.exports = React.createClass({ - displayName: 'LoginFooter', + displayName: 'AuthFooter', render: function() { return ( -
    + ); diff --git a/src/components/views/auth/LoginHeader.js b/src/components/views/auth/AuthHeader.js similarity index 77% rename from src/components/views/auth/LoginHeader.js rename to src/components/views/auth/AuthHeader.js index cd1f9c6a28..08c9ad41c9 100644 --- a/src/components/views/auth/LoginHeader.js +++ b/src/components/views/auth/AuthHeader.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -19,12 +20,14 @@ limitations under the License. const React = require('react'); module.exports = React.createClass({ - displayName: 'LoginHeader', + displayName: 'AuthHeader', render: function() { return ( -
    - Matrix +
    +
    + Matrix +
    ); }, diff --git a/src/components/views/auth/LoginPage.js b/src/components/views/auth/AuthPage.js similarity index 91% rename from src/components/views/auth/LoginPage.js rename to src/components/views/auth/AuthPage.js index 83da8fa60b..09540885c6 100644 --- a/src/components/views/auth/LoginPage.js +++ b/src/components/views/auth/AuthPage.js @@ -20,11 +20,11 @@ limitations under the License. const React = require('react'); module.exports = React.createClass({ - displayName: 'LoginPage', + displayName: 'AuthPage', render: function() { return ( -
    +
    { this.props.children }
    ); From 1d01ac398c58fc2c0ebe05988f3aeca4c4fb72d4 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 21 Jan 2019 18:49:28 -0600 Subject: [PATCH 49/74] Rename `mx_Login_box` to `mx_AuthPage_modal` and fold into `AuthPage` --- res/css/structures/auth/_Login.scss | 8 ------ res/css/views/auth/_AuthPage.scss | 8 ++++++ .../structures/auth/ForgotPassword.js | 6 ++--- src/components/structures/auth/Login.js | 26 +++++++++---------- .../structures/auth/PostRegistration.js | 20 +++++++------- .../structures/auth/Registration.js | 26 +++++++++---------- src/components/views/auth/AuthPage.js | 4 ++- 7 files changed, 46 insertions(+), 52 deletions(-) diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index a5f62089d6..c4eebc48a1 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -15,14 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_Login_box { - width: 300px; - min-height: 450px; - padding-top: 50px; - padding-bottom: 50px; - margin: auto; -} - .mx_Login_support { text-align: center; font-size: 13px; diff --git a/res/css/views/auth/_AuthPage.scss b/res/css/views/auth/_AuthPage.scss index d87a906275..6916f1d92a 100644 --- a/res/css/views/auth/_AuthPage.scss +++ b/res/css/views/auth/_AuthPage.scss @@ -30,3 +30,11 @@ limitations under the License. margin-top: 32px; margin-bottom: 20px; } + +.mx_AuthPage_modal { + width: 300px; + min-height: 450px; + padding-top: 50px; + padding-bottom: 50px; + margin: auto; +} diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index 7c763b0c77..ab0f660be0 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -281,10 +281,8 @@ module.exports = React.createClass({ return ( -
    - - { resetPasswordJsx } -
    + + { resetPasswordJsx }
    ); }, diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index 069e3aebb6..c64223ba48 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -559,20 +559,18 @@ module.exports = React.createClass({ return ( -
    - -
    - { header } - { errorTextSection } - { this.componentForStep(this.state.currentFlow) } - { serverConfig } - - { _t('Create an account') } - - { loginAsGuestJsx } - - -
    + +
    + { header } + { errorTextSection } + { this.componentForStep(this.state.currentFlow) } + { serverConfig } + + { _t('Create an account') } + + { loginAsGuestJsx } + +
    ); diff --git a/src/components/structures/auth/PostRegistration.js b/src/components/structures/auth/PostRegistration.js index 78622695cf..fff326f6ac 100644 --- a/src/components/structures/auth/PostRegistration.js +++ b/src/components/structures/auth/PostRegistration.js @@ -64,17 +64,15 @@ module.exports = React.createClass({ const AuthHeader = sdk.getComponent('auth.AuthHeader'); return ( -
    - -
    - { _t('Set a display name:') } - - { _t('Upload an avatar:') } - - - { this.state.errorString } -
    + +
    + { _t('Set a display name:') } + + { _t('Upload an avatar:') } + + + { this.state.errorString }
    ); diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index bd5a3a3fda..39f7964281 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -475,20 +475,18 @@ module.exports = React.createClass({ return ( -
    - - { header } - { registerBody } - { signIn } - { errorText } - - -
    + + { header } + { registerBody } + { signIn } + { errorText } + +
    ); }, diff --git a/src/components/views/auth/AuthPage.js b/src/components/views/auth/AuthPage.js index 09540885c6..d5f82f7264 100644 --- a/src/components/views/auth/AuthPage.js +++ b/src/components/views/auth/AuthPage.js @@ -25,7 +25,9 @@ module.exports = React.createClass({ render: function() { return (
    - { this.props.children } +
    + { this.props.children } +
    ); }, From a488304410fc42fa5e6c30acf1c24d45db87a23b Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 17:49:48 -0700 Subject: [PATCH 50/74] Appease the linter Appease the linter round 2 Appease the linter round 3 Appease the linter round 4 Appease the linter round 5 --- .eslintrc.js | 5 +- src/components/structures/MatrixChat.js | 3 +- src/components/structures/TabbedView.js | 8 +-- .../views/dialogs/UserSettingsDialog.js | 50 +++++++++++++------ .../views/elements/AccessibleButton.js | 4 +- .../views/settings/tabs/GeneralSettingsTab.js | 14 ++---- 6 files changed, 52 insertions(+), 32 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 971809f851..ec48f6b2ff 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -42,9 +42,8 @@ module.exports = { // bind or arrow function in props causes performance issues // (but we currently use them in some places) - "react/jsx-no-bind": ["warn", { - "ignoreRefs": true, - }], + // It's disabled here, but we should using it sparingly. + "react/jsx-no-bind": "off", "react/jsx-key": ["error"], // Components in JSX should always be defined. diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 2b585506ae..4e6ffbd2c8 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -610,12 +610,13 @@ export default React.createClass({ case 'view_indexed_room': this._viewIndexedRoom(payload.roomIndex); break; - case 'view_user_settings': + case 'view_user_settings': { const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); //this._setPage(PageTypes.UserSettings); //this.notifyNewScreen('settings'); break; + } case 'view_old_user_settings': this._setPage(PageTypes.UserSettings); this.notifyNewScreen('settings'); diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index ab1f5d76de..43979140a8 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -81,9 +81,11 @@ export class TabbedView extends React.Component { tabIcon = {tab.icon}; } + const onClickHandler = () => this._setActiveTab(tab); + return ( - this._setActiveTab(tab)}> + {tabIcon} {_t(tab.label)} @@ -113,4 +115,4 @@ export class TabbedView extends React.Component {
    ); } -} \ No newline at end of file +} diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d355eb77bc..d0b3f436b8 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -22,8 +22,8 @@ import AccessibleButton from "../elements/AccessibleButton"; import GeneralSettingsTab from "../settings/tabs/GeneralSettingsTab"; import dis from '../../../dispatcher'; +// TODO: Ditch this whole component export class TempTab extends React.Component { - // TODO: Ditch this static propTypes = { onClose: PropTypes.func.isRequired, }; @@ -45,13 +45,41 @@ export default class UserSettingsDialog extends React.Component { _getTabs() { return [ - new Tab(_td("General"), , ), - new Tab(_td("Notifications"), ,
    Notifications Test
    ), - new Tab(_td("Preferences"), ,
    Preferences Test
    ), - new Tab(_td("Voice & Video"), ,
    Voice Test
    ), - new Tab(_td("Security & Privacy"), ,
    Security Test
    ), - new Tab(_td("Help & About"), ,
    Help Test
    ), - new Tab(_td("Visit old settings"), , ), + new Tab( + _td("General"), + , + , + ), + new Tab( + _td("Notifications"), + , +
    Notifications Test
    , + ), + new Tab( + _td("Preferences"), + , +
    Preferences Test
    , + ), + new Tab( + _td("Voice & Video"), + , +
    Voice Test
    , + ), + new Tab( + _td("Security & Privacy"), + , +
    Security Test
    , + ), + new Tab( + _td("Help & About"), + , +
    Help Test
    , + ), + new Tab( + _td("Visit old settings"), + , + , + ), ]; } @@ -66,12 +94,6 @@ export default class UserSettingsDialog extends React.Component {
    - // ); } } diff --git a/src/components/views/elements/AccessibleButton.js b/src/components/views/elements/AccessibleButton.js index 938426d5bc..1c39ba4f49 100644 --- a/src/components/views/elements/AccessibleButton.js +++ b/src/components/views/elements/AccessibleButton.js @@ -39,7 +39,7 @@ export default function AccessibleButton(props) { // And divs which we report as role button to assistive technologies. // Browsers handle space and enter keypresses differently and we are only adjusting to the // inconsistencies here - restProps.onKeyDown = function (e) { + restProps.onKeyDown = function(e) { if (e.keyCode === KeyCode.ENTER) { e.stopPropagation(); e.preventDefault(); @@ -50,7 +50,7 @@ export default function AccessibleButton(props) { e.preventDefault(); } }; - restProps.onKeyUp = function (e) { + restProps.onKeyUp = function(e) { if (e.keyCode === KeyCode.SPACE) { e.stopPropagation(); e.preventDefault(); diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 513d33fabe..81501033ec 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -21,7 +21,6 @@ import Field from "../../elements/Field"; import AccessibleButton from "../../elements/AccessibleButton"; export default class GeneralSettingsTab extends React.Component { - constructor() { super(); @@ -56,6 +55,7 @@ export default class GeneralSettingsTab extends React.Component { }; _renderProfileSection() { + // TODO: Ditch avatar placeholder and use the real thing const form = (
    @@ -63,24 +63,20 @@ export default class GeneralSettingsTab extends React.Component {

    {this.state.userId}

    + onChange={this._onDisplayNameChanged} />
    - {/*TODO: Ditch avatar placeholder and use the real thing*/} -
    +
    + disabled={!this.state.enableProfileSave}> {_t("Save")} ); - return ( -
    + return (
    {_t("Profile")} {form}
    From feed17d9acc54d5e4d130b68640c4a4eda4ab359 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 21 Jan 2019 20:55:40 -0700 Subject: [PATCH 51/74] Minor code style fix --- src/components/views/settings/tabs/GeneralSettingsTab.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/views/settings/tabs/GeneralSettingsTab.js b/src/components/views/settings/tabs/GeneralSettingsTab.js index 81501033ec..c8816325c0 100644 --- a/src/components/views/settings/tabs/GeneralSettingsTab.js +++ b/src/components/views/settings/tabs/GeneralSettingsTab.js @@ -76,7 +76,8 @@ export default class GeneralSettingsTab extends React.Component { ); - return (
    + return ( +
    {_t("Profile")} {form}
    From 9f5244fa8b646b7fec1d4da7781bd73714657540 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 22 Jan 2019 10:16:31 +0000 Subject: [PATCH 52/74] Deduplicate
    tag --- .../views/dialogs/keybackup/CreateKeyBackupDialog.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index fd3d6776eb..2c54344a30 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -252,9 +252,7 @@ export default React.createClass({ for (let i = 0; i < this.state.zxcvbnResult.feedback.suggestions.length; ++i) { suggestions.push(
    {this.state.zxcvbnResult.feedback.suggestions[i]}
    ); } - const suggestionBlock = suggestions.length > 0 ?
    - {suggestions} -
    :
    {_t("Keep going...")}
    ; + const suggestionBlock =
    {suggestions.length > 0 ? suggestions : _t("Keep going...")}
    ; helpText =
    {this.state.zxcvbnResult.feedback.warning} From 881353037e30767957f638a7a06521efe1424994 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 10:34:54 +0000 Subject: [PATCH 53/74] forgot .length somehow Co-Authored-By: bwindels --- src/components/views/rooms/WhoIsTypingTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 4c97110797..b540abf61e 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -78,7 +78,7 @@ module.exports = React.createClass({ }, isVisible: function() { - return this.state.usersTyping.length !== 0 || Object.keys(this.state.delayedStopTypingTimers) !== 0; + return this.state.usersTyping.length !== 0 || Object.keys(this.state.delayedStopTypingTimers).length !== 0; }, onRoomTimeline: function(event, room) { From 5d45d5dfac1dd0f166f7a6ae778dd36735401336 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 11:37:09 +0100 Subject: [PATCH 54/74] formatting --- src/components/structures/ScrollPanel.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/structures/ScrollPanel.js b/src/components/structures/ScrollPanel.js index 96318d64e1..be5f23c420 100644 --- a/src/components/structures/ScrollPanel.js +++ b/src/components/structures/ScrollPanel.js @@ -681,6 +681,7 @@ module.exports = React.createClass({ _collectGeminiScroll: function(gemScroll) { this._gemScroll = gemScroll; }, + /** * Set the current height as the min height for the message list * so the timeline cannot shrink. This is used to avoid @@ -693,6 +694,7 @@ module.exports = React.createClass({ messageList.style.minHeight = `${currentHeight}px`; } }, + /** * Clear the previously set min height */ From 0e0128c29749651ff733de7eeddad5e18ea46583 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 11:37:18 +0100 Subject: [PATCH 55/74] if instead of short-circuit and for readability --- src/components/views/rooms/WhoIsTypingTile.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index b540abf61e..e826380469 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -110,7 +110,9 @@ module.exports = React.createClass({ // abort all the timers for the users that started typing again usersThatStartedTyping.forEach((m) => { const timer = this.state.delayedStopTypingTimers[m.userId]; - timer && timer.abort(); + if (timer) { + timer.abort(); + } }); // prepare new delayedStopTypingTimers object to update state with let delayedStopTypingTimers = Object.assign({}, this.state.delayedStopTypingTimers); From f83411ea0b97b92bd73091f21451b55a95817087 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 11:46:02 +0100 Subject: [PATCH 56/74] whitespace --- src/components/views/rooms/WhoIsTypingTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index e826380469..4ee11d77b2 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -128,7 +128,7 @@ module.exports = React.createClass({ delayedStopTypingTimers[m.userId] = timer; timer.start(); timer.finished().then( - () => this._removeUserTimer(m.userId), //on elapsed + () => this._removeUserTimer(m.userId), // on elapsed () => {/* aborted */}, ); } From 5dd5f4f1dc4e00c95a4403e2318d2adf31754e28 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 14:39:03 +0100 Subject: [PATCH 57/74] add JumpToBottomButtom component --- res/css/_components.scss | 1 + res/css/views/rooms/_JumpToBottomButton.scss | 69 +++++++++++++++++++ res/img/icon-jump-to-bottom.svg | 32 +++++++++ .../views/rooms/JumpToBottomButton.js | 32 +++++++++ 4 files changed, 134 insertions(+) create mode 100644 res/css/views/rooms/_JumpToBottomButton.scss create mode 100644 res/img/icon-jump-to-bottom.svg create mode 100644 src/components/views/rooms/JumpToBottomButton.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 92070483f5..6532fcd74c 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -98,6 +98,7 @@ @import "./views/rooms/_AuxPanel.scss"; @import "./views/rooms/_EntityTile.scss"; @import "./views/rooms/_EventTile.scss"; +@import "./views/rooms/_JumpToBottomButton.scss"; @import "./views/rooms/_LinkPreviewWidget.scss"; @import "./views/rooms/_MemberDeviceInfo.scss"; @import "./views/rooms/_MemberInfo.scss"; diff --git a/res/css/views/rooms/_JumpToBottomButton.scss b/res/css/views/rooms/_JumpToBottomButton.scss new file mode 100644 index 0000000000..968139671f --- /dev/null +++ b/res/css/views/rooms/_JumpToBottomButton.scss @@ -0,0 +1,69 @@ +/* +Copyright 2019 New Vector 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. +*/ + +@charset "utf-8"; + +.mx_JumpToBottomButton { + z-index: 1000; + position: absolute; + // 12 because height is 50 but button is only 38 = 12+(50-38) = 24 + bottom: 12px; + right: 24px; + width: 38px; + // give it a fixed height so the badge doesn't make + // it taller and pop upwards when visible + height: 50px; + text-align: center; +} + +.mx_JumpToBottomButton_badge { + position: relative; + top: -12px; + border-radius: 16px; + font-weight: bold; + font-size: 12px; + line-height: 14px; + text-align: center; + // to be able to get it centered + // with text-align in parent + display: inline-block; + padding: 0 4px; + color: $secondary-accent-color; + background-color: $warning-color; +} + +.mx_JumpToBottomButton_scrollDown { + position: relative; + height: 38px; + border-radius: 19px; + box-sizing: border-box; + background: $primary-bg-color; + border: 1.3px solid $roomtile-name-color; + cursor: pointer; +} + +.mx_JumpToBottomButton_scrollDown:before { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + mask: url('$(res)/img/icon-jump-to-bottom.svg'); + mask-repeat: no-repeat; + mask-position: 9px 14px; + background: $roomtile-name-color; +} diff --git a/res/img/icon-jump-to-bottom.svg b/res/img/icon-jump-to-bottom.svg new file mode 100644 index 0000000000..c4210b4ebe --- /dev/null +++ b/res/img/icon-jump-to-bottom.svg @@ -0,0 +1,32 @@ + + + + + + image/svg+xml + + + + + + + + diff --git a/src/components/views/rooms/JumpToBottomButton.js b/src/components/views/rooms/JumpToBottomButton.js new file mode 100644 index 0000000000..d68af07317 --- /dev/null +++ b/src/components/views/rooms/JumpToBottomButton.js @@ -0,0 +1,32 @@ +/* +Copyright 2019 New Vector 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 { _t } from '../../../languageHandler'; +import AccessibleButton from '../elements/AccessibleButton'; + +export default (props) => { + let badge; + if (props.numUnreadMessages) { + badge =
    {props.numUnreadMessages}
    + } + return (
    + + + { badge } +
    ); +}; From de4cda4572dc04e243382566e18ace33110dc71a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 14:49:41 +0100 Subject: [PATCH 58/74] remove unread messages and jump to bottom from RoomStatusBar --- src/components/structures/RoomStatusBar.js | 53 +--------------------- src/components/structures/RoomView.js | 3 -- 2 files changed, 1 insertion(+), 55 deletions(-) diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 12888769ff..b57bac805e 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -45,14 +45,6 @@ module.exports = React.createClass({ propTypes: { // the room this statusbar is representing. room: PropTypes.object.isRequired, - - // the number of messages which have arrived since we've been scrolled up - numUnreadMessages: PropTypes.number, - - // this is true if we are fully scrolled-down, and are looking at - // the end of the live timeline. - atEndOfLiveTimeline: PropTypes.bool, - // This is true when the user is alone in the room, but has also sent a message. // Used to suggest to the user to invite someone sentMessageAndIsAlone: PropTypes.bool, @@ -82,9 +74,6 @@ module.exports = React.createClass({ // 'you are alone' bar onStopWarningClick: PropTypes.func, - // callback for when the user clicks on the 'scroll to bottom' button - onScrollToBottomClick: PropTypes.func, - // callback for when we do something that changes the size of the // status bar. This is used to trigger a re-layout in the parent // component. @@ -180,8 +169,6 @@ module.exports = React.createClass({ // indicate other sizes. _getSize: function() { if (this._shouldShowConnectionError() || - this.props.numUnreadMessages || - !this.props.atEndOfLiveTimeline || this.props.hasActiveCall || this.props.sentMessageAndIsAlone ) { @@ -194,28 +181,6 @@ module.exports = React.createClass({ // return suitable content for the image on the left of the status bar. _getIndicator: function() { - if (this.props.numUnreadMessages) { - return ( -
    - -
    - ); - } - - const AccessibleButton = sdk.getComponent("elements.AccessibleButton"); - if (!this.props.atEndOfLiveTimeline) { - return ( - - {_t("Scroll - - ); - } - if (this.props.hasActiveCall) { const TintableSvg = sdk.getComponent("elements.TintableSvg"); return ( @@ -231,9 +196,7 @@ module.exports = React.createClass({ }, _shouldShowConnectionError: function() { - // no conn bar trumps unread count since you can't get unread messages - // without a connection! (technically may already have some but meh) - // It also trumps the "some not sent" msg since you can't resend without + // no conn bar trumps the "some not sent" msg since you can't resend without // a connection! // There's one situation in which we don't show this 'no connection' bar, and that's // if it's a resource limit exceeded error: those are shown in the top bar. @@ -363,20 +326,6 @@ module.exports = React.createClass({ return this._getUnsentMessageContent(); } - // unread count trumps who is typing since the unread count is only - // set when you've scrolled up - if (this.props.numUnreadMessages) { - // MUST use var name "count" for pluralization to kick in - const unreadMsgs = _t("%(count)s new messages", {count: this.props.numUnreadMessages}); - - return ( -
    - { unreadMsgs } -
    - ); - } - if (this.props.hasActiveCall) { return (
    diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index f63e5b3273..eb530be1a1 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1651,14 +1651,11 @@ module.exports = React.createClass({ isStatusAreaExpanded = this.state.statusBarVisible; statusBar = Date: Tue, 22 Jan 2019 14:50:16 +0100 Subject: [PATCH 59/74] use JumpToBottomButton in RoomView --- src/components/structures/RoomView.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index eb530be1a1..53eae5467c 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1861,6 +1861,14 @@ module.exports = React.createClass({ onCloseClick={this.forgetReadMarker} />); } + let jumpToBottom; + if (!this.state.atEndOfLiveTimeline) { + const JumpToBottomButton = sdk.getComponent('rooms.JumpToBottomButton'); + jumpToBottom = (); + } const statusBarAreaClass = classNames( "mx_RoomView_statusArea", { @@ -1898,6 +1906,7 @@ module.exports = React.createClass({ { auxPanel }
    { topUnreadMessagesBar } + { jumpToBottom } { messagePanel } { searchResultsPanel }
    From e5de33ab77a78267059518740269114e76072069 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 14:50:31 +0100 Subject: [PATCH 60/74] hide RoomStatusBar when not needed again as it will be shown a lot less now, having the layout jump up and down won't be so much of a problem. --- src/components/structures/RoomView.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index 53eae5467c..ea01c9bcae 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1473,11 +1473,10 @@ module.exports = React.createClass({ onStatusBarHidden: function() { // This is currently not desired as it is annoying if it keeps expanding and collapsing - // TODO: Find a less annoying way of hiding the status bar - /*if (this.unmounted) return; + if (this.unmounted) return; this.setState({ statusBarVisible: false, - });*/ + }); }, /** From 689df1b50616eb0961e2bf0cd11cab70bb36531a Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 14:51:11 +0100 Subject: [PATCH 61/74] copyright --- src/components/views/rooms/TopUnreadMessagesBar.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/rooms/TopUnreadMessagesBar.js b/src/components/views/rooms/TopUnreadMessagesBar.js index ed04ce594d..99f3b7fb23 100644 --- a/src/components/views/rooms/TopUnreadMessagesBar.js +++ b/src/components/views/rooms/TopUnreadMessagesBar.js @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 85b55847e10e19cfdc09ef0226d66361f8be1c50 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 14:52:22 +0100 Subject: [PATCH 62/74] missing semicolon --- src/components/views/rooms/JumpToBottomButton.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/JumpToBottomButton.js b/src/components/views/rooms/JumpToBottomButton.js index d68af07317..487071855f 100644 --- a/src/components/views/rooms/JumpToBottomButton.js +++ b/src/components/views/rooms/JumpToBottomButton.js @@ -20,7 +20,7 @@ import AccessibleButton from '../elements/AccessibleButton'; export default (props) => { let badge; if (props.numUnreadMessages) { - badge =
    {props.numUnreadMessages}
    + badge = (
    {props.numUnreadMessages}
    ); } return (
    Date: Tue, 22 Jan 2019 16:40:11 +0100 Subject: [PATCH 63/74] set default size of 350px for left panel --- src/components/structures/LoggedInView.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 409988842f..6f2e1f3989 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -183,10 +183,13 @@ const LoggedInView = React.createClass({ }, _loadResizerPreferences() { - const lhsSize = window.localStorage.getItem("mx_lhs_size"); + let lhsSize = window.localStorage.getItem("mx_lhs_size"); if (lhsSize !== null) { - this.resizer.forHandleAt(0).resize(parseInt(lhsSize, 10)); + lhsSize = parseInt(lhsSize, 10); + } else { + lhsSize = 350; } + this.resizer.forHandleAt(0).resize(lhsSize); }, onAccountData: function(event) { From 1373b2f5e27600677165569b0d5790f38378e856 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 17:34:48 +0100 Subject: [PATCH 64/74] apparently room can be null here --- src/components/views/rooms/WhoIsTypingTile.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/WhoIsTypingTile.js b/src/components/views/rooms/WhoIsTypingTile.js index 4ee11d77b2..dba40f033a 100644 --- a/src/components/views/rooms/WhoIsTypingTile.js +++ b/src/components/views/rooms/WhoIsTypingTile.js @@ -82,7 +82,7 @@ module.exports = React.createClass({ }, onRoomTimeline: function(event, room) { - if (room.roomId === this.props.room.roomId) { + if (room && room.roomId === this.props.room.roomId) { const userId = event.getSender(); // remove user from usersTyping const usersTyping = this.state.usersTyping.filter((m) => m.userId !== userId); From 7d4165802cc08dbc1e9b849911c6a1bdf52fa2b9 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Tue, 22 Jan 2019 17:54:04 +0100 Subject: [PATCH 65/74] show rooms and people section when empty while filtering --- src/components/views/rooms/RoomList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomList.js b/src/components/views/rooms/RoomList.js index 1a714e3a84..01e6d280c6 100644 --- a/src/components/views/rooms/RoomList.js +++ b/src/components/views/rooms/RoomList.js @@ -593,7 +593,7 @@ module.exports = React.createClass({ subListsProps = subListsProps.filter((props => { const len = props.list.length + (props.extraTiles ? props.extraTiles.length : 0); - return len !== 0 || (props.onAddRoom && !this.props.searchFilter); + return len !== 0 || props.onAddRoom; })); return subListsProps.reduce((components, props, i) => { From 0deb210fd89b91b41d147c4a08969ea9acc1b480 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 09:54:35 -0700 Subject: [PATCH 66/74] Collapse DOM around tab label icon --- res/css/structures/_TabbedView.scss | 22 +++++++------------ .../views/dialogs/_UserSettingsDialog.scss | 12 +++++----- src/components/structures/TabbedView.js | 8 +++---- .../views/dialogs/UserSettingsDialog.js | 14 ++++++------ 4 files changed, 25 insertions(+), 31 deletions(-) diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index 86149020b8..0f4b67ad71 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -38,6 +38,7 @@ limitations under the License. font-weight: 600; height: 20px; margin-bottom: 6px; + position: relative; } .mx_TabbedView_tabLabel_active { @@ -50,34 +51,27 @@ limitations under the License. background-color: orange; } -.mx_TabbedView_tabLabel_icon { - width: 14px; - height: 14px; +.mx_TabbedView_maskedIcon {; margin-left: 6px; margin-right: 9px; - position: relative; -} - -.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon { width: 14px; height: 14px; display: inline-block; } -.mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { +.mx_TabbedView_maskedIcon:before { + display: inline-block; background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; mask-size: 14px; + width: 14px; + height: 14px; mask-position: center; content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; + vertical-align: middle; } -.mx_TabbedView_tabLabel_active .mx_TabbedView_tabLabel_icon > .mx_TabbedView_maskedIcon:before { +.mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon:before { background-color: $tab-label-active-icon-bg-color; } diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index def5d3f724..c4bd8a5110 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -39,25 +39,25 @@ // ========================================================== .mx_UserSettingsDialog_settingsIcon:before { - mask: url('$(res)/img/feather-icons/settings.svg'); + mask-image: url('$(res)/img/feather-icons/settings.svg'); } .mx_UserSettingsDialog_voiceIcon:before { - mask: url('$(res)/img/feather-icons/phone.svg'); + mask-image: url('$(res)/img/feather-icons/phone.svg'); } .mx_UserSettingsDialog_bellIcon:before { - mask: url('$(res)/img/feather-icons/notifications.svg'); + mask-image: url('$(res)/img/feather-icons/notifications.svg'); } .mx_UserSettingsDialog_preferencesIcon:before { - mask: url('$(res)/img/feather-icons/sliders.svg'); + mask-image: url('$(res)/img/feather-icons/sliders.svg'); } .mx_UserSettingsDialog_securityIcon:before { - mask: url('$(res)/img/feather-icons/lock.svg'); + mask-image: url('$(res)/img/feather-icons/lock.svg'); } .mx_UserSettingsDialog_helpIcon:before { - mask: url('$(res)/img/feather-icons/help-circle.svg'); + mask-image: url('$(res)/img/feather-icons/help-circle.svg'); } \ No newline at end of file diff --git a/src/components/structures/TabbedView.js b/src/components/structures/TabbedView.js index 43979140a8..2b136128f3 100644 --- a/src/components/structures/TabbedView.js +++ b/src/components/structures/TabbedView.js @@ -26,12 +26,12 @@ export class Tab { /** * Creates a new tab. * @param {string} tabLabel The untranslated tab label. - * @param {string} tabIconJsx The JSX for the tab icon. This should be a plain img element or null. + * @param {string} tabIconClass The class for the tab icon. This should be a simple mask. * @param {string} tabJsx The JSX for the tab container. */ - constructor(tabLabel, tabIconJsx, tabJsx) { + constructor(tabLabel, tabIconClass, tabJsx) { this.label = tabLabel; - this.icon = tabIconJsx; + this.icon = tabIconClass; this.body = tabJsx; } } @@ -78,7 +78,7 @@ export class TabbedView extends React.Component { let tabIcon = null; if (tab.icon) { - tabIcon = {tab.icon}; + tabIcon = ; } const onClickHandler = () => this._setActiveTab(tab); diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index d0b3f436b8..dd404ce280 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -47,37 +47,37 @@ export default class UserSettingsDialog extends React.Component { return [ new Tab( _td("General"), - , + "mx_UserSettingsDialog_settingsIcon", , ), new Tab( _td("Notifications"), - , + "mx_UserSettingsDialog_bellIcon",
    Notifications Test
    , ), new Tab( _td("Preferences"), - , + "mx_UserSettingsDialog_preferencesIcon",
    Preferences Test
    , ), new Tab( _td("Voice & Video"), - , + "mx_UserSettingsDialog_voiceIcon",
    Voice Test
    , ), new Tab( _td("Security & Privacy"), - , + "mx_UserSettingsDialog_securityIcon",
    Security Test
    , ), new Tab( _td("Help & About"), - , + "mx_UserSettingsDialog_helpIcon",
    Help Test
    , ), new Tab( _td("Visit old settings"), - , + "mx_UserSettingsDialog_helpIcon", , ), ]; From 5ef901513313d985ada89b8ec622c4a8c2732145 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 10:28:33 -0700 Subject: [PATCH 67/74] Add labs flag for tabbed settings --- src/components/structures/MatrixChat.js | 11 ++-- src/i18n/strings/en_EN.json | 87 +++++++++++++------------ src/settings/Settings.js | 6 ++ 3 files changed, 57 insertions(+), 47 deletions(-) diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index 4e6ffbd2c8..4c783eedac 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -611,10 +611,13 @@ export default React.createClass({ this._viewIndexedRoom(payload.roomIndex); break; case 'view_user_settings': { - const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); - Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); - //this._setPage(PageTypes.UserSettings); - //this.notifyNewScreen('settings'); + if (SettingsStore.isFeatureEnabled("feature_tabbed_settings")) { + const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); + Modal.createTrackedDialog('User settings', '', UserSettingsDialog, {}); + } else { + this._setPage(PageTypes.UserSettings); + this.notifyNewScreen('settings'); + } break; } case 'view_old_user_settings': diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index defc77e3dd..ae9eb2e9b4 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -262,6 +262,7 @@ "Please contact your homeserver administrator.": "Please contact your homeserver administrator.", "Failed to join room": "Failed to join room", "Message Pinning": "Message Pinning", + "Tabbed settings": "Tabbed settings", "Custom user status messages": "Custom user status messages", "Increase performance by only loading room members on first view": "Increase performance by only loading room members on first view", "Backup of encryption keys to server": "Backup of encryption keys to server", @@ -401,9 +402,10 @@ "Off": "Off", "On": "On", "Noisy": "Noisy", - "General": "General", - "Profile": "Profile", "Display Name": "Display Name", + "Save": "Save", + "Profile": "Profile", + "General": "General", "Cannot add any more widgets": "Cannot add any more widgets", "The maximum permitted number of widgets have already been added to this room.": "The maximum permitted number of widgets have already been added to this room.", "Add a widget": "Add a widget", @@ -536,7 +538,6 @@ "World readable": "World readable", "Guests can join": "Guests can join", "Failed to set avatar.": "Failed to set avatar.", - "Save": "Save", "(~%(count)s results)|other": "(~%(count)s results)", "(~%(count)s results)|one": "(~%(count)s result)", "Join Room": "Join Room", @@ -713,46 +714,6 @@ "Removed or unknown message type": "Removed or unknown message type", "Message removed by %(userId)s": "Message removed by %(userId)s", "Message removed": "Message removed", - "Robot check is currently unavailable on desktop - please use a web browser": "Robot check is currently unavailable on desktop - please use a web browser", - "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", - "Custom Server Options": "Custom Server Options", - "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", - "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", - "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", - "To continue, please enter your password.": "To continue, please enter your password.", - "Password:": "Password:", - "Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies", - "Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:", - "An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s", - "Please check your email to continue registration.": "Please check your email to continue registration.", - "Token incorrect": "Token incorrect", - "A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s", - "Please enter the code it contains:": "Please enter the code it contains:", - "Code": "Code", - "Start authentication": "Start authentication", - "powered by Matrix": "powered by Matrix", - "The email field must not be blank.": "The email field must not be blank.", - "The user name field must not be blank.": "The user name field must not be blank.", - "The phone number field must not be blank.": "The phone number field must not be blank.", - "The password field must not be blank.": "The password field must not be blank.", - "Username on %(hs)s": "Username on %(hs)s", - "User name": "User name", - "Mobile phone number": "Mobile phone number", - "Forgot your password?": "Forgot your password?", - "Matrix ID": "Matrix ID", - "%(serverName)s Matrix ID": "%(serverName)s Matrix ID", - "Sign in with": "Sign in with", - "Email address": "Email address", - "Sign in": "Sign in", - "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", - "Email address (optional)": "Email address (optional)", - "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", - "Mobile phone number (optional)": "Mobile phone number (optional)", - "Default server": "Default server", - "Custom server": "Custom server", - "Home server URL": "Home server URL", - "Identity server URL": "Identity server URL", - "What does this mean?": "What does this mean?", "Remove from community": "Remove from community", "Disinvite this user from community?": "Disinvite this user from community?", "Remove this user from community?": "Remove this user from community?", @@ -889,6 +850,7 @@ "And %(count)s more...|other": "And %(count)s more...", "ex. @bob:example.com": "ex. @bob:example.com", "Add User": "Add User", + "Matrix ID": "Matrix ID", "Matrix Room ID": "Matrix Room ID", "email address": "email address", "That doesn't look like a valid email address": "That doesn't look like a valid email address", @@ -1015,6 +977,7 @@ "Please check your email and click on the link it contains. Once this is done, click continue.": "Please check your email and click on the link it contains. Once this is done, click continue.", "Unable to add email address": "Unable to add email address", "Unable to verify email address.": "Unable to verify email address.", + "Email address": "Email address", "This will allow you to reset your password and receive notifications.": "This will allow you to reset your password and receive notifications.", "Skip": "Skip", "Only use lower case letters, numbers and '=_-./'": "Only use lower case letters, numbers and '=_-./'", @@ -1102,6 +1065,44 @@ "Set status": "Set status", "Set a new status...": "Set a new status...", "View Community": "View Community", + "Robot check is currently unavailable on desktop - please use a web browser": "Robot check is currently unavailable on desktop - please use a web browser", + "This Home Server would like to make sure you are not a robot": "This Home Server would like to make sure you are not a robot", + "Custom Server Options": "Custom Server Options", + "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.": "You can use the custom server options to sign into other Matrix servers by specifying a different Home server URL.", + "This allows you to use this app with an existing Matrix account on a different home server.": "This allows you to use this app with an existing Matrix account on a different home server.", + "You can also set a custom identity server but this will typically prevent interaction with users based on email address.": "You can also set a custom identity server but this will typically prevent interaction with users based on email address.", + "To continue, please enter your password.": "To continue, please enter your password.", + "Password:": "Password:", + "Please review and accept all of the homeserver's policies": "Please review and accept all of the homeserver's policies", + "Please review and accept the policies of this homeserver:": "Please review and accept the policies of this homeserver:", + "An email has been sent to %(emailAddress)s": "An email has been sent to %(emailAddress)s", + "Please check your email to continue registration.": "Please check your email to continue registration.", + "Token incorrect": "Token incorrect", + "A text message has been sent to %(msisdn)s": "A text message has been sent to %(msisdn)s", + "Please enter the code it contains:": "Please enter the code it contains:", + "Code": "Code", + "Start authentication": "Start authentication", + "powered by Matrix": "powered by Matrix", + "The email field must not be blank.": "The email field must not be blank.", + "The user name field must not be blank.": "The user name field must not be blank.", + "The phone number field must not be blank.": "The phone number field must not be blank.", + "The password field must not be blank.": "The password field must not be blank.", + "Username on %(hs)s": "Username on %(hs)s", + "User name": "User name", + "Mobile phone number": "Mobile phone number", + "Forgot your password?": "Forgot your password?", + "%(serverName)s Matrix ID": "%(serverName)s Matrix ID", + "Sign in with": "Sign in with", + "Sign in": "Sign in", + "If you don't specify an email address, you won't be able to reset your password. Are you sure?": "If you don't specify an email address, you won't be able to reset your password. Are you sure?", + "Email address (optional)": "Email address (optional)", + "You are registering with %(SelectedTeamName)s": "You are registering with %(SelectedTeamName)s", + "Mobile phone number (optional)": "Mobile phone number (optional)", + "Default server": "Default server", + "Custom server": "Custom server", + "Home server URL": "Home server URL", + "Identity server URL": "Identity server URL", + "What does this mean?": "What does this mean?", "Sorry, your browser is not able to run Riot.": "Sorry, your browser is not able to run Riot.", "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.": "Riot uses many advanced browser features, some of which are not available or experimental in your current browser.", "Please install Chrome or Firefox for the best experience.": "Please install Chrome or Firefox for the best experience.", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 5a9bd9e455..4871ee92f9 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -84,6 +84,12 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_tabbed_settings": { + isFeature: true, + displayName: _td("Tabbed settings"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_custom_status": { isFeature: true, displayName: _td("Custom user status messages"), From be94e1aebc915b31b02328040f0c674f446ec1fb Mon Sep 17 00:00:00 2001 From: Christopher Medlin Date: Tue, 22 Jan 2019 11:39:26 -0800 Subject: [PATCH 68/74] Fix integrations error popup Signed-off-by: Christopher Medlin --- res/css/views/rooms/_RoomSettings.scss | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_RoomSettings.scss b/res/css/views/rooms/_RoomSettings.scss index 4454cd479c..5ed9168aab 100644 --- a/res/css/views/rooms/_RoomSettings.scss +++ b/res/css/views/rooms/_RoomSettings.scss @@ -66,15 +66,16 @@ limitations under the License. .mx_RoomSettings_integrationsButton_errorPopup { position: absolute; top: 110%; - left: -125%; - width: 348%; - padding: 2%; + left: -275%; + width: 550%; + padding: 30%; font-size: 10pt; line-height: 1.5em; border-radius: 5px; background-color: $accent-color; color: $accent-fg-color; text-align: center; + z-index: 1000; } .mx_RoomSettings_unbanButton { display: inline; From f0e8182ff362c6f4352b6baabd081fb66126a550 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 19:25:09 -0700 Subject: [PATCH 69/74] Support selects on Field Luckily, the styling is copy/paste capable. --- res/css/views/elements/_Field.scss | 19 +++++++++++++------ src/components/views/elements/Field.js | 13 ++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index c728f4bcd4..1fcd238c23 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -21,7 +21,8 @@ limitations under the License. margin: 1em 0; } -.mx_Field input { +.mx_Field input, +.mx_Field select { font-weight: normal; border-radius: 4px; transition: border-color 0.25s; @@ -29,17 +30,20 @@ limitations under the License. padding: 8px 9px; } -.mx_Field input:focus { +.mx_Field input:focus, +.mx_Field select:focus { outline: 0; border-color: $input-focused-border-color; } -.mx_Field input::placeholder { +.mx_Field input::placeholder, +.mx_Field select::placeholder { transition: color 0.25s ease-in 0s; color: transparent; } -.mx_Field input:placeholder-shown:focus::placeholder { +.mx_Field input:placeholder-shown:focus::placeholder, +.mx_Field select:placeholder-shown:focus::placeholder { transition: color 0.25s ease-in 0.1s; color: $greyed-fg-color; } @@ -61,7 +65,9 @@ limitations under the License. } .mx_Field input:focus + label, -.mx_Field input:not(:placeholder-shown) + label { +.mx_Field input:not(:placeholder-shown) + label, +.mx_Field select:focus + label, +.mx_Field select:not(:placeholder-shown) + label { transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s, @@ -72,6 +78,7 @@ limitations under the License. background-color: $field-focused-label-bg-color; } -.mx_Field input:focus + label { +.mx_Field input:focus + label, +.mx_Field select:focus + label { color: $input-focused-border-color; } diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 428c7f1d6c..896eff5918 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -27,6 +27,9 @@ export default class Field extends React.PureComponent { label: PropTypes.string, // The field's placeholder string. placeholder: PropTypes.string, + // The type of field to create. Defaults to "input". Should be "input" or "select". + // To define options for a select, use + element: PropTypes.string, // All other props pass through to the . } @@ -38,13 +41,13 @@ export default class Field extends React.PureComponent { delete extraProps.type; delete extraProps.placeholder; delete extraProps.label; + delete extraProps.element; + + const element = this.props.element || "input"; + const fieldInput = React.createElement(element, extraProps, this.props.children); return
    - + {fieldInput}
    ; } From 9d2bfdc0c0d49d7b4ee259024814d55f8faf7a8e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 22 Jan 2019 11:50:11 -0600 Subject: [PATCH 70/74] Fix CSS indentation level --- res/css/views/auth/_AuthFooter.scss | 14 +++++++------- res/css/views/auth/_AuthHeader.scss | 8 ++++---- res/css/views/auth/_AuthPage.scss | 28 ++++++++++++++-------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/res/css/views/auth/_AuthFooter.scss b/res/css/views/auth/_AuthFooter.scss index 4737513125..0d9e59e7df 100644 --- a/res/css/views/auth/_AuthFooter.scss +++ b/res/css/views/auth/_AuthFooter.scss @@ -15,14 +15,14 @@ limitations under the License. */ .mx_AuthFooter { - display: block; - text-align: center; - margin-top: 15px; - width: 100%; - font-size: 13px; - opacity: 0.8; + display: block; + text-align: center; + margin-top: 15px; + width: 100%; + font-size: 13px; + opacity: 0.8; } .mx_AuthFooter a:link { - color: $primary-fg-color; + color: $primary-fg-color; } diff --git a/res/css/views/auth/_AuthHeader.scss b/res/css/views/auth/_AuthHeader.scss index 58df178aa9..426215dc62 100644 --- a/res/css/views/auth/_AuthHeader.scss +++ b/res/css/views/auth/_AuthHeader.scss @@ -15,11 +15,11 @@ limitations under the License. */ .mx_AuthHeader_logo { - text-align: center; - height: 150px; - margin-bottom: 45px; + text-align: center; + height: 150px; + margin-bottom: 45px; } .mx_AuthHeader_logo img { - max-height: 100% + max-height: 100% } diff --git a/res/css/views/auth/_AuthPage.scss b/res/css/views/auth/_AuthPage.scss index 6916f1d92a..8dae4ca64e 100644 --- a/res/css/views/auth/_AuthPage.scss +++ b/res/css/views/auth/_AuthPage.scss @@ -15,26 +15,26 @@ limitations under the License. */ .mx_AuthPage { - width: 100%; - height: 100%; + width: 100%; + height: 100%; - display: flex; - align-items: center; - justify-content: center; + display: flex; + align-items: center; + justify-content: center; - overflow: auto; + overflow: auto; } .mx_AuthPage h2 { - font-weight: 300; - margin-top: 32px; - margin-bottom: 20px; + font-weight: 300; + margin-top: 32px; + margin-bottom: 20px; } .mx_AuthPage_modal { - width: 300px; - min-height: 450px; - padding-top: 50px; - padding-bottom: 50px; - margin: auto; + width: 300px; + min-height: 450px; + padding-top: 50px; + padding-bottom: 50px; + margin: auto; } From 575cd1e37b73e47487726a038e85f4967a6c5e0a Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 22 Jan 2019 19:28:23 -0600 Subject: [PATCH 71/74] Add modal look to authentication flows This changes the auth screens to use the modal-like style of the redesign. This does not attempt to style the actual body content of each screen. Instead, it covers the header area with logo, footer links, and overall modal container only. --- res/css/_components.scss | 1 + res/css/views/auth/_AuthBody.scss | 23 ++++++++++++++++ res/css/views/auth/_AuthFooter.scss | 12 ++++----- res/css/views/auth/_AuthHeader.scss | 12 ++++++--- res/css/views/auth/_AuthPage.scss | 18 ++++++------- res/themes/dharma/css/_dharma.scss | 4 +++ res/themes/light/css/_base.scss | 4 +++ .../structures/auth/ForgotPassword.js | 7 ++--- src/components/structures/auth/Login.js | 7 +++-- .../structures/auth/PostRegistration.js | 21 ++++++++------- .../structures/auth/Registration.js | 15 ++++++----- src/components/views/auth/AuthBody.js | 27 +++++++++++++++++++ src/components/views/auth/AuthPage.js | 4 +++ 13 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 res/css/views/auth/_AuthBody.scss create mode 100644 src/components/views/auth/AuthBody.js diff --git a/res/css/_components.scss b/res/css/_components.scss index 6532fcd74c..be36ea7a5f 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -23,6 +23,7 @@ @import "./structures/_UserSettings.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_Login.scss"; +@import "./views/auth/_AuthBody.scss"; @import "./views/auth/_AuthButtons.scss"; @import "./views/auth/_AuthFooter.scss"; @import "./views/auth/_AuthHeader.scss"; diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss new file mode 100644 index 0000000000..97632c2e3f --- /dev/null +++ b/res/css/views/auth/_AuthBody.scss @@ -0,0 +1,23 @@ +/* +Copyright 2019 New Vector 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_AuthBody { + width: 500px; + background-color: $authpage-body-bg-color; + border-radius: 0 4px 4px 0; + padding: 25px 60px; + box-sizing: border-box; +} diff --git a/res/css/views/auth/_AuthFooter.scss b/res/css/views/auth/_AuthFooter.scss index 0d9e59e7df..6ad5b0a459 100644 --- a/res/css/views/auth/_AuthFooter.scss +++ b/res/css/views/auth/_AuthFooter.scss @@ -15,14 +15,14 @@ limitations under the License. */ .mx_AuthFooter { - display: block; text-align: center; - margin-top: 15px; width: 100%; - font-size: 13px; - opacity: 0.8; + font-size: 14px; + opacity: 0.72; + margin: 20px 0; } -.mx_AuthFooter a:link { - color: $primary-fg-color; +.mx_AuthFooter a:link, a:hover, a:visited { + color: $accent-fg-color; + margin: 0 22px; } diff --git a/res/css/views/auth/_AuthHeader.scss b/res/css/views/auth/_AuthHeader.scss index 426215dc62..fb0b3e666b 100644 --- a/res/css/views/auth/_AuthHeader.scss +++ b/res/css/views/auth/_AuthHeader.scss @@ -14,12 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_AuthHeader { + width: 206px; + padding: 25px 50px; + box-sizing: border-box; +} + .mx_AuthHeader_logo { - text-align: center; - height: 150px; - margin-bottom: 45px; + margin-top: 15px; } .mx_AuthHeader_logo img { - max-height: 100% + width: 100%; } diff --git a/res/css/views/auth/_AuthPage.scss b/res/css/views/auth/_AuthPage.scss index 8dae4ca64e..9e32750771 100644 --- a/res/css/views/auth/_AuthPage.scss +++ b/res/css/views/auth/_AuthPage.scss @@ -17,12 +17,10 @@ limitations under the License. .mx_AuthPage { width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; - + flex-direction: column; overflow: auto; + background-color: $authpage-bg-color; } .mx_AuthPage h2 { @@ -32,9 +30,11 @@ limitations under the License. } .mx_AuthPage_modal { - width: 300px; - min-height: 450px; - padding-top: 50px; - padding-bottom: 50px; - margin: auto; + display: flex; + margin: 100px auto auto; + border-radius: 4px; + // Not currently supported in Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1178765 + backdrop-filter: blur(10px); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.33); + background-color: $authpage-modal-bg-color; } diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index c9f62fbe6b..1678c9b586 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -193,6 +193,10 @@ $room-warning-bg-color: #fff8e3; $memberstatus-placeholder-color: $roomtile-name-color; +$authpage-bg-color: #2e3649; +$authpage-modal-bg-color: rgba(255, 255, 255, 0.59); +$authpage-body-bg-color: #ffffff; + /*** form elements ***/ // .mx_textinput is a container for a text input diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index dbab909f13..c03db23930 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -188,6 +188,10 @@ $room-warning-bg-color: #fff8e3; $memberstatus-placeholder-color: $roomtile-name-color; +$authpage-bg-color: #2e3649; +$authpage-modal-bg-color: rgba(255, 255, 255, 0.59); +$authpage-body-bg-color: #ffffff; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/src/components/structures/auth/ForgotPassword.js b/src/components/structures/auth/ForgotPassword.js index ab0f660be0..87e8c7d131 100644 --- a/src/components/structures/auth/ForgotPassword.js +++ b/src/components/structures/auth/ForgotPassword.js @@ -185,7 +185,7 @@ module.exports = React.createClass({ render: function() { const AuthPage = sdk.getComponent("auth.AuthPage"); const AuthHeader = sdk.getComponent("auth.AuthHeader"); - const AuthFooter = sdk.getComponent("auth.AuthFooter"); + const AuthBody = sdk.getComponent("auth.AuthBody"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const Spinner = sdk.getComponent("elements.Spinner"); @@ -272,7 +272,6 @@ module.exports = React.createClass({ { _t('Create an account') } -
    ); @@ -282,7 +281,9 @@ module.exports = React.createClass({ return ( - { resetPasswordJsx } + + {resetPasswordJsx} + ); }, diff --git a/src/components/structures/auth/Login.js b/src/components/structures/auth/Login.js index c64223ba48..28bed9af05 100644 --- a/src/components/structures/auth/Login.js +++ b/src/components/structures/auth/Login.js @@ -517,7 +517,7 @@ module.exports = React.createClass({ const Loader = sdk.getComponent("elements.Spinner"); const AuthPage = sdk.getComponent("auth.AuthPage"); const AuthHeader = sdk.getComponent("auth.AuthHeader"); - const AuthFooter = sdk.getComponent("auth.AuthFooter"); + const AuthBody = sdk.getComponent("auth.AuthBody"); const ServerConfig = sdk.getComponent("auth.ServerConfig"); const loader = this.state.busy ?
    : null; @@ -560,7 +560,7 @@ module.exports = React.createClass({ return ( -
    + { header } { errorTextSection } { this.componentForStep(this.state.currentFlow) } @@ -570,8 +570,7 @@ module.exports = React.createClass({ { loginAsGuestJsx } - -
    +
    ); }, diff --git a/src/components/structures/auth/PostRegistration.js b/src/components/structures/auth/PostRegistration.js index fff326f6ac..1e24d0920a 100644 --- a/src/components/structures/auth/PostRegistration.js +++ b/src/components/structures/auth/PostRegistration.js @@ -62,18 +62,21 @@ module.exports = React.createClass({ const ChangeAvatar = sdk.getComponent('settings.ChangeAvatar'); const AuthPage = sdk.getComponent('auth.AuthPage'); const AuthHeader = sdk.getComponent('auth.AuthHeader'); + const AuthBody = sdk.getComponent("auth.AuthBody"); return ( -
    - { _t('Set a display name:') } - - { _t('Upload an avatar:') } - - - { this.state.errorString } -
    + +
    + { _t('Set a display name:') } + + { _t('Upload an avatar:') } + + + { this.state.errorString } +
    +
    ); }, diff --git a/src/components/structures/auth/Registration.js b/src/components/structures/auth/Registration.js index 39f7964281..fe6fb078e3 100644 --- a/src/components/structures/auth/Registration.js +++ b/src/components/structures/auth/Registration.js @@ -397,7 +397,7 @@ module.exports = React.createClass({ render: function() { const AuthHeader = sdk.getComponent('auth.AuthHeader'); - const AuthFooter = sdk.getComponent('auth.AuthFooter'); + const AuthBody = sdk.getComponent("auth.AuthBody"); const AuthPage = sdk.getComponent('auth.AuthPage'); const InteractiveAuth = sdk.getComponent('structures.InteractiveAuth'); const Spinner = sdk.getComponent("elements.Spinner"); @@ -481,12 +481,13 @@ module.exports = React.createClass({ this.state.teamSelected.domain + "/icon.png" : null} /> - { header } - { registerBody } - { signIn } - { errorText } - - + + { header } + { registerBody } + { signIn } + { errorText } + + ); }, diff --git a/src/components/views/auth/AuthBody.js b/src/components/views/auth/AuthBody.js new file mode 100644 index 0000000000..9a078efb52 --- /dev/null +++ b/src/components/views/auth/AuthBody.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +import React from 'react'; + +export default class AuthBody extends React.PureComponent { + render() { + return
    + { this.props.children } +
    ; + } +} diff --git a/src/components/views/auth/AuthPage.js b/src/components/views/auth/AuthPage.js index d5f82f7264..8cb8cf7d53 100644 --- a/src/components/views/auth/AuthPage.js +++ b/src/components/views/auth/AuthPage.js @@ -18,16 +18,20 @@ limitations under the License. 'use strict'; const React = require('react'); +import sdk from '../../../index'; module.exports = React.createClass({ displayName: 'AuthPage', render: function() { + const AuthFooter = sdk.getComponent('auth.AuthFooter'); + return (
    { this.props.children }
    +
    ); }, From 2b3c8c44503c03fbc3557e8b5c26587dafb8e313 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 20:03:59 -0700 Subject: [PATCH 72/74] Correctly form a select/input element --- res/css/views/elements/_Field.scss | 9 +++------ src/components/views/elements/Field.js | 10 +++++----- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 1fcd238c23..4f6e868249 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -36,14 +36,12 @@ limitations under the License. border-color: $input-focused-border-color; } -.mx_Field input::placeholder, -.mx_Field select::placeholder { +.mx_Field input::placeholder { transition: color 0.25s ease-in 0s; color: transparent; } -.mx_Field input:placeholder-shown:focus::placeholder, -.mx_Field select:placeholder-shown:focus::placeholder { +.mx_Field input:placeholder-shown:focus::placeholder { transition: color 0.25s ease-in 0.1s; color: $greyed-fg-color; } @@ -66,8 +64,7 @@ limitations under the License. .mx_Field input:focus + label, .mx_Field input:not(:placeholder-shown) + label, -.mx_Field select:focus + label, -.mx_Field select:not(:placeholder-shown) + label { +.mx_Field select:focus + label { transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s, diff --git a/src/components/views/elements/Field.js b/src/components/views/elements/Field.js index 896eff5918..69b890b911 100644 --- a/src/components/views/elements/Field.js +++ b/src/components/views/elements/Field.js @@ -36,12 +36,12 @@ export default class Field extends React.PureComponent { render() { const extraProps = Object.assign({}, this.props); - // Remove explicit props - delete extraProps.id; - delete extraProps.type; - delete extraProps.placeholder; - delete extraProps.label; + // Remove explicit properties that shouldn't be copied delete extraProps.element; + delete extraProps.children; + + // Set some defaults for the element + extraProps.type = extraProps.type || "text"; const element = this.props.element || "input"; const fieldInput = React.createElement(element, extraProps, this.props.children); From 7ccf64584e990872a93de3778ba83a194d2155f7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 22 Jan 2019 21:35:36 -0700 Subject: [PATCH 73/74] Fix AuthFooter CSS rules conflicting with anchors all over the app Fixes https://github.com/vector-im/riot-web/issues/8227 --- res/css/views/auth/_AuthFooter.scss | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/res/css/views/auth/_AuthFooter.scss b/res/css/views/auth/_AuthFooter.scss index 6ad5b0a459..741be49a69 100644 --- a/res/css/views/auth/_AuthFooter.scss +++ b/res/css/views/auth/_AuthFooter.scss @@ -22,7 +22,9 @@ limitations under the License. margin: 20px 0; } -.mx_AuthFooter a:link, a:hover, a:visited { +.mx_AuthFooter a:link, +.mx_AuthFooter a:hover, +.mx_AuthFooter a:visited { color: $accent-fg-color; margin: 0 22px; } From 0f3ee9c786d7f8f37ca6a7ff4077cbd45b2214c7 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 22 Jan 2019 21:51:42 -0600 Subject: [PATCH 74/74] Extract auth header logo to new component This will allow `riot-web` to replace only the logo, rather than the whole header. --- res/css/_components.scss | 1 + res/css/views/auth/_AuthHeader.scss | 8 ------ res/css/views/auth/_AuthHeaderLogo.scss | 23 ++++++++++++++++++ src/components/views/auth/AuthHeader.js | 7 +++--- src/components/views/auth/AuthHeaderLogo.js | 27 +++++++++++++++++++++ 5 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 res/css/views/auth/_AuthHeaderLogo.scss create mode 100644 src/components/views/auth/AuthHeaderLogo.js diff --git a/res/css/_components.scss b/res/css/_components.scss index be36ea7a5f..9a5e390933 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -27,6 +27,7 @@ @import "./views/auth/_AuthButtons.scss"; @import "./views/auth/_AuthFooter.scss"; @import "./views/auth/_AuthHeader.scss"; +@import "./views/auth/_AuthHeaderLogo.scss"; @import "./views/auth/_AuthPage.scss"; @import "./views/auth/_InteractiveAuthEntryComponents.scss"; @import "./views/auth/_ServerConfig.scss"; diff --git a/res/css/views/auth/_AuthHeader.scss b/res/css/views/auth/_AuthHeader.scss index fb0b3e666b..376864d268 100644 --- a/res/css/views/auth/_AuthHeader.scss +++ b/res/css/views/auth/_AuthHeader.scss @@ -19,11 +19,3 @@ limitations under the License. padding: 25px 50px; box-sizing: border-box; } - -.mx_AuthHeader_logo { - margin-top: 15px; -} - -.mx_AuthHeader_logo img { - width: 100%; -} diff --git a/res/css/views/auth/_AuthHeaderLogo.scss b/res/css/views/auth/_AuthHeaderLogo.scss new file mode 100644 index 0000000000..3d8ab29325 --- /dev/null +++ b/res/css/views/auth/_AuthHeaderLogo.scss @@ -0,0 +1,23 @@ +/* +Copyright 2019 New Vector 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_AuthHeaderLogo { + margin-top: 15px; +} + +.mx_AuthHeaderLogo img { + width: 100%; +} diff --git a/src/components/views/auth/AuthHeader.js b/src/components/views/auth/AuthHeader.js index 08c9ad41c9..c1d831a70a 100644 --- a/src/components/views/auth/AuthHeader.js +++ b/src/components/views/auth/AuthHeader.js @@ -18,16 +18,17 @@ limitations under the License. 'use strict'; const React = require('react'); +import sdk from '../../../index'; module.exports = React.createClass({ displayName: 'AuthHeader', render: function() { + const AuthHeaderLogo = sdk.getComponent('auth.AuthHeaderLogo'); + return (
    -
    - Matrix -
    +
    ); }, diff --git a/src/components/views/auth/AuthHeaderLogo.js b/src/components/views/auth/AuthHeaderLogo.js new file mode 100644 index 0000000000..9edf149a83 --- /dev/null +++ b/src/components/views/auth/AuthHeaderLogo.js @@ -0,0 +1,27 @@ +/* +Copyright 2019 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +'use strict'; + +import React from 'react'; + +export default class AuthHeaderLogo extends React.PureComponent { + render() { + return
    + Matrix +
    ; + } +}