diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d1595396b..43a1494497 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +Changes in [3.13.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.13.1) (2021-02-04) +===================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.13.0...v3.13.1) + + * [Release] Fix z-index of stickerpicker + [\#5618](https://github.com/matrix-org/matrix-react-sdk/pull/5618) + Changes in [3.13.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.13.0) (2021-02-03) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.13.0-rc.1...v3.13.0) diff --git a/package.json b/package.json index a5f626d09a..2263c7de32 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.13.0", + "version": "3.13.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { diff --git a/res/css/_common.scss b/res/css/_common.scss index 87336a1c03..6e9d252659 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -21,6 +21,11 @@ limitations under the License. $hover-transition: 0.08s cubic-bezier(.46, .03, .52, .96); // quadratic +$EventTile_e2e_state_indicator_width: 4px; + +$MessageTimestamp_width: 46px; /* 8 + 30 (avatar) + 8 */ +$MessageTimestamp_width_hover: calc($MessageTimestamp_width - 2 * $EventTile_e2e_state_indicator_width); + :root { font-size: 10px; } diff --git a/res/css/views/messages/_RedactedBody.scss b/res/css/views/messages/_RedactedBody.scss index e4ab0c0835..600ac0c6b7 100644 --- a/res/css/views/messages/_RedactedBody.scss +++ b/res/css/views/messages/_RedactedBody.scss @@ -30,7 +30,7 @@ limitations under the License. mask-size: contain; content: ''; position: absolute; - top: 2px; + top: 1px; left: 0; } } diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index acf3049804..42d91b3eb5 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -26,7 +26,7 @@ $left-gutter: 64px; } .mx_EventTile.mx_EventTile_info { - padding-top: 0px; + padding-top: 1px; } .mx_EventTile_avatar { @@ -37,7 +37,7 @@ $left-gutter: 64px; } .mx_EventTile.mx_EventTile_info .mx_EventTile_avatar { - top: $font-8px; + top: $font-6px; left: $left-gutter; } @@ -420,15 +420,15 @@ $left-gutter: 64px; } .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line { - border-left: $e2e-verified-color 4px solid; + border-left: $e2e-verified-color $EventTile_e2e_state_indicator_width solid; } .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line { - border-left: $e2e-unverified-color 4px solid; + border-left: $e2e-unverified-color $EventTile_e2e_state_indicator_width solid; } .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { - border-left: $e2e-unknown-color 4px solid; + border-left: $e2e-unknown-color $EventTile_e2e_state_indicator_width solid; } .mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line, @@ -446,8 +446,7 @@ $left-gutter: 64px; .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp { - left: 3px; - width: auto; + width: $MessageTimestamp_width_hover; } // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 50ef50110f..543e6ed685 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -34,7 +34,7 @@ $left-gutter: 64px; .mx_MessageTimestamp { position: absolute; - width: 46px; /* 8 + 30 (avatar) + 8 */ + width: $MessageTimestamp_width; } .mx_EventTile_line, .mx_EventTile_reply { diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 8ed4b6cd11..accdd16b4e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -755,6 +755,8 @@ export default class MatrixChat extends React.PureComponent { break; case 'on_logged_in': if ( + // Skip this handling for token login as that always calls onLoggedIn itself + !this.tokenLogin && !Lifecycle.isSoftLogout() && this.state.view !== Views.LOGIN && this.state.view !== Views.REGISTER && @@ -1652,10 +1654,16 @@ export default class MatrixChat extends React.PureComponent { // TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149 let threepidInvite: IThreepidInvite; + // if we landed here from a 3PID invite, persist it if (params.signurl && params.email) { threepidInvite = ThreepidInviteStore.instance .storeInvite(roomString, params as IThreepidInviteWireFormat); } + // otherwise check that this room doesn't already have a known invite + if (!threepidInvite) { + const invites = ThreepidInviteStore.instance.getInvites(); + threepidInvite = invites.find(invite => invite.roomId === roomString); + } // on our URLs there might be a ?via=matrix.org or similar to help // joins to the room succeed. We'll pass these through as an array diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index b8dae41ef8..371623642b 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -229,7 +229,7 @@ export default class MessagePanel extends React.Component { onAction = (payload) => { switch (payload.action) { - case "message_sent": + case "scroll_to_bottom": this.scrollToBottom(); break; } diff --git a/src/components/structures/RightPanel.js b/src/components/structures/RightPanel.js index 41f4d83743..d66049d3a5 100644 --- a/src/components/structures/RightPanel.js +++ b/src/components/structures/RightPanel.js @@ -30,7 +30,6 @@ import MatrixClientContext from "../../contexts/MatrixClientContext"; import {Action} from "../../dispatcher/actions"; import RoomSummaryCard from "../views/right_panel/RoomSummaryCard"; import WidgetCard from "../views/right_panel/WidgetCard"; -import defaultDispatcher from "../../dispatcher/dispatcher"; export default class RightPanel extends React.Component { static get propTypes() { @@ -186,7 +185,7 @@ export default class RightPanel extends React.Component { } } - onCloseUserInfo = () => { + onClose = () => { // XXX: There are three different ways of 'closing' this panel depending on what state // things are in... this knows far more than it should do about the state of the rest // of the app and is generally a bit silly. @@ -198,31 +197,21 @@ export default class RightPanel extends React.Component { dis.dispatch({ action: "view_home_page", }); - } else if (this.state.phase === RightPanelPhases.EncryptionPanel && + } else if ( + this.state.phase === RightPanelPhases.EncryptionPanel && this.state.verificationRequest && this.state.verificationRequest.pending ) { // When the user clicks close on the encryption panel cancel the pending request first if any this.state.verificationRequest.cancel(); } else { - // Otherwise we have got our user from RoomViewStore which means we're being shown - // within a room/group, so go back to the member panel if we were in the encryption panel, - // or the member list if we were in the member panel... phew. - const isEncryptionPhase = this.state.phase === RightPanelPhases.EncryptionPanel; + // the RightPanelStore has no way of knowing which mode room/group it is in, so we handle closing here dis.dispatch({ - action: Action.ViewUser, - member: isEncryptionPhase ? this.state.member : null, + action: Action.ToggleRightPanel, + type: this.props.groupId ? "group" : "room", }); } }; - onClose = () => { - // the RightPanelStore has no way of knowing which mode room/group it is in, so we handle closing here - defaultDispatcher.dispatch({ - action: Action.ToggleRightPanel, - type: this.props.groupId ? "group" : "room", - }); - }; - render() { const MemberList = sdk.getComponent('rooms.MemberList'); const UserInfo = sdk.getComponent('right_panel.UserInfo'); @@ -260,7 +249,7 @@ export default class RightPanel extends React.Component { user={this.state.member} room={this.props.room} key={roomId || this.state.member.userId} - onClose={this.onCloseUserInfo} + onClose={this.onClose} phase={this.state.phase} verificationRequest={this.state.verificationRequest} verificationRequestPromise={this.state.verificationRequestPromise} @@ -276,7 +265,7 @@ export default class RightPanel extends React.Component { user={this.state.member} groupId={this.props.groupId} key={this.state.member.userId} - onClose={this.onCloseUserInfo} />; + onClose={this.onClose} />; break; case RightPanelPhases.GroupRoomInfo: diff --git a/src/components/views/elements/PersistedElement.js b/src/components/views/elements/PersistedElement.js index 07b01cb03f..3732f644b8 100644 --- a/src/components/views/elements/PersistedElement.js +++ b/src/components/views/elements/PersistedElement.js @@ -23,6 +23,7 @@ import ResizeObserver from 'resize-observer-polyfill'; import dis from '../../../dispatcher/dispatcher'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import {isNullOrUndefined} from "matrix-js-sdk/src/utils"; // Shamelessly ripped off Modal.js. There's probably a better way // of doing reusable widgets like dialog boxes & menus where we go and @@ -61,6 +62,9 @@ export default class PersistedElement extends React.Component { // Any PersistedElements with the same persistKey will use // the same DOM container. persistKey: PropTypes.string.isRequired, + + // z-index for the element. Defaults to 9. + zIndex: PropTypes.number, }; constructor() { @@ -165,7 +169,7 @@ export default class PersistedElement extends React.Component { const parentRect = parent.getBoundingClientRect(); Object.assign(child.style, { - zIndex: 9, + zIndex: isNullOrUndefined(this.props.zIndex) ? 9 : this.props.zIndex, position: 'absolute', top: parentRect.top + 'px', left: parentRect.left + 'px', diff --git a/src/components/views/right_panel/BaseCard.tsx b/src/components/views/right_panel/BaseCard.tsx index 5927c7c3cc..09973809de 100644 --- a/src/components/views/right_panel/BaseCard.tsx +++ b/src/components/views/right_panel/BaseCard.tsx @@ -33,6 +33,7 @@ interface IProps { previousPhase?: RightPanelPhases; closeLabel?: string; onClose?(): void; + refireParams?; } interface IGroupProps { @@ -56,6 +57,7 @@ const BaseCard: React.FC = ({ withoutScrollContainer, previousPhase, children, + refireParams, }) => { let backButton; if (previousPhase) { @@ -63,6 +65,7 @@ const BaseCard: React.FC = ({ defaultDispatcher.dispatch({ action: Action.SetRightPanelPhase, phase: previousPhase, + refireParams: refireParams, }); }; backButton = ; diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx index cdb4c43b09..a4b5cd0fbb 100644 --- a/src/components/views/right_panel/UserInfo.tsx +++ b/src/components/views/right_panel/UserInfo.tsx @@ -60,6 +60,7 @@ import QuestionDialog from "../dialogs/QuestionDialog"; import ConfirmUserActionDialog from "../dialogs/ConfirmUserActionDialog"; import InfoDialog from "../dialogs/InfoDialog"; import { EventType } from "matrix-js-sdk/src/@types/event"; +import {SetRightPanelPhasePayload} from "../../../dispatcher/payloads/SetRightPanelPhasePayload"; interface IDevice { deviceId: string; @@ -1534,6 +1535,24 @@ const UserInfo: React.FC = ({ const classes = ["mx_UserInfo"]; + let refireParams; + let previousPhase: RightPanelPhases; + // We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time + if (room && phase === RightPanelPhases.EncryptionPanel) { + previousPhase = RightPanelPhases.RoomMemberInfo; + refireParams = {member: member}; + } else if (room) { + previousPhase = RightPanelPhases.RoomMemberList; + } + + const onEncryptionPanelClose = () => { + dis.dispatch({ + action: Action.SetRightPanelPhase, + phase: previousPhase, + refireParams: refireParams, + }); + } + let content; switch (phase) { case RightPanelPhases.RoomMemberInfo: @@ -1553,19 +1572,13 @@ const UserInfo: React.FC = ({ } member={member} - onClose={onClose} + onClose={onEncryptionPanelClose} isRoomEncrypted={isRoomEncrypted} /> ); break; } - let previousPhase: RightPanelPhases; - // We have no previousPhase for when viewing a UserInfo from a Group or without a Room at this time - if (room) { - previousPhase = RightPanelPhases.RoomMemberList; - } - let closeLabel = undefined; if (phase === RightPanelPhases.EncryptionPanel) { const verificationRequest = (props as React.ComponentProps).verificationRequest; @@ -1581,6 +1594,7 @@ const UserInfo: React.FC = ({ onClose={onClose} closeLabel={closeLabel} previousPhase={previousPhase} + refireParams={refireParams} > { content } ; diff --git a/src/components/views/room_settings/RoomProfileSettings.js b/src/components/views/room_settings/RoomProfileSettings.js index c76c0823e4..c651216e8c 100644 --- a/src/components/views/room_settings/RoomProfileSettings.js +++ b/src/components/views/room_settings/RoomProfileSettings.js @@ -69,19 +69,24 @@ export default class RoomProfileSettings extends React.Component { // clear file upload field so same file can be selected this._avatarUpload.current.value = ""; this.setState({ - avatarUrl: undefined, - avatarFile: undefined, + avatarUrl: null, + avatarFile: null, enableProfileSave: true, }); }; - _clearProfile = async (e) => { + _cancelProfileChanges = async (e) => { e.stopPropagation(); e.preventDefault(); if (!this.state.enableProfileSave) return; - this._removeAvatar(); - this.setState({enableProfileSave: false, displayName: this.state.originalDisplayName}); + this.setState({ + enableProfileSave: false, + displayName: this.state.originalDisplayName, + topic: this.state.originalTopic, + avatarUrl: this.state.originalAvatarUrl, + avatarFile: null, + }); }; _saveProfile = async (e) => { @@ -108,7 +113,7 @@ export default class RoomProfileSettings extends React.Component { newState.originalAvatarUrl = newState.avatarUrl; newState.avatarFile = null; } else if (this.state.originalAvatarUrl !== this.state.avatarUrl) { - await client.sendStateEvent(this.props.roomId, 'm.room.avatar', {url: undefined}, ''); + await client.sendStateEvent(this.props.roomId, 'm.room.avatar', {}, ''); } if (this.state.originalTopic !== this.state.topic) { @@ -164,11 +169,15 @@ export default class RoomProfileSettings extends React.Component { const AvatarSetting = sdk.getComponent('settings.AvatarSetting'); let profileSettingsButtons; - if (this.state.canSetTopic && this.state.canSetName) { + if ( + this.state.canSetName || + this.state.canSetTopic || + this.state.canSetAvatar + ) { profileSettingsButtons = (
diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index da430e622a..6a867386f7 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -426,7 +426,8 @@ export default class MessageComposer extends React.Component { , ); - if (SettingsStore.getValue(UIFeature.Widgets)) { + if (SettingsStore.getValue(UIFeature.Widgets) && + SettingsStore.getValue("MessageComposerInput.showStickersButton")) { controls.push(); } diff --git a/src/components/views/rooms/SendMessageComposer.js b/src/components/views/rooms/SendMessageComposer.js index 7b516e1f52..62c474e417 100644 --- a/src/components/views/rooms/SendMessageComposer.js +++ b/src/components/views/rooms/SendMessageComposer.js @@ -403,6 +403,7 @@ export default class SendMessageComposer extends React.Component { this._editorRef.clearUndoHistory(); this._editorRef.focus(); this._clearStoredEditorState(); + dis.dispatch({action: "scroll_to_bottom"}); } componentWillUnmount() { diff --git a/src/components/views/rooms/Stickerpicker.js b/src/components/views/rooms/Stickerpicker.js index 0b81f82721..5446d15671 100644 --- a/src/components/views/rooms/Stickerpicker.js +++ b/src/components/views/rooms/Stickerpicker.js @@ -264,7 +264,7 @@ export default class Stickerpicker extends React.Component { width: this.popoverWidth, }} > - + { + _cancelProfileChanges = async (e) => { e.stopPropagation(); e.preventDefault(); if (!this.state.enableProfileSave) return; - this._removeAvatar(); - this.setState({enableProfileSave: false, displayName: this.state.originalDisplayName}); + this.setState({ + enableProfileSave: false, + displayName: this.state.originalDisplayName, + avatarUrl: this.state.originalAvatarUrl, + avatarFile: null, + }); }; _saveProfile = async (e) => { @@ -186,7 +190,7 @@ export default class ProfileSettings extends React.Component {
diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js index a32549b326..91df7cb2eb 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.js @@ -34,6 +34,7 @@ export default class PreferencesUserSettingsTab extends React.Component { 'MessageComposerInput.suggestEmoji', 'sendTypingNotifications', 'MessageComposerInput.ctrlEnterToSend', + 'MessageComposerInput.showStickersButton', ]; static TIMELINE_SETTINGS = [ diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 823efd1adf..fcd8202db1 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -795,6 +795,7 @@ "Font size": "Font size", "Use custom size": "Use custom size", "Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing", + "Show stickers button": "Show stickers button", "Use a more compact ‘Modern’ layout": "Use a more compact ‘Modern’ layout", "Show a placeholder for removed messages": "Show a placeholder for removed messages", "Show join/leave messages (invites/kicks/bans unaffected)": "Show join/leave messages (invites/kicks/bans unaffected)", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 6415723f56..9e29df94b6 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -240,6 +240,11 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: true, invertedSettingName: 'MessageComposerInput.dontSuggestEmoji', }, + "MessageComposerInput.showStickersButton": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td('Show stickers button'), + default: true, + }, // TODO: Wire up appropriately to UI (FTUE notifications) "Notifications.alwaysShowBadgeCounts": { supportedLevels: LEVELS_ROOM_OR_ACCOUNT,