diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml index 60964de469..ac05da02e3 100644 --- a/.buildkite/pipeline.yaml +++ b/.buildkite/pipeline.yaml @@ -24,6 +24,7 @@ steps: plugins: - docker#v3.0.1: image: "matrixdotorg/riotweb-ci-e2etests-env:latest" + propagate-environment: true - label: ":karma: Tests" agents: diff --git a/res/css/_common.scss b/res/css/_common.scss index 11894e414a..d3cf1921e0 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -523,3 +523,38 @@ textarea { opacity: 0; cursor: pointer; } + +// username colors +// used by SenderProfile & RoomPreviewBar +.mx_Username_color1 { + color: $username-variant1-color; +} + +.mx_Username_color2 { + color: $username-variant2-color; +} + +.mx_Username_color3 { + color: $username-variant3-color; +} + +.mx_Username_color4 { + color: $username-variant4-color; +} + +.mx_Username_color5 { + color: $username-variant5-color; +} + +.mx_Username_color6 { + color: $username-variant6-color; +} + +.mx_Username_color7 { + color: $username-variant7-color; +} + +.mx_Username_color8 { + color: $username-variant8-color; +} + diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index cead3f4672..50d412ad58 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -163,15 +163,6 @@ limitations under the License. height: 1px; } - -.mx_RoomView_body .mx_RoomView_statusArea { - order: 3; -} - -.mx_RoomView_body .mx_MessageComposer { - order: 4; -} - .mx_RoomView_messageListWrapper { min-height: 100%; diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 25ad51a3fb..fe1f283009 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -37,6 +37,12 @@ limitations under the License. .mx_AccessibleButton_kind_primary { color: $button-primary-fg-color; background-color: $button-primary-bg-color; + font-weight: 600; +} + +.mx_AccessibleButton_kind_secondary { + color: $accent-color; + font-weight: 600; } .mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled { diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index a4a2aba11f..655cb39489 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -18,36 +18,3 @@ limitations under the License. font-weight: 600; } -.mx_SenderProfile_color1 { - color: $username-variant1-color; -} - -.mx_SenderProfile_color2 { - color: $username-variant2-color; -} - -.mx_SenderProfile_color3 { - color: $username-variant3-color; -} - -.mx_SenderProfile_color4 { - color: $username-variant4-color; -} - -.mx_SenderProfile_color5 { - color: $username-variant5-color; -} - -.mx_SenderProfile_color6 { - color: $username-variant6-color; -} - -.mx_SenderProfile_color7 { - color: $username-variant7-color; -} - -.mx_SenderProfile_color8 { - color: $username-variant8-color; -} - - diff --git a/res/css/views/rooms/_RoomPreviewBar.scss b/res/css/views/rooms/_RoomPreviewBar.scss index 8196740499..ea3b787971 100644 --- a/res/css/views/rooms/_RoomPreviewBar.scss +++ b/res/css/views/rooms/_RoomPreviewBar.scss @@ -15,48 +15,112 @@ limitations under the License. */ .mx_RoomPreviewBar { - text-align: center; - height: 176px; - background-color: $event-selected-color; + flex: 0 0 auto; align-items: center; flex-direction: column; justify-content: center; display: flex; - background-color: $preview-bar-bg-color; -webkit-align-items: center; + + h3 { + font-size: 18px; + font-weight: 600; + + &.mx_RoomPreviewBar_spinnerTitle { + display: flex; + flex-direction: row; + align-items: center; + } + } + + .mx_Spinner { + width: auto; + height: auto; + margin: 10px 10px 10px 0; + flex: 0 0 auto; + } } -.mx_RoomPreviewBar_wrapper { +.mx_RoomPreviewBar_dark { + background-color: $tagpanel-bg-color; + color: $accent-fg-color; } -.mx_RoomPreviewBar_invite_text { - color: $primary-fg-color; +.mx_RoomPreviewBar_actions { + display: flex; } -.mx_RoomPreviewBar_join_text { - color: $warning-color; +.mx_RoomPreviewBar_message { + display: flex; + align-items: stretch; + + p { + overflow-wrap: break-word; + } } -.mx_RoomPreviewBar_preview_text { - margin-top: 25px; - color: $settings-grey-fg-color; +.mx_RoomPreviewBar_panel { + padding: 8px 8px 8px 20px; + border-top: 1px solid $panel-divider-color; + + flex-direction: row; + + .mx_RoomPreviewBar_actions { + flex: 0 0 auto; + flex-direction: row; + padding: 3px 8px; + + &>* { + margin-left: 12px; + } + } + + .mx_RoomPreviewBar_message { + flex: 1 0 0; + min-width: 0; + display: flex; + flex-direction: column; + + &>* { + margin: 4px; + } + } } -.mx_RoomPreviewBar_join_text a { +.mx_RoomPreviewBar_dialog { + margin: auto; + box-sizing: content; + width: 400px; + border-radius: 4px; + flex-direction: column; + padding: 20px; + text-align: center; + + .mx_RoomPreviewBar_message { + flex-direction: column; + + &>* { + margin: 5px 0 20px 0; + } + } + + .mx_RoomPreviewBar_actions { + flex-direction: column-reverse; + .mx_AccessibleButton { + padding: 7px 50px;//extra wide + } + + &>* { + margin-top: 12px; + } + } +} + +.mx_RoomPreviewBar_inviter { + font-weight: 600; +} + +a.mx_RoomPreviewBar_inviter { text-decoration: underline; cursor: pointer; } - -.mx_RoomPreviewBar_warning { - display: flex; - align-items: center; - padding: 8px; -} - -.mx_RoomPreviewBar_warningIcon { - padding: 12px; -} - -.mx_RoomPreviewBar_spinnerIntro { - margin-top: 50px; -} \ No newline at end of file diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js index f6ca6fbdd9..33f1bac090 100644 --- a/src/components/structures/MatrixChat.js +++ b/src/components/structures/MatrixChat.js @@ -565,6 +565,9 @@ export default React.createClass({ }, }); break; + case 'view_user_info': + this._viewUser(payload.userId, payload.subAction); + break; case 'view_room': // Takes either a room ID or room alias: if switching to a room the client is already // known to be in (eg. user clicks on a room in the recents panel), supply the ID @@ -919,6 +922,22 @@ export default React.createClass({ this.notifyNewScreen('home'); }, + _viewUser: function(userId, subAction) { + // Wait for the first sync so that `getRoom` gives us a room object if it's + // in the sync response + const waitForSync = this.firstSyncPromise ? + this.firstSyncPromise.promise : Promise.resolve(); + waitForSync.then(() => { + if (subAction === 'chat') { + this._chatCreateOrReuse(userId); + return; + } + this.notifyNewScreen('user/' + userId); + this.setState({currentUserId: userId}); + this._setPage(PageTypes.UserView); + }); + }, + _setMxId: function(payload) { const SetMxIdDialog = sdk.getComponent('views.dialogs.SetMxIdDialog'); const close = Modal.createTrackedDialog('Set MXID', '', SetMxIdDialog, { @@ -1612,19 +1631,10 @@ export default React.createClass({ dis.dispatch(payload); } else if (screen.indexOf('user/') == 0) { const userId = screen.substring(5); - - // Wait for the first sync so that `getRoom` gives us a room object if it's - // in the sync response - const waitFor = this.firstSyncPromise ? - this.firstSyncPromise.promise : Promise.resolve(); - waitFor.then(() => { - if (params.action === 'chat') { - this._chatCreateOrReuse(userId); - return; - } - this.notifyNewScreen('user/' + userId); - this.setState({currentUserId: userId}); - this._setPage(PageTypes.UserView); + dis.dispatch({ + action: 'view_user_info', + userId: userId, + subAction: params.action, }); } else if (screen.indexOf('group/') == 0) { const groupId = screen.substring(6); diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index fdfc6acf9b..99f13dcd35 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -1511,16 +1511,21 @@ module.exports = React.createClass({ const ScrollPanel = sdk.getComponent("structures.ScrollPanel"); const TintableSvg = sdk.getComponent("elements.TintableSvg"); const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar"); - const Loader = sdk.getComponent("elements.Spinner"); const TimelinePanel = sdk.getComponent("structures.TimelinePanel"); const RoomUpgradeWarningBar = sdk.getComponent("rooms.RoomUpgradeWarningBar"); const RoomRecoveryReminder = sdk.getComponent("rooms.RoomRecoveryReminder"); if (!this.state.room) { - if (this.state.roomLoading || this.state.peekLoading) { + const loading = this.state.roomLoading || this.state.peekLoading; + if (loading) { return (
- +
); } else { @@ -1538,28 +1543,16 @@ module.exports = React.createClass({ const roomAlias = this.state.roomAlias; return (
- -
-
- -
-
-
); } @@ -1569,9 +1562,12 @@ module.exports = React.createClass({ if (myMembership == 'invite') { if (this.state.joining || this.state.rejecting) { return ( -
- -
+ ); } else { const myUserId = MatrixClientPeg.get().credentials.userId; @@ -1586,26 +1582,14 @@ module.exports = React.createClass({ // We have a regular invite for this room. return (
- -
-
- -
-
-
); } @@ -1661,7 +1645,9 @@ module.exports = React.createClass({ const hiddenHighlightCount = this._getHiddenHighlightCount(); let aux = null; + let previewBar; let hideCancel = false; + let hideRightPanel = false; if (this.state.forwardingEvent !== null) { aux = ; } else if (this.state.searching) { @@ -1688,18 +1674,26 @@ module.exports = React.createClass({ invitedEmail = this.props.thirdPartyInvite.invitedEmail; } hideCancel = true; - aux = ( + previewBar = ( ); + if (!this.state.canPeek) { + return ( +
+ { previewBar } +
+ ); + } else { + hideRightPanel = true; + } } else if (hiddenHighlightCount > 0) { aux = ( ; } - if (MatrixClientPeg.get().isGuest()) { - const AuthButtons = sdk.getComponent('views.auth.AuthButtons'); - messageComposer = ; - } - // TODO: Why aren't we storing the term/scope/count in this format // in this.state if this is what RoomHeader desires? if (this.state.searchResults) { @@ -1875,14 +1864,16 @@ module.exports = React.createClass({ }, ); - const rightPanel = this.state.room ? : undefined; + const rightPanel = !hideRightPanel && this.state.room && + ; + const collapsedRhs = hideRightPanel || this.props.collapsedRhs; return (
@@ -1910,6 +1901,7 @@ module.exports = React.createClass({ { statusBar }
+ { previewBar } { messageComposer }
diff --git a/src/components/views/auth/AuthButtons.js b/src/components/views/auth/AuthButtons.js deleted file mode 100644 index 35bfabbbca..0000000000 --- a/src/components/views/auth/AuthButtons.js +++ /dev/null @@ -1,57 +0,0 @@ -/* -Copyright 2017 Vector Creations 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. -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'; - -const React = require('react'); -import { _t } from '../../../languageHandler'; -const dis = require('../../../dispatcher'); -const AccessibleButton = require('../elements/AccessibleButton'); - -module.exports = React.createClass({ - displayName: 'AuthButtons', - - propTypes: { - }, - - onLoginClick: function() { - dis.dispatch({ action: 'start_login' }); - }, - - onRegisterClick: function() { - dis.dispatch({ action: 'start_registration' }); - }, - - render: function() { - const loginButton = ( -
- - { _t("Login") } - - - { _t("Register") } - -
- ); - - return ( -
- { loginButton } -
- ); - }, -}); diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index 0d47de4266..75898736f1 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -23,7 +23,7 @@ import sdk from '../../../index'; import Flair from '../elements/Flair.js'; import FlairStore from '../../../stores/FlairStore'; import { _t } from '../../../languageHandler'; -import {hashCode} from '../../../utils/FormattingUtils'; +import {getUserNameColorClass} from '../../../utils/FormattingUtils'; export default React.createClass({ displayName: 'SenderProfile', @@ -97,7 +97,7 @@ export default React.createClass({ render() { const EmojiText = sdk.getComponent('elements.EmojiText'); const {mxEvent} = this.props; - const colorNumber = (hashCode(mxEvent.getSender()) % 8) + 1; + const colorClass = getUserNameColorClass(mxEvent.getSender()); const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); const {msgtype} = mxEvent.getContent(); @@ -121,7 +121,7 @@ export default React.createClass({ // Name + flair const nameFlair = - + { nameElem } { flair } diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index dbe409d6d7..55c39d3947 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -17,12 +17,29 @@ limitations under the License. 'use strict'; -const React = require('react'); +import React from 'react'; import PropTypes from 'prop-types'; -const sdk = require('../../../index'); -const MatrixClientPeg = require('../../../MatrixClientPeg'); - +import sdk from '../../../index'; +import MatrixClientPeg from '../../../MatrixClientPeg'; +import dis from '../../../dispatcher'; +import classNames from 'classnames'; import { _t } from '../../../languageHandler'; +import {getUserNameColorClass} from '../../../utils/FormattingUtils'; + +const MessageCase = Object.freeze({ + NotLoggedIn: "NotLoggedIn", + Joining: "Joining", + Loading: "Loading", + Rejecting: "Rejecting", + Kicked: "Kicked", + Banned: "Banned", + OtherThreePIDError: "OtherThreePIDError", + InvitedEmailMismatch: "InvitedEmailMismatch", + Invite: "Invite", + ViewingRoom: "ViewingRoom", + RoomNotFound: "RoomNotFound", + OtherError: "OtherError", +}); module.exports = React.createClass({ displayName: 'RoomPreviewBar', @@ -31,7 +48,6 @@ module.exports = React.createClass({ onJoinClick: PropTypes.func, onRejectClick: PropTypes.func, onForgetClick: PropTypes.func, - // if inviterName is specified, the preview bar will shown an invite to the room. // You should also specify onRejectClick if specifiying inviterName inviterName: PropTypes.string, @@ -50,7 +66,9 @@ module.exports = React.createClass({ // purpose of the spinner. spinner: PropTypes.bool, spinnerState: PropTypes.oneOf(["joining"]), - + loading: PropTypes.bool, + joining: PropTypes.bool, + rejecting: PropTypes.bool, // The alias that was used to access this room, if appropriate // If given, this will be how the room is referred to (eg. // in error messages). @@ -60,7 +78,6 @@ module.exports = React.createClass({ getDefaultProps: function() { return { onJoinClick: function() {}, - canPreview: true, }; }, @@ -72,7 +89,7 @@ module.exports = React.createClass({ componentWillMount: function() { // If this is an invite and we've been told what email - // address was invited, fetch the user's list of 3pids + // address was invited, fetch the user's list of Threepids // so we can check them against the one that was invited if (this.props.inviterName && this.props.invitedEmail) { this.setState({busy: true}); @@ -88,157 +105,334 @@ module.exports = React.createClass({ } }, - _roomNameElement: function() { - return this.props.room ? this.props.room.name : (this.props.room_alias || ""); + _onInviterClick(evt) { + evt.preventDefault(); + const member = this._getInviteMember(); + dis.dispatch({action: 'view_user_info', userId: member.userId}); }, - render: function() { - let joinBlock; let previewBlock; + _getMessageCase() { + const isGuest = MatrixClientPeg.get().isGuest(); - if (this.props.spinner || this.state.busy) { - const Spinner = sdk.getComponent("elements.Spinner"); - let spinnerIntro = ""; - if (this.props.spinnerState === "joining") { - spinnerIntro = _t("Joining room..."); - } - return (
-

{ spinnerIntro }

- -
); + if (isGuest) { + return MessageCase.NotLoggedIn; } + const myMember = this.props.room && + this.props.room.getMember(MatrixClientPeg.get().getUserId()); + + if (myMember) { + if (myMember.isKicked()) { + return MessageCase.Kicked; + } else if (myMember.membership === "ban") { + return MessageCase.Banned; + } + } + + if (this.props.joining) { + return MessageCase.Joining; + } else if (this.props.rejecting) { + return MessageCase.Rejecting; + } else if (this.props.loading || this.state.busy) { + return MessageCase.Loading; + } + + if (this.props.inviterName) { + if (this.props.invitedEmail) { + if (this.state.threePidFetchError) { + return MessageCase.OtherThreePIDError; + } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().getUserId()) { + return MessageCase.InvitedEmailMismatch; + } + } + return MessageCase.Invite; + } else if (this.props.error) { + if (this.props.error.errcode == 'M_NOT_FOUND') { + return MessageCase.RoomNotFound; + } else { + return MessageCase.OtherError; + } + } else { + return MessageCase.ViewingRoom; + } + }, + + _getKickOrBanInfo() { const myMember = this.props.room ? this.props.room.getMember(MatrixClientPeg.get().getUserId()) : null; - const kicked = myMember && myMember.isKicked(); - const banned = myMember && myMember && myMember.membership == 'ban'; + if (!myMember) { + return {}; + } + const kickerMember = this.props.room.currentState.getMember( + myMember.events.member.getSender(), + ); + const memberName = kickerMember ? + kickerMember.name : myMember.events.member.getSender(); + const reason = myMember.events.member.getContent().reason; + return {memberName, reason}; + }, - if (this.props.inviterName) { - let emailMatchBlock; - if (this.props.invitedEmail) { - if (this.state.threePidFetchError) { - emailMatchBlock =
- { _t("Unable to ascertain that the address this invite was sent to matches one associated with your account.") } -
; - } else if (this.state.invitedEmailMxid != MatrixClientPeg.get().credentials.userId) { - emailMatchBlock = -
-
- /!\\ -
-
- { _t("This invitation was sent to an email address which is not associated with this account:") } - { this.props.invitedEmail } -
- { _t("You may wish to login with a different account, or add this email to this account.") } -
-
; - } - } - joinBlock = ( -
-
- { _t('You have been invited to join this room by %(inviterName)s', {inviterName: this.props.inviterName}) } -
-
- { _t( - 'Would you like to accept or decline this invitation?', - {}, - { - 'acceptText': (sub) => { sub }, - 'declineText': (sub) => { sub }, - }, - ) } -
- { emailMatchBlock } -
- ); - } else if (kicked || banned) { - const roomName = this._roomNameElement(); - const kickerMember = this.props.room.currentState.getMember( - myMember.events.member.getSender(), - ); - const kickerName = kickerMember ? - kickerMember.name : myMember.events.member.getSender(); - let reason; - if (myMember.events.member.getContent().reason) { - reason =
{ _t("Reason: %(reasonText)s", {reasonText: myMember.events.member.getContent().reason}) }
; - } - let rejoinBlock; - if (!banned) { - rejoinBlock = ; + _joinRule: function() { + const room = this.props.room; + if (room) { + const joinRules = room.currentState.getStateEvents('m.room.join_rules', ''); + if (joinRules) { + return joinRules.getContent().join_rule; } + } + }, - let actionText; - if (kicked) { - if (roomName) { - actionText = _t("You have been kicked from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); - } else { - actionText = _t("You have been kicked from this room by %(userName)s.", {userName: kickerName}); - } - } else if (banned) { - if (roomName) { - actionText = _t("You have been banned from %(roomName)s by %(userName)s.", {roomName: roomName, userName: kickerName}); - } else { - actionText = _t("You have been banned from this room by %(userName)s.", {userName: kickerName}); - } - } // no other options possible due to the kicked || banned check above. - - joinBlock = ( -
-
- { actionText } -
- { reason } - { rejoinBlock } - { _t("Forget room") } -
-
- ); - } else if (this.props.error) { - const name = this.props.roomAlias || _t("This room"); - let error; - if (this.props.error.errcode == 'M_NOT_FOUND') { - error = _t("%(roomName)s does not exist.", {roomName: name}); - } else { - error = _t("%(roomName)s is not accessible at this time.", {roomName: name}); - } - joinBlock = ( -
-
- { error } -
-
- ); + _roomName: function(atStart = false) { + const name = this.props.room ? this.props.room.name : this.props.roomAlias; + if (name) { + return name; + } else if (atStart) { + return _t("This room"); } else { - const name = this._roomNameElement(); - joinBlock = ( -
-
- { name ? _t('You are trying to access %(roomName)s.', {roomName: name}) : _t('You are trying to access a room.') } -
- { _t("Click here to join the discussion!", - {}, - { 'a': (sub) => { sub } }, - ) } -
-
+ return _t("this room"); + } + }, + + _getInviteMember: function() { + const {room} = this.props; + if (!room) { + return; + } + const myUserId = MatrixClientPeg.get().getUserId(); + const inviteEvent = room.currentState.getMember(myUserId); + if (!inviteEvent) { + return; + } + const inviterUserId = inviteEvent.events.member.getSender(); + return room.currentState.getMember(inviterUserId); + }, + + onLoginClick: function() { + dis.dispatch({ action: 'start_login' }); + }, + + onRegisterClick: function() { + dis.dispatch({ action: 'start_registration' }); + }, + + render: function() { + let showSpinner = false; + let darkStyle = false; + let title; + let subTitle; + let primaryActionHandler; + let primaryActionLabel; + let secondaryActionHandler; + let secondaryActionLabel; + + const messageCase = this._getMessageCase(); + switch (messageCase) { + case MessageCase.Joining: { + title = _t("Joining room …"); + showSpinner = true; + break; + } + case MessageCase.Loading: { + title = _t("Loading …"); + showSpinner = true; + break; + } + case MessageCase.Rejecting: { + title = _t("Rejecting invite …"); + showSpinner = true; + break; + } + case MessageCase.NotLoggedIn: { + darkStyle = true; + title = _t("Join the conversation with an account"); + primaryActionLabel = _t("Sign Up"); + primaryActionHandler = this.onRegisterClick; + secondaryActionLabel = _t("Sign In"); + secondaryActionHandler = this.onLoginClick; + break; + } + case MessageCase.Kicked: { + const {memberName, reason} = this._getKickOrBanInfo(); + title = _t("You were kicked from %(roomName)s by %(memberName)s", + {memberName, roomName: this._roomName()}); + subTitle = _t("Reason: %(reason)s", {reason}); + + if (this._joinRule() === "invite") { + primaryActionLabel = _t("Forget this room"); + primaryActionHandler = this.props.onForgetClick; + } else { + primaryActionLabel = _t("Re-join"); + primaryActionHandler = this.props.onJoinClick; + secondaryActionLabel = _t("Forget this room"); + secondaryActionHandler = this.props.onForgetClick; + } + break; + } + case MessageCase.Banned: { + const {memberName, reason} = this._getKickOrBanInfo(); + title = _t("You were banned from %(roomName)s by %(memberName)s", + {memberName, roomName: this._roomName()}); + subTitle = _t("Reason: %(reason)s", {reason}); + primaryActionLabel = _t("Forget this room"); + primaryActionHandler = this.props.onForgetClick; + break; + } + case MessageCase.OtherThreePIDError: { + title = _t("Something went wrong with your invite to this room"); + const joinRule = this._joinRule(); + const errCodeMessage = _t("%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.", + {errcode: this.state.threePidFetchError.errcode}, + ); + switch (joinRule) { + case "invite": + subTitle = [ + _t("You can only join it with a working invite."), + errCodeMessage, + ]; + break; + case "public": + subTitle = _t("You can still join it because this is a public room."); + primaryActionLabel = _t("Join the discussion"); + primaryActionHandler = this.props.onJoinClick; + break; + default: + subTitle = errCodeMessage; + primaryActionLabel = _t("Try to join anyway"); + primaryActionHandler = this.props.onJoinClick; + break; + } + break; + } + case MessageCase.InvitedEmailMismatch: { + title = _t("The room invite wasn't sent to your account"); + const joinRule = this._joinRule(); + if (joinRule === "public") { + subTitle = _t("You can still join it because this is a public room."); + primaryActionLabel = _t("Join the discussion"); + primaryActionHandler = this.props.onJoinClick; + } else { + subTitle = _t("Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.", {email: this.props.invitedEmail}); + if (joinRule !== "invite") { + primaryActionLabel = _t("Try to join anyway"); + primaryActionHandler = this.props.onJoinClick; + } + } + break; + } + case MessageCase.Invite: { + const inviteMember = this._getInviteMember(); + let avatar; + let inviterElement; + if (inviteMember) { + const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar"); + avatar = (); + const inviterClasses = [ + "mx_RoomPreviewBar_inviter", + getUserNameColorClass(inviteMember.userId), + ].join(" "); + inviterElement = ( + + {inviteMember.name} + + ); + } else { + inviterElement = ({this.props.inviterName}); + } + + title = _t("Do you want to join this room?"); + subTitle = [ + avatar, + _t(" invited you", {}, {userName: () => inviterElement}), + ]; + + primaryActionLabel = _t("Accept"); + primaryActionHandler = this.props.onJoinClick; + secondaryActionLabel = _t("Reject"); + secondaryActionHandler = this.props.onRejectClick; + break; + } + case MessageCase.ViewingRoom: { + if (this.props.canPreview) { + title = _t("You're previewing this room. Want to join it?"); + } else { + title = _t("%(roomName)s can't be previewed. Do you want to join it?", + {roomName: this._roomName(true)}); + } + primaryActionLabel = _t("Join the discussion"); + primaryActionHandler = this.props.onJoinClick; + break; + } + case MessageCase.RoomNotFound: { + title = _t("%(roomName)s does not exist.", {roomName: this._roomName(true)}); + subTitle = _t("This room doesn't exist. Are you sure you're at the right place?"); + break; + } + case MessageCase.OtherError: { + title = _t("%(roomName)s is not accessible at this time.", {roomName: this._roomName(true)}); + subTitle = [ + _t("Try again later, or ask a room admin to check if you have access."), + _t("%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.", + { errcode: this.props.error.errcode }, + { issueLink: label => { label } }, + ), + ]; + break; + } + } + + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + const Spinner = sdk.getComponent('elements.Spinner'); + + let subTitleElements; + if (subTitle) { + if (!Array.isArray(subTitle)) { + subTitle = [subTitle]; + } + subTitleElements = subTitle.map((t, i) =>

{t}

); + } + + let titleElement; + if (showSpinner) { + titleElement =

{ title }

; + } else { + titleElement =

{ title }

; + } + + let primaryButton; + if (primaryActionHandler) { + primaryButton = ( + + { primaryActionLabel } + ); } - if (this.props.canPreview) { - previewBlock = ( -
- { _t('This is a preview of this room. Room interactions have been disabled') }. -
+ let secondaryButton; + if (secondaryActionHandler) { + secondaryButton = ( + + { secondaryActionLabel } + ); } + const classes = classNames("mx_RoomPreviewBar", "dark-panel", `mx_RoomPreviewBar_${messageCase}`, { + "mx_RoomPreviewBar_panel": this.props.canPreview, + "mx_RoomPreviewBar_dialog": !this.props.canPreview, + "mx_RoomPreviewBar_dark": darkStyle, + }); + return ( -
-
- { joinBlock } - { previewBlock } +
+
+ { titleElement } + { subTitleElements } +
+
+ { secondaryButton } + { primaryButton }
); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index d0f265e430..2b50fd9ad3 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -601,7 +601,7 @@ "This room is not accessible by remote Matrix servers": "This room is not accessible by remote Matrix servers", "Upgrade this room to the recommended room version": "Upgrade this room to the recommended room version", "this room": "this room", - "View older messages in %(roomName)s": "View older messages in %(roomName)s", + "View older messages in %(roomName)s.": "View older messages in %(roomName)s.", "Room information": "Room information", "Internal room ID:": "Internal room ID:", "Room version": "Room version", @@ -792,25 +792,36 @@ "Low priority": "Low priority", "Historical": "Historical", "System Alerts": "System Alerts", - "Joining room...": "Joining room...", - "Unable to ascertain that the address this invite was sent to matches one associated with your account.": "Unable to ascertain that the address this invite was sent to matches one associated with your account.", - "This invitation was sent to an email address which is not associated with this account:": "This invitation was sent to an email address which is not associated with this account:", - "You may wish to login with a different account, or add this email to this account.": "You may wish to login with a different account, or add this email to this account.", - "You have been invited to join this room by %(inviterName)s": "You have been invited to join this room by %(inviterName)s", - "Would you like to accept or decline this invitation?": "Would you like to accept or decline this invitation?", - "Reason: %(reasonText)s": "Reason: %(reasonText)s", - "Rejoin": "Rejoin", - "You have been kicked from %(roomName)s by %(userName)s.": "You have been kicked from %(roomName)s by %(userName)s.", - "You have been kicked from this room by %(userName)s.": "You have been kicked from this room by %(userName)s.", - "You have been banned from %(roomName)s by %(userName)s.": "You have been banned from %(roomName)s by %(userName)s.", - "You have been banned from this room by %(userName)s.": "You have been banned from this room by %(userName)s.", "This room": "This room", + "Joining room …": "Joining room …", + "Loading …": "Loading …", + "Rejecting invite …": "Rejecting invite …", + "Join the conversation with an account": "Join the conversation with an account", + "Sign Up": "Sign Up", + "Sign In": "Sign In", + "You were kicked from %(roomName)s by %(memberName)s": "You were kicked from %(roomName)s by %(memberName)s", + "Reason: %(reason)s": "Reason: %(reason)s", + "Forget this room": "Forget this room", + "Re-join": "Re-join", + "You were banned from %(roomName)s by %(memberName)s": "You were banned from %(roomName)s by %(memberName)s", + "Something went wrong with your invite to this room": "Something went wrong with your invite to this room", + "%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.": "%(errcode)s was returned while trying to valide your invite. You could try to pass this information on to a room admin.", + "You can only join it with a working invite.": "You can only join it with a working invite.", + "You can still join it because this is a public room.": "You can still join it because this is a public room.", + "Join the discussion": "Join the discussion", + "Try to join anyway": "Try to join anyway", + "The room invite wasn't sent to your account": "The room invite wasn't sent to your account", + "Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.": "Sign in with a different account, ask for another invite, or add the e-mail address %(email)s to this account.", + "Do you want to join this room?": "Do you want to join this room?", + " invited you": " invited you", + "Reject": "Reject", + "You're previewing this room. Want to join it?": "You're previewing this room. Want to join it?", + "%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s can't be previewed. Do you want to join it?", "%(roomName)s does not exist.": "%(roomName)s does not exist.", + "This room doesn't exist. Are you sure you're at the right place?": "This room doesn't exist. Are you sure you're at the right place?", "%(roomName)s is not accessible at this time.": "%(roomName)s is not accessible at this time.", - "You are trying to access %(roomName)s.": "You are trying to access %(roomName)s.", - "You are trying to access a room.": "You are trying to access a room.", - "Click here to join the discussion!": "Click here to join the discussion!", - "This is a preview of this room. Room interactions have been disabled": "This is a preview of this room. Room interactions have been disabled", + "Try again later, or ask a room admin to check if you have access.": "Try again later, or ask a room admin to check if you have access.", + "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.": "%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please submit a bug report.", "Use Key Backup": "Use Key Backup", "Never lose encrypted messages": "Never lose encrypted messages", "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.": "Messages in this room are secured with end-to-end encryption. Only you and the recipient(s) have the keys to read these messages.", @@ -1245,7 +1256,6 @@ "Reject invitation": "Reject invitation", "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", "Unable to reject invite": "Unable to reject invite", - "Reject": "Reject", "You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)", "Resend": "Resend", "Cancel Sending": "Cancel Sending", @@ -1279,7 +1289,6 @@ "Hide": "Hide", "Home": "Home", "Sign in": "Sign in", - "Login": "Login", "powered by Matrix": "powered by Matrix", "This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.", "Custom Server Options": "Custom Server Options", diff --git a/src/utils/FormattingUtils.js b/src/utils/FormattingUtils.js index 435b33ac5d..b461d22079 100644 --- a/src/utils/FormattingUtils.js +++ b/src/utils/FormattingUtils.js @@ -58,3 +58,8 @@ export function hashCode(str) { } return Math.abs(hash); } + +export function getUserNameColorClass(userId) { + const colorNumber = (hashCode(userId) % 8) + 1; + return `mx_Username_color${colorNumber}`; +}