mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-29 03:49:28 +08:00
Merge branch 'develop' into show-room-name
This commit is contained in:
commit
58ab5c36bb
@ -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)
|
||||
|
@ -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": {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ limitations under the License.
|
||||
mask-size: contain;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
top: 1px;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -755,6 +755,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||
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<IProps, IState> {
|
||||
// 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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',
|
||||
|
@ -33,6 +33,7 @@ interface IProps {
|
||||
previousPhase?: RightPanelPhases;
|
||||
closeLabel?: string;
|
||||
onClose?(): void;
|
||||
refireParams?;
|
||||
}
|
||||
|
||||
interface IGroupProps {
|
||||
@ -56,6 +57,7 @@ const BaseCard: React.FC<IProps> = ({
|
||||
withoutScrollContainer,
|
||||
previousPhase,
|
||||
children,
|
||||
refireParams,
|
||||
}) => {
|
||||
let backButton;
|
||||
if (previousPhase) {
|
||||
@ -63,6 +65,7 @@ const BaseCard: React.FC<IProps> = ({
|
||||
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: previousPhase,
|
||||
refireParams: refireParams,
|
||||
});
|
||||
};
|
||||
backButton = <AccessibleButton className="mx_BaseCard_back" onClick={onBackClick} title={_t("Back")} />;
|
||||
|
@ -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<Props> = ({
|
||||
|
||||
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<SetRightPanelPhasePayload>({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: previousPhase,
|
||||
refireParams: refireParams,
|
||||
});
|
||||
}
|
||||
|
||||
let content;
|
||||
switch (phase) {
|
||||
case RightPanelPhases.RoomMemberInfo:
|
||||
@ -1553,19 +1572,13 @@ const UserInfo: React.FC<Props> = ({
|
||||
<EncryptionPanel
|
||||
{...props as React.ComponentProps<typeof EncryptionPanel>}
|
||||
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<typeof EncryptionPanel>).verificationRequest;
|
||||
@ -1581,6 +1594,7 @@ const UserInfo: React.FC<Props> = ({
|
||||
onClose={onClose}
|
||||
closeLabel={closeLabel}
|
||||
previousPhase={previousPhase}
|
||||
refireParams={refireParams}
|
||||
>
|
||||
{ content }
|
||||
</BaseCard>;
|
||||
|
@ -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 = (
|
||||
<div className="mx_ProfileSettings_buttons">
|
||||
<AccessibleButton
|
||||
onClick={this._clearProfile}
|
||||
onClick={this._cancelProfileChanges}
|
||||
kind="link"
|
||||
disabled={!this.state.enableProfileSave}
|
||||
>
|
||||
|
@ -426,7 +426,8 @@ export default class MessageComposer extends React.Component {
|
||||
<EmojiButton key="emoji_button" addEmoji={this.addEmoji} />,
|
||||
);
|
||||
|
||||
if (SettingsStore.getValue(UIFeature.Widgets)) {
|
||||
if (SettingsStore.getValue(UIFeature.Widgets) &&
|
||||
SettingsStore.getValue("MessageComposerInput.showStickersButton")) {
|
||||
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -264,7 +264,7 @@ export default class Stickerpicker extends React.Component {
|
||||
width: this.popoverWidth,
|
||||
}}
|
||||
>
|
||||
<PersistedElement persistKey={PERSISTED_ELEMENT_KEY} style={{zIndex: STICKERPICKER_Z_INDEX}}>
|
||||
<PersistedElement persistKey={PERSISTED_ELEMENT_KEY} zIndex={STICKERPICKER_Z_INDEX}>
|
||||
<AppTile
|
||||
app={stickerApp}
|
||||
room={this.props.room}
|
||||
|
@ -52,19 +52,23 @@ export default class ProfileSettings 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,
|
||||
avatarUrl: this.state.originalAvatarUrl,
|
||||
avatarFile: null,
|
||||
});
|
||||
};
|
||||
|
||||
_saveProfile = async (e) => {
|
||||
@ -186,7 +190,7 @@ export default class ProfileSettings extends React.Component {
|
||||
</div>
|
||||
<div className="mx_ProfileSettings_buttons">
|
||||
<AccessibleButton
|
||||
onClick={this._clearProfile}
|
||||
onClick={this._cancelProfileChanges}
|
||||
kind="link"
|
||||
disabled={!this.state.enableProfileSave}
|
||||
>
|
||||
|
@ -34,6 +34,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
||||
'MessageComposerInput.suggestEmoji',
|
||||
'sendTypingNotifications',
|
||||
'MessageComposerInput.ctrlEnterToSend',
|
||||
'MessageComposerInput.showStickersButton',
|
||||
];
|
||||
|
||||
static TIMELINE_SETTINGS = [
|
||||
|
@ -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)",
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user