Merge remote-tracking branch 'upstream/develop' into fix/12652/screen-share
This commit is contained in:
commit
e5188a5258
83 changed files with 2020 additions and 992 deletions
|
@ -25,7 +25,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
|||
|
||||
import ReplyThread from "../elements/ReplyThread";
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as TextForEvent from "../../../TextForEvent";
|
||||
import { hasText } from "../../../TextForEvent";
|
||||
import * as sdk from "../../../index";
|
||||
import dis from '../../../dispatcher/dispatcher';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
@ -644,7 +644,18 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
|
||||
// return early if there are no read receipts
|
||||
if (!this.props.readReceipts || this.props.readReceipts.length === 0) {
|
||||
return null;
|
||||
// We currently must include `mx_EventTile_readAvatars` in the DOM
|
||||
// of all events, as it is the positioned parent of the animated
|
||||
// read receipts. We can't let it unmount when a receipt moves
|
||||
// events, so for now we mount it for all events. Without it, the
|
||||
// animation will start from the top of the timeline (because it
|
||||
// lost its container).
|
||||
// See also https://github.com/vector-im/element-web/issues/17561
|
||||
return (
|
||||
<div className="mx_EventTile_msgOption">
|
||||
<span className="mx_EventTile_readAvatars" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker');
|
||||
|
@ -652,11 +663,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
const receiptOffset = 15;
|
||||
let left = 0;
|
||||
|
||||
const receipts = this.props.readReceipts || [];
|
||||
|
||||
if (receipts.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const receipts = this.props.readReceipts;
|
||||
|
||||
for (let i = 0; i < receipts.length; ++i) {
|
||||
const receipt = receipts[i];
|
||||
|
@ -906,6 +913,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
|
||||
}
|
||||
|
||||
// we can't use local echoes as scroll tokens, because their event IDs change.
|
||||
// Local echos have a send "status".
|
||||
const scrollToken = this.props.mxEvent.status
|
||||
? undefined
|
||||
: this.props.mxEvent.getId();
|
||||
|
||||
let avatar;
|
||||
let sender;
|
||||
let avatarSize;
|
||||
|
@ -975,7 +988,8 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
onFocusChange={this.onActionBarFocusChange}
|
||||
/> : undefined;
|
||||
|
||||
const showTimestamp = this.props.mxEvent.getTs() && (this.props.alwaysShowTimestamps || this.state.hover);
|
||||
const showTimestamp = this.props.mxEvent.getTs() &&
|
||||
(this.props.alwaysShowTimestamps || this.props.last || this.state.hover || this.state.actionBarFocused);
|
||||
const timestamp = showTimestamp ?
|
||||
<MessageTimestamp showTwelveHour={this.props.isTwelveHour} ts={this.props.mxEvent.getTs()} /> : null;
|
||||
|
||||
|
@ -1046,7 +1060,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
case 'notif': {
|
||||
const room = this.context.getRoom(this.props.mxEvent.getRoomId());
|
||||
return (
|
||||
<div className={classes} aria-live={ariaLive} aria-atomic="true">
|
||||
<li className={classes} aria-live={ariaLive} aria-atomic="true" data-scroll-tokens={scrollToken}>
|
||||
<div className="mx_EventTile_roomName">
|
||||
<RoomAvatar room={room} width={28} height={28} />
|
||||
<a href={permalink} onClick={this.onPermalinkClicked}>
|
||||
|
@ -1069,12 +1083,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
onHeightChanged={this.props.onHeightChanged}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
case 'file_grid': {
|
||||
return (
|
||||
<div className={classes} aria-live={ariaLive} aria-atomic="true">
|
||||
<li className={classes} aria-live={ariaLive} aria-atomic="true" data-scroll-tokens={scrollToken}>
|
||||
<div className="mx_EventTile_line">
|
||||
<EventTileType ref={this.tile}
|
||||
mxEvent={this.props.mxEvent}
|
||||
|
@ -1095,7 +1109,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
{ timestamp }
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1108,10 +1122,12 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
this.props.onHeightChanged,
|
||||
this.props.permalinkCreator,
|
||||
this.replyThread,
|
||||
null,
|
||||
this.props.alwaysShowTimestamps || this.state.hover,
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={classes} aria-live={ariaLive} aria-atomic="true">
|
||||
<li className={classes} aria-live={ariaLive} aria-atomic="true" data-scroll-tokens={scrollToken}>
|
||||
{ ircTimestamp }
|
||||
{ avatar }
|
||||
{ sender }
|
||||
|
@ -1129,7 +1145,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
showUrlPreview={false}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
|
@ -1139,17 +1155,18 @@ export default class EventTile extends React.Component<IProps, IState> {
|
|||
this.props.permalinkCreator,
|
||||
this.replyThread,
|
||||
this.props.layout,
|
||||
this.props.alwaysShowTimestamps || this.state.hover,
|
||||
);
|
||||
|
||||
// tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers
|
||||
return (
|
||||
React.createElement(this.props.as || "div", {
|
||||
React.createElement(this.props.as || "li", {
|
||||
"ref": this.ref,
|
||||
"className": classes,
|
||||
"tabIndex": -1,
|
||||
"aria-live": ariaLive,
|
||||
"aria-atomic": "true",
|
||||
"data-scroll-tokens": this.props["data-scroll-tokens"],
|
||||
"data-scroll-tokens": scrollToken,
|
||||
"onMouseEnter": () => this.setState({ hover: true }),
|
||||
"onMouseLeave": () => this.setState({ hover: false }),
|
||||
}, [
|
||||
|
@ -1200,7 +1217,7 @@ export function haveTileForEvent(e) {
|
|||
const handler = getHandlerTile(e);
|
||||
if (handler === undefined) return false;
|
||||
if (handler === 'messages.TextualEvent') {
|
||||
return TextForEvent.textForEvent(e) !== '';
|
||||
return hasText(e);
|
||||
} else if (handler === 'messages.RoomCreate') {
|
||||
return Boolean(e.getContent()['predecessor']);
|
||||
} else {
|
||||
|
@ -1340,11 +1357,15 @@ class SentReceipt extends React.PureComponent<ISentReceiptProps, ISentReceiptSta
|
|||
tooltip = <Tooltip className="mx_EventTile_readAvatars_receiptTooltip" label={label} yOffset={20} />;
|
||||
}
|
||||
|
||||
return <span className="mx_EventTile_readAvatars">
|
||||
<span className={receiptClasses} onMouseEnter={this.onHoverStart} onMouseLeave={this.onHoverEnd}>
|
||||
{nonCssBadge}
|
||||
{tooltip}
|
||||
</span>
|
||||
</span>;
|
||||
return (
|
||||
<div className="mx_EventTile_msgOption">
|
||||
<span className="mx_EventTile_readAvatars">
|
||||
<span className={receiptClasses} onMouseEnter={this.onHoverStart} onMouseLeave={this.onHoverEnd}>
|
||||
{nonCssBadge}
|
||||
{tooltip}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 Michael Telatynski
|
||||
|
||||
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';
|
||||
import {Key} from '../../../Keyboard';
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
||||
@replaceableComponent("views.rooms.ForwardMessage")
|
||||
export default class ForwardMessage extends React.Component {
|
||||
static propTypes = {
|
||||
onCancelClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this._onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this._onKeyDown);
|
||||
}
|
||||
|
||||
_onKeyDown = ev => {
|
||||
switch (ev.key) {
|
||||
case Key.ESCAPE:
|
||||
this.props.onCancelClick();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="mx_ForwardMessage">
|
||||
<h1>{ _t('Please select the destination room for this message') }</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -31,6 +31,17 @@ import dis from "../../../dispatcher/dispatcher";
|
|||
import SpaceStore from "../../../stores/SpaceStore";
|
||||
import {showSpaceInvite} from "../../../utils/space";
|
||||
|
||||
import { privateShouldBeEncrypted } from "../../../createRoom";
|
||||
|
||||
import EventTileBubble from "../messages/EventTileBubble";
|
||||
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
|
||||
|
||||
function hasExpectedEncryptionSettings(room): boolean {
|
||||
const isEncrypted: boolean = room._client?.isRoomEncrypted(room.roomId);
|
||||
const isPublic: boolean = room.getJoinRule() === "public";
|
||||
return isPublic || !privateShouldBeEncrypted() || isEncrypted;
|
||||
}
|
||||
|
||||
const NewRoomIntro = () => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const {room, roomId} = useContext(RoomContext);
|
||||
|
@ -166,7 +177,31 @@ const NewRoomIntro = () => {
|
|||
</React.Fragment>;
|
||||
}
|
||||
|
||||
function openRoomSettings(event) {
|
||||
event.preventDefault();
|
||||
dis.dispatch({
|
||||
action: "open_room_settings",
|
||||
initial_tab_id: ROOM_SECURITY_TAB,
|
||||
});
|
||||
}
|
||||
|
||||
const sub2 = _t(
|
||||
"Your private messages are normally encrypted, but this room isn't. "+
|
||||
"Usually this is due to an unsupported device or method being used, " +
|
||||
"like email invites. <a>Enable encryption in settings.</a>", {},
|
||||
{ a: sub => <a onClick={openRoomSettings} href="#">{sub}</a> },
|
||||
);
|
||||
|
||||
return <div className="mx_NewRoomIntro">
|
||||
|
||||
{ !hasExpectedEncryptionSettings(room) && (
|
||||
<EventTileBubble
|
||||
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
|
||||
title={_t("End-to-end encryption isn't enabled")}
|
||||
subtitle={sub2}
|
||||
/>
|
||||
)}
|
||||
|
||||
{ body }
|
||||
</div>;
|
||||
};
|
||||
|
|
|
@ -89,7 +89,7 @@ export default class ReplyPreview extends React.Component {
|
|||
</div>
|
||||
<div className="mx_ReplyPreview_clear" />
|
||||
<EventTile
|
||||
last={true}
|
||||
alwaysShowTimestamps={true}
|
||||
tileShape="reply_preview"
|
||||
mxEvent={this.state.event}
|
||||
permalinkCreator={this.props.permalinkCreator}
|
||||
|
|
|
@ -22,7 +22,6 @@ import { _t } from '../../../languageHandler';
|
|||
import { MatrixClientPeg } from '../../../MatrixClientPeg';
|
||||
import RateLimitedFunc from '../../../ratelimitedfunc';
|
||||
|
||||
import { CancelButton } from './SimpleRoomHeader';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import RoomHeaderButtons from '../right_panel/RoomHeaderButtons';
|
||||
import E2EIcon from './E2EIcon';
|
||||
|
@ -44,7 +43,6 @@ export default class RoomHeader extends React.Component {
|
|||
onSettingsClick: PropTypes.func,
|
||||
onSearchClick: PropTypes.func,
|
||||
onLeaveClick: PropTypes.func,
|
||||
onCancelClick: PropTypes.func,
|
||||
e2eStatus: PropTypes.string,
|
||||
onAppsClick: PropTypes.func,
|
||||
appsShown: PropTypes.bool,
|
||||
|
@ -54,7 +52,6 @@ export default class RoomHeader extends React.Component {
|
|||
static defaultProps = {
|
||||
editing: false,
|
||||
inRoom: false,
|
||||
onCancelClick: null,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -93,11 +90,6 @@ export default class RoomHeader extends React.Component {
|
|||
|
||||
render() {
|
||||
let searchStatus = null;
|
||||
let cancelButton = null;
|
||||
|
||||
if (this.props.onCancelClick) {
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
||||
}
|
||||
|
||||
// don't display the search count until the search completes and
|
||||
// gives us a valid (possibly zero) searchCount.
|
||||
|
@ -217,7 +209,6 @@ export default class RoomHeader extends React.Component {
|
|||
<div className="mx_RoomHeader_e2eIcon">{ e2eIcon }</div>
|
||||
{ name }
|
||||
{ topicElement }
|
||||
{ cancelButton }
|
||||
{ rightRow }
|
||||
<RoomHeaderButtons room={this.props.room} />
|
||||
</div>
|
||||
|
|
|
@ -55,6 +55,7 @@ interface IProps {
|
|||
onKeyDown: (ev: React.KeyboardEvent) => void;
|
||||
onFocus: (ev: React.FocusEvent) => void;
|
||||
onBlur: (ev: React.FocusEvent) => void;
|
||||
onResize: () => void;
|
||||
onListCollapse?: (isExpanded: boolean) => void;
|
||||
resizeNotifier: ResizeNotifier;
|
||||
isMinimized: boolean;
|
||||
|
@ -404,7 +405,9 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
|||
const newSublists = objectWithOnly(newLists, newListIds);
|
||||
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
|
||||
|
||||
this.setState({sublists, isNameFiltering});
|
||||
this.setState({sublists, isNameFiltering}, () => {
|
||||
this.props.onResize();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -454,8 +454,9 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
|||
const sublist = possibleSticky.parentElement.parentElement;
|
||||
const list = sublist.parentElement.parentElement;
|
||||
// the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky
|
||||
const isAtTop = list.scrollTop <= HEADER_HEIGHT;
|
||||
const isAtBottom = list.scrollTop >= list.scrollHeight - list.offsetHeight;
|
||||
const listScrollTop = Math.round(list.scrollTop);
|
||||
const isAtTop = listScrollTop <= Math.round(HEADER_HEIGHT);
|
||||
const isAtBottom = listScrollTop >= Math.round(list.scrollHeight - list.offsetHeight);
|
||||
const isStickyTop = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyTop');
|
||||
const isStickyBottom = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyBottom');
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ export default class SearchResultTile extends React.Component {
|
|||
|
||||
const ts1 = mxEv.getTs();
|
||||
const ret = [<DateSeparator key={ts1 + "-search"} ts={ts1} />];
|
||||
const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps");
|
||||
|
||||
const timeline = result.context.getTimeline();
|
||||
for (let j = 0; j < timeline.length; j++) {
|
||||
|
@ -67,6 +68,7 @@ export default class SearchResultTile extends React.Component {
|
|||
highlightLink={this.props.resultLink}
|
||||
onHeightChanged={this.props.onHeightChanged}
|
||||
isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")}
|
||||
alwaysShowTimestamps={alwaysShowTimestamps}
|
||||
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -16,23 +16,9 @@ limitations under the License.
|
|||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||
|
||||
// cancel button which is shared between room header and simple room header
|
||||
export function CancelButton(props) {
|
||||
const {onClick} = props;
|
||||
|
||||
return (
|
||||
<AccessibleButton className='mx_RoomHeader_cancelButton' onClick={onClick}>
|
||||
<img src={require("../../../../res/img/cancel.svg")} className='mx_filterFlipColor'
|
||||
width="18" height="18" alt={_t("Cancel")} />
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* A stripped-down room header used for things like the user settings
|
||||
* and room directory.
|
||||
|
@ -41,18 +27,13 @@ export function CancelButton(props) {
|
|||
export default class SimpleRoomHeader extends React.Component {
|
||||
static propTypes = {
|
||||
title: PropTypes.string,
|
||||
onCancelClick: PropTypes.func,
|
||||
|
||||
// `src` to a TintableSvg. Optional.
|
||||
icon: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
let cancelButton;
|
||||
let icon;
|
||||
if (this.props.onCancelClick) {
|
||||
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
|
||||
}
|
||||
if (this.props.icon) {
|
||||
const TintableSvg = sdk.getComponent('elements.TintableSvg');
|
||||
icon = <TintableSvg
|
||||
|
@ -66,7 +47,6 @@ export default class SimpleRoomHeader extends React.Component {
|
|||
<div className="mx_RoomHeader_simpleHeader">
|
||||
{ icon }
|
||||
{ this.props.title }
|
||||
{ cancelButton }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -367,7 +367,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
/**
|
||||
* Launch the integration manager on the stickers integration page
|
||||
*/
|
||||
_launchManageIntegrations() {
|
||||
_launchManageIntegrations = () => {
|
||||
// TODO: Open the right integration manager for the widget
|
||||
if (SettingsStore.getValue("feature_many_integration_managers")) {
|
||||
IntegrationManagers.sharedInstance().openAll(
|
||||
|
@ -382,7 +382,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
this.state.widgetId,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let stickerPicker;
|
||||
|
@ -401,7 +401,7 @@ export default class Stickerpicker extends React.PureComponent {
|
|||
key="controls_hide_stickers"
|
||||
className={className}
|
||||
onClick={this._onHideStickersClick}
|
||||
active={this.state.showStickers}
|
||||
active={this.state.showStickers.toString()}
|
||||
title={_t("Hide Stickers")}
|
||||
>
|
||||
</AccessibleButton>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue