Merge branch 'develop' into travis/new-audio

This commit is contained in:
Travis Ralston 2021-06-30 13:25:41 -06:00
commit 12d7be2b6a
672 changed files with 5382 additions and 5638 deletions

View file

@ -1,6 +1,6 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,12 +16,12 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import {formatFullDateNoTime} from '../../../DateUtils';
import {replaceableComponent} from "../../../utils/replaceableComponent";
function getdaysArray() {
import { _t } from '../../../languageHandler';
import { formatFullDateNoTime } from '../../../DateUtils';
import { replaceableComponent } from "../../../utils/replaceableComponent";
function getDaysArray(): string[] {
return [
_t('Sunday'),
_t('Monday'),
@ -33,17 +33,17 @@ function getdaysArray() {
];
}
@replaceableComponent("views.messages.DateSeparator")
export default class DateSeparator extends React.Component {
static propTypes = {
ts: PropTypes.number.isRequired,
};
interface IProps {
ts: number;
}
getLabel() {
@replaceableComponent("views.messages.DateSeparator")
export default class DateSeparator extends React.Component<IProps> {
private getLabel() {
const date = new Date(this.props.ts);
const today = new Date();
const yesterday = new Date();
const days = getdaysArray();
const days = getDaysArray();
yesterday.setDate(today.getDate() - 1);
if (date.toDateString() === today.toDateString()) {

View file

@ -14,20 +14,20 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import * as HtmlUtils from '../../../HtmlUtils';
import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils';
import {formatTime} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk/src/models/event';
import {pillifyLinks, unmountPills} from '../../../utils/pillify';
import { formatTime } from '../../../DateUtils';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import { pillifyLinks, unmountPills } from '../../../utils/pillify';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import Modal from '../../../Modal';
import classNames from 'classnames';
import RedactedBody from "./RedactedBody";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
function getReplacedContent(event) {
const originalContent = event.getOriginalContent();
@ -46,21 +46,21 @@ export default class EditHistoryMessage extends React.PureComponent {
constructor(props) {
super(props);
const cli = MatrixClientPeg.get();
const {userId} = cli.credentials;
const { userId } = cli.credentials;
const event = this.props.mxEvent;
const room = cli.getRoom(event.getRoomId());
if (event.localRedactionEvent()) {
event.localRedactionEvent().on("status", this._onAssociatedStatusChanged);
}
const canRedact = room.currentState.maySendRedactionForEvent(event, userId);
this.state = {canRedact, sendStatus: event.getAssociatedStatus()};
this.state = { canRedact, sendStatus: event.getAssociatedStatus() };
this._content = createRef();
this._pills = [];
}
_onAssociatedStatusChanged = () => {
this.setState({sendStatus: this.props.mxEvent.getAssociatedStatus()});
this.setState({ sendStatus: this.props.mxEvent.getAssociatedStatus() });
};
_onRedactClick = async () => {
@ -129,7 +129,7 @@ export default class EditHistoryMessage extends React.PureComponent {
}
render() {
const {mxEvent} = this.props;
const { mxEvent } = this.props;
const content = getReplacedContent(mxEvent);
let contentContainer;
if (mxEvent.isRedacted()) {
@ -139,7 +139,7 @@ export default class EditHistoryMessage extends React.PureComponent {
if (this.props.previousEdit) {
contentElements = editBodyDiffToHtml(getReplacedContent(this.props.previousEdit), content);
} else {
contentElements = HtmlUtils.bodyToHtml(content, null, {stripReplyFallback: true});
contentElements = HtmlUtils.bodyToHtml(content, null, { stripReplyFallback: true });
}
if (mxEvent.getContent().msgtype === "m.emote") {
const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender();

View file

@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {forwardRef, useContext} from 'react';
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import React, { forwardRef, useContext } from 'react';
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
@ -27,7 +27,7 @@ interface IProps {
mxEvent: MatrixEvent;
}
const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({mxEvent}, ref) => {
const EncryptionEvent = forwardRef<HTMLDivElement, IProps>(({ mxEvent }, ref) => {
const cli = useContext(MatrixClientContext);
const roomId = mxEvent.getRoomId();
const isRoomEncrypted = MatrixClientPeg.get().isRoomEncrypted(roomId);

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {forwardRef, ReactNode, ReactChildren} from "react";
import React, { forwardRef, ReactNode, ReactChildren } from "react";
import classNames from "classnames";
interface IProps {

View file

@ -0,0 +1,112 @@
/*
Copyright 2016 OpenMarket 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 MFileBody from './MFileBody';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import InlineSpinner from '../elements/InlineSpinner';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media";
@replaceableComponent("views.messages.MAudioBody")
export default class MAudioBody extends React.Component {
constructor(props) {
super(props);
this.state = {
playing: false,
decryptedUrl: null,
decryptedBlob: null,
error: null,
};
}
onPlayToggle() {
this.setState({
playing: !this.state.playing,
});
}
_getContentUrl() {
const media = mediaFromContent(this.props.mxEvent.getContent());
if (media.isEncrypted) {
return this.state.decryptedUrl;
} else {
return media.srcHttp;
}
}
componentDidMount() {
const content = this.props.mxEvent.getContent();
if (content.file !== undefined && this.state.decryptedUrl === null) {
let decryptedBlob;
decryptFile(content.file).then(function(blob) {
decryptedBlob = blob;
return URL.createObjectURL(decryptedBlob);
}).then((url) => {
this.setState({
decryptedUrl: url,
decryptedBlob: decryptedBlob,
});
}, (err) => {
console.warn("Unable to decrypt attachment: ", err);
this.setState({
error: err,
});
});
}
}
componentWillUnmount() {
if (this.state.decryptedUrl) {
URL.revokeObjectURL(this.state.decryptedUrl);
}
}
render() {
const content = this.props.mxEvent.getContent();
if (this.state.error !== null) {
return (
<span className="mx_MAudioBody">
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
{ _t("Error decrypting audio") }
</span>
);
}
if (content.file !== undefined && this.state.decryptedUrl === null) {
// Need to decrypt the attachment
// The attachment is decrypted in componentDidMount.
// For now add an img tag with a 16x16 spinner.
// Not sure how tall the audio player is so not sure how tall it should actually be.
return (
<span className="mx_MAudioBody">
<InlineSpinner />
</span>
);
}
const contentUrl = this._getContentUrl();
return (
<span className="mx_MAudioBody">
<audio src={contentUrl} controls />
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
</span>
);
}
}

View file

@ -14,15 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import filesize from 'filesize';
import { _t } from '../../../languageHandler';
import {decryptFile} from '../../../utils/DecryptFile';
import { decryptFile } from '../../../utils/DecryptFile';
import Modal from '../../../Modal';
import AccessibleButton from "../elements/AccessibleButton";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {mediaFromContent} from "../../../customisations/Media";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media";
import ErrorDialog from "../dialogs/ErrorDialog";
let downloadIconUrl; // cached copy of the download.svg asset for the sandboxed iframe later on
@ -242,7 +242,7 @@ export default class MFileBody extends React.Component {
<span className="mx_MFileBody">
{placeholder}
<div className="mx_MFileBody_download">
<div style={{display: "none"}}>
<div style={{ display: "none" }}>
{ /*
* Add dummy copy of the "a" tag
* We'll use it to learn how the download link

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import MFileBody from './MFileBody';
@ -27,8 +27,8 @@ import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import InlineSpinner from '../elements/InlineSpinner';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {mediaFromContent} from "../../../customisations/Media";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media";
@replaceableComponent("views.messages.MImageBody")
export default class MImageBody extends React.Component {
@ -90,7 +90,7 @@ export default class MImageBody extends React.Component {
showImage() {
localStorage.setItem("mx_ShowImage_" + this.props.mxEvent.getId(), "true");
this.setState({showImage: true});
this.setState({ showImage: true });
this._downloadImage();
}
@ -296,7 +296,7 @@ export default class MImageBody extends React.Component {
if (showImage) {
// Don't download anything becaue we don't want to display anything.
this._downloadImage();
this.setState({showImage: true});
this.setState({ showImage: true });
}
this._afterComponentDidMount();
@ -345,7 +345,7 @@ export default class MImageBody extends React.Component {
imageElement = <HiddenImagePlaceholder />;
} else {
imageElement = (
<img style={{display: 'none'}} src={thumbUrl} ref={this._image}
<img style={{ display: 'none' }} src={thumbUrl} ref={this._image}
alt={content.body}
onError={this.onImageError}
onLoad={this.onImageLoad}
@ -417,7 +417,7 @@ export default class MImageBody extends React.Component {
</div>
}
<div style={{display: !showPlaceholder ? undefined : 'none'}}>
<div style={{ display: !showPlaceholder ? undefined : 'none' }}>
{ img }
{ gifLabel }
</div>

View file

@ -21,7 +21,7 @@ import WidgetStore from "../../../stores/WidgetStore";
import EventTileBubble from "./EventTileBubble";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { Container, WidgetLayoutStore } from "../../../stores/widgets/WidgetLayoutStore";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
mxEvent: MatrixEvent;
@ -52,20 +52,20 @@ export default class MJitsiWidgetEvent extends React.PureComponent<IProps> {
// removed
return <EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t('Video conference ended by %(senderName)s', {senderName})}
title={_t('Video conference ended by %(senderName)s', { senderName })}
/>;
} else if (prevUrl) {
// modified
return <EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t('Video conference updated by %(senderName)s', {senderName})}
title={_t('Video conference updated by %(senderName)s', { senderName })}
subtitle={joinCopy}
/>;
} else {
// assume added
return <EventTileBubble
className="mx_MJitsiWidgetEvent"
title={_t("Video conference started by %(senderName)s", {senderName})}
title={_t("Video conference started by %(senderName)s", { senderName })}
subtitle={joinCopy}
/>;
}

View file

@ -17,12 +17,12 @@ limitations under the License.
import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import {getNameForEventRoom, userLabelForEventRoom}
import { getNameForEventRoom, userLabelForEventRoom }
from '../../../utils/KeyVerificationStateObserver';
import EventTileBubble from "./EventTileBubble";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.MKeyVerificationConclusion")
export default class MKeyVerificationConclusion extends React.Component {
@ -90,7 +90,7 @@ export default class MKeyVerificationConclusion extends React.Component {
}
render() {
const {mxEvent} = this.props;
const { mxEvent } = this.props;
const request = mxEvent.verificationRequest;
if (!this._shouldRender(mxEvent, request)) {
@ -103,15 +103,15 @@ export default class MKeyVerificationConclusion extends React.Component {
let title;
if (request.done) {
title = _t("You verified %(name)s", {name: getNameForEventRoom(request.otherUserId, mxEvent)});
title = _t("You verified %(name)s", { name: getNameForEventRoom(request.otherUserId, mxEvent) });
} else if (request.cancelled) {
const userId = request.cancellingUserId;
if (userId === myUserId) {
title = _t("You cancelled verifying %(name)s",
{name: getNameForEventRoom(request.otherUserId, mxEvent)});
{ name: getNameForEventRoom(request.otherUserId, mxEvent) });
} else {
title = _t("%(name)s cancelled verifying",
{name: getNameForEventRoom(userId, mxEvent)});
{ name: getNameForEventRoom(userId, mxEvent) });
}
}

View file

@ -53,7 +53,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
dis.dispatch({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.EncryptionPanel,
refireParams: {verificationRequest, member},
refireParams: { verificationRequest, member },
});
};
@ -90,14 +90,14 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
if (userId === myUserId) {
return _t("You accepted");
} else {
return _t("%(name)s accepted", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
return _t("%(name)s accepted", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
}
}
private cancelledLabel(userId: string) {
const client = MatrixClientPeg.get();
const myUserId = client.getUserId();
const {cancellationCode} = this.props.mxEvent.verificationRequest;
const { cancellationCode } = this.props.mxEvent.verificationRequest;
const declined = cancellationCode === "m.user";
if (userId === myUserId) {
if (declined) {
@ -107,9 +107,9 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
}
} else {
if (declined) {
return _t("%(name)s declined", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
return _t("%(name)s declined", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
} else {
return _t("%(name)s cancelled", {name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId())});
return _t("%(name)s cancelled", { name: getNameForEventRoom(userId, this.props.mxEvent.getRoomId()) });
}
}
}
@ -117,7 +117,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
public render() {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const {mxEvent} = this.props;
const { mxEvent } = this.props;
const request = mxEvent.verificationRequest;
if (!request || request.invalid) {
@ -147,7 +147,7 @@ export default class MKeyVerificationRequest extends React.Component<IProps> {
if (!request.initiatedByMe) {
const name = getNameForEventRoom(request.requestingUserId, mxEvent.getRoomId());
title = _t("%(name)s wants to verify", {name});
title = _t("%(name)s wants to verify", { name });
subtitle = userLabelForEventRoom(request.requestingUserId, mxEvent.getRoomId());
if (request.canAccept) {
stateNode = (<div className="mx_cryptoEvent_buttons">

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import MImageBody from './MImageBody';
import * as sdk from '../../../index';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.MStickerBody")
export default class MStickerBody extends MImageBody {
@ -42,8 +42,7 @@ export default class MStickerBody extends MImageBody {
// Placeholder to show in place of the sticker image if
// img onLoad hasn't fired yet.
getPlaceholder() {
const TintableSVG = sdk.getComponent('elements.TintableSvg');
return <TintableSVG src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
return <img src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
}
// Tooltip to show on mouse over
@ -53,7 +52,7 @@ export default class MStickerBody extends MImageBody {
if (!content || !content.body || !content.info || !content.info.w) return null;
const Tooltip = sdk.getComponent('elements.Tooltip');
return <div style={{left: content.info.w + 'px'}} className="mx_MStickerBody_tooltip">
return <div style={{ left: content.info.w + 'px' }} className="mx_MStickerBody_tooltip">
<Tooltip label={content.body} />
</div>;
}

View file

@ -21,8 +21,8 @@ import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import InlineSpinner from '../elements/InlineSpinner';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {mediaFromContent} from "../../../customisations/Media";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media";
interface IProps {
/* the MatrixEvent to show */
@ -51,7 +51,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
decryptedThumbnailUrl: null,
decryptedBlob: null,
error: null,
}
};
}
thumbScale(fullWidth: number, fullHeight: number, thumbWidth: number, thumbHeight: number) {
@ -182,7 +182,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
this.videoRef.current.play();
});
this.props.onHeightChanged();
}
};
render() {
const content = this.props.mxEvent.getContent();

View file

@ -15,16 +15,16 @@ limitations under the License.
*/
import React from "react";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {Playback} from "../../../voice/Playback";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Playback } from "../../../voice/Playback";
import MFileBody from "./MFileBody";
import InlineSpinner from '../elements/InlineSpinner';
import {_t} from "../../../languageHandler";
import {mediaFromContent} from "../../../customisations/Media";
import {decryptFile} from "../../../utils/DecryptFile";
import { _t } from "../../../languageHandler";
import { mediaFromContent } from "../../../customisations/Media";
import { decryptFile } from "../../../utils/DecryptFile";
import RecordingPlayback from "../audio_messages/RecordingPlayback";
import {IMediaEventContent} from "../../../customisations/models/IMediaEventContent";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
interface IProps {
mxEvent: MatrixEvent;
@ -52,9 +52,9 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
try {
const blob = await decryptFile(content.file);
buffer = await blob.arrayBuffer();
this.setState({decryptedBlob: blob});
this.setState({ decryptedBlob: blob });
} catch (e) {
this.setState({error: e});
this.setState({ error: e });
console.warn("Unable to decrypt voice message", e);
return; // stop processing the audio file
}
@ -62,7 +62,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
try {
buffer = await media.downloadSource().then(r => r.blob()).then(r => r.arrayBuffer());
} catch (e) {
this.setState({error: e});
this.setState({ error: e });
console.warn("Unable to download voice message", e);
return; // stop processing the audio file
}
@ -106,6 +106,6 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
<RecordingPlayback playback={this.state.playback} />
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
</span>
)
);
}
}

View file

@ -15,9 +15,9 @@ limitations under the License.
*/
import React from "react";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import MAudioBody from "./MAudioBody";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import SettingsStore from "../../../settings/SettingsStore";
import MVoiceMessageBody from "./MVoiceMessageBody";

View file

@ -16,24 +16,24 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {useEffect} from 'react';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { EventStatus } from 'matrix-js-sdk/src/models/event';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import dis from '../../../dispatcher/dispatcher';
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from '../../structures/ContextMenu';
import { aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu';
import { isContentActionable, canEditContent } from '../../../utils/EventUtils';
import RoomContext from "../../../contexts/RoomContext";
import Toolbar from "../../../accessibility/Toolbar";
import {RovingAccessibleTooltipButton, useRovingTabIndex} from "../../../accessibility/RovingTabIndex";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {canCancel} from "../context_menus/MessageContextMenu";
import { RovingAccessibleTooltipButton, useRovingTabIndex } from "../../../accessibility/RovingTabIndex";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { canCancel } from "../context_menus/MessageContextMenu";
import Resend from "../../../Resend";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange}) => {
const OptionsButton = ({ mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange }) => {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
const [onFocus, isActive, ref] = useRovingTabIndex(button);
useEffect(() => {
@ -48,15 +48,14 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
const replyThread = getReplyThread && getReplyThread();
const buttonRect = button.current.getBoundingClientRect();
contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}>
<MessageContextMenu
mxEvent={mxEvent}
permalinkCreator={permalinkCreator}
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
onFinished={closeMenu}
/>
</ContextMenu>;
contextMenu = <MessageContextMenu
{...aboveLeftOf(buttonRect)}
mxEvent={mxEvent}
permalinkCreator={permalinkCreator}
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
onFinished={closeMenu}
/>;
}
return <React.Fragment>
@ -74,7 +73,7 @@ const OptionsButton = ({mxEvent, getTile, getReplyThread, permalinkCreator, onFo
</React.Fragment>;
};
const ReactButton = ({mxEvent, reactions, onFocusChange}) => {
const ReactButton = ({ mxEvent, reactions, onFocusChange }) => {
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
const [onFocus, isActive, ref] = useRovingTabIndex(button);
useEffect(() => {

View file

@ -14,14 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import React, { createRef } from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import SettingsStore from "../../../settings/SettingsStore";
import {Mjolnir} from "../../../mjolnir/Mjolnir";
import { Mjolnir } from "../../../mjolnir/Mjolnir";
import RedactedBody from "./RedactedBody";
import UnknownBody from "./UnknownBody";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.MessageEvent")
export default class MessageEvent extends React.Component {

View file

@ -16,8 +16,8 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import {_t} from '../../../languageHandler';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.MjolnirBody")
export default class MjolnirBody extends React.Component {
@ -43,7 +43,7 @@ export default class MjolnirBody extends React.Component {
return (
<div className='mx_MjolnirBody'><i>{_t(
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>",
{}, {a: (sub) => <a href="#" onClick={this._onAllowClick}>{sub}</a>},
{}, { a: (sub) => <a href="#" onClick={this._onAllowClick}>{sub}</a> },
)}</i></div>
);
}

View file

@ -125,7 +125,7 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
private onDecrypted = () => {
// Decryption changes whether the event is actionable
this.forceUpdate();
}
};
private onReactionsChange = () => {
// TODO: Call `onHeightChanged` as needed
@ -136,7 +136,7 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
// has changed (this is triggered by events for that purpose only) and
// `PureComponent`s shallow state / props compare would otherwise filter this out.
this.forceUpdate();
}
};
private getMyReactions() {
const reactions = this.props.reactions;
@ -155,7 +155,7 @@ export default class ReactionsRow extends React.PureComponent<IProps, IState> {
this.setState({
showAll: true,
});
}
};
render() {
const { mxEvent, reactions } = this.props;

View file

@ -68,7 +68,7 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
"key": content,
},
});
dis.dispatch({action: "message_sent"});
dis.dispatch({ action: "message_sent" });
}
};
@ -79,13 +79,13 @@ export default class ReactionsRowButton extends React.PureComponent<IProps, ISta
tooltipRendered: true,
tooltipVisible: true,
});
}
};
onMouseLeave = () => {
this.setState({
tooltipVisible: false,
});
}
};
render() {
const { mxEvent, content, count, reactionEvents, myReactionEvent } = this.props;

View file

@ -14,19 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {useContext} from "react";
import {MatrixClient} from "matrix-js-sdk/src/client";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import React, { useContext } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from "../../../languageHandler";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {formatFullDate} from "../../../DateUtils";
import { formatFullDate } from "../../../DateUtils";
import SettingsStore from "../../../settings/SettingsStore";
interface IProps {
mxEvent: MatrixEvent;
}
const RedactedBody = React.forwardRef<any, IProps>(({mxEvent}, ref) => {
const RedactedBody = React.forwardRef<any, IProps>(({ mxEvent }, ref) => {
const cli: MatrixClient = useContext(MatrixClientContext);
let text = _t("Message deleted");

View file

@ -18,13 +18,13 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import AccessibleButton from '../elements/AccessibleButton';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import {mediaFromMxc} from "../../../customisations/Media";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromMxc } from "../../../customisations/Media";
@replaceableComponent("views.messages.RoomAvatarEvent")
export default class RoomAvatarEvent extends React.Component {
@ -60,7 +60,7 @@ export default class RoomAvatarEvent extends React.Component {
if (!ev.getContent().url || ev.getContent().url.trim().length === 0) {
return (
<div className="mx_TextualEvent">
{ _t('%(senderDisplayName)s removed the room avatar.', {senderDisplayName}) }
{ _t('%(senderDisplayName)s removed the room avatar.', { senderDisplayName }) }
</div>
);
}

View file

@ -21,9 +21,9 @@ import PropTypes from 'prop-types';
import dis from '../../../dispatcher/dispatcher';
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import EventTileBubble from "./EventTileBubble";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.RoomCreate")
export default class RoomCreate extends React.Component {

View file

@ -39,7 +39,7 @@ export default class SenderProfile extends React.Component<IProps, IState> {
private unmounted: boolean;
constructor(props: IProps) {
super(props)
super(props);
const senderId = this.props.mxEvent.getSender();
this.state = {
@ -56,7 +56,6 @@ export default class SenderProfile extends React.Component<IProps, IState> {
this.getPublicisedGroups();
}
this.context.on('RoomState.events', this.onRoomStateEvents);
}
@ -70,7 +69,7 @@ export default class SenderProfile extends React.Component<IProps, IState> {
const userGroups = await FlairStore.getPublicisedGroupsCached(
this.context, this.props.mxEvent.getSender(),
);
this.setState({userGroups});
this.setState({ userGroups });
}
}
@ -106,9 +105,9 @@ export default class SenderProfile extends React.Component<IProps, IState> {
}
render() {
const {mxEvent} = this.props;
const { mxEvent } = this.props;
const colorClass = getUserNameColorClass(mxEvent.getSender());
const {msgtype} = mxEvent.getContent();
const { msgtype } = mxEvent.getContent();
const disambiguate = mxEvent.sender?.disambiguate;
const displayName = mxEvent.sender?.rawDisplayName || mxEvent.getSender() || "";

View file

@ -190,7 +190,7 @@ export default class TextualBody extends React.Component {
const buttonRect = button.getBoundingClientRect();
const GenericTextContextMenu = sdk.getComponent('context_menus.GenericTextContextMenu');
const {close} = ContextMenu.createMenu(GenericTextContextMenu, {
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
...toRightOf(buttonRect, 2),
message: successful ? _t('Copied!') : _t('Failed to copy'),
});
@ -404,7 +404,7 @@ export default class TextualBody extends React.Component {
},
unhideWidget: () => {
this.setState({widgetHidden: false});
this.setState({ widgetHidden: false });
if (global.localStorage) {
global.localStorage.removeItem("hide_preview_" + this.props.mxEvent.getId());
}
@ -460,7 +460,7 @@ export default class TextualBody extends React.Component {
_openHistoryDialog = async () => {
const MessageEditHistoryDialog = sdk.getComponent("views.dialogs.MessageEditHistoryDialog");
Modal.createDialog(MessageEditHistoryDialog, {mxEvent: this.props.mxEvent});
Modal.createDialog(MessageEditHistoryDialog, { mxEvent: this.props.mxEvent });
};
_renderEditedMarker() {
@ -469,7 +469,7 @@ export default class TextualBody extends React.Component {
const tooltip = <div>
<div className="mx_Tooltip_title">
{_t("Edited at %(date)s", {date: dateString})}
{_t("Edited at %(date)s", { date: dateString })}
</div>
<div className="mx_Tooltip_sub">
{_t("Click to view edits")}
@ -480,7 +480,7 @@ export default class TextualBody extends React.Component {
<AccessibleTooltipButton
className="mx_EventTile_edited"
onClick={this._openHistoryDialog}
title={_t("Edited at %(date)s. Click to view edits.", {date: dateString})}
title={_t("Edited at %(date)s. Click to view edits.", { date: dateString })}
tooltip={tooltip}
>
<span>{`(${_t("edited")})`}</span>

View file

@ -18,7 +18,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import * as TextForEvent from "../../../TextForEvent";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.messages.TextualEvent")
export default class TextualEvent extends React.Component {

View file

@ -1,5 +1,5 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Copyright 2020 - 2021 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,14 +16,24 @@ limitations under the License.
import React from 'react';
import classNames from 'classnames';
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import SdkConfig from "../../../SdkConfig";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import BugReportDialog from '../dialogs/BugReportDialog';
interface IProps {
mxEvent: MatrixEvent;
}
interface IState {
error: Error;
}
@replaceableComponent("views.messages.TileErrorBoundary")
export default class TileErrorBoundary extends React.Component {
export default class TileErrorBoundary extends React.Component<IProps, IState> {
constructor(props) {
super(props);
@ -32,17 +42,13 @@ export default class TileErrorBoundary extends React.Component {
};
}
static getDerivedStateFromError(error) {
static getDerivedStateFromError(error: Error): Partial<IState> {
// Side effects are not permitted here, so we only update the state so
// that the next render shows an error message.
return { error };
}
_onBugReport = () => {
const BugReportDialog = sdk.getComponent("dialogs.BugReportDialog");
if (!BugReportDialog) {
return;
}
private onBugReport = (): void => {
Modal.createTrackedDialog('Bug Report Dialog', '', BugReportDialog, {
label: 'react-soft-crash-tile',
});
@ -60,7 +66,7 @@ export default class TileErrorBoundary extends React.Component {
let submitLogsButton;
if (SdkConfig.get().bug_report_endpoint_url) {
submitLogsButton = <a onClick={this._onBugReport} href="#">
submitLogsButton = <a onClick={this.onBugReport} href="#">
{_t("Submit logs")}
</a>;
}

View file

@ -15,9 +15,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {forwardRef} from "react";
import React, { forwardRef } from "react";
export default forwardRef(({mxEvent}, ref) => {
export default forwardRef(({ mxEvent }, ref) => {
const text = mxEvent.getContent().body;
return (
<span className="mx_UnknownBody" ref={ref}>

View file

@ -17,7 +17,7 @@ limitations under the License.
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
@replaceableComponent("views.messages.ViewSourceEvent")
@ -36,7 +36,7 @@ export default class ViewSourceEvent extends React.PureComponent {
}
componentDidMount() {
const {mxEvent} = this.props;
const { mxEvent } = this.props;
const client = MatrixClientPeg.get();
client.decryptEventIfNeeded(mxEvent);