Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into export-conversations

This commit is contained in:
Jaiwanth 2021-07-26 00:18:56 +05:30
commit b04bfeda33
196 changed files with 5000 additions and 3423 deletions

View file

@ -206,7 +206,7 @@ export default class CallEvent extends React.Component<IProps, IState> {
{ sender }
</div>
<div className="mx_CallEvent_type">
<div className="mx_CallEvent_type_icon"></div>
<div className="mx_CallEvent_type_icon" />
{ callType }
</div>
</div>

View file

@ -16,13 +16,14 @@ limitations under the License.
import React from "react";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { Playback } from "../../../voice/Playback";
import { Playback } from "../../../audio/Playback";
import InlineSpinner from '../elements/InlineSpinner';
import { _t } from "../../../languageHandler";
import AudioPlayer from "../audio_messages/AudioPlayer";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import MFileBody from "./MFileBody";
import { IBodyProps } from "./IBodyProps";
import { PlaybackManager } from "../../../audio/PlaybackManager";
interface IState {
error?: Error;
@ -62,7 +63,7 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
const waveform = content?.["org.matrix.msc1767.audio"]?.waveform?.map(p => p / 1024);
// We should have a buffer to work with now: let's set it up
const playback = new Playback(buffer, waveform);
const playback = PlaybackManager.instance.createPlaybackInstance(buffer, waveform);
playback.clockInfo.populatePlaceholdersFrom(this.props.mxEvent);
this.setState({ playback });
@ -75,7 +76,6 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
public render() {
if (this.state.error) {
// TODO: @@TR: Verify error state
return (
<span className="mx_MAudioBody">
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
@ -95,7 +95,6 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
}
if (!this.state.playback) {
// TODO: @@TR: Verify loading/decrypting state
return (
<span className="mx_MAudioBody">
<InlineSpinner />

View file

@ -23,7 +23,7 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
import { mediaFromContent } from "../../../customisations/Media";
import ErrorDialog from "../dialogs/ErrorDialog";
import { TileShape } from "../rooms/EventTile";
import { IContent } from "matrix-js-sdk/src";
import { presentableTextForFile } from "../../../utils/FileUtils";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import { IBodyProps } from "./IBodyProps";
@ -93,35 +93,6 @@ export function computedStyle(element: HTMLElement) {
return cssText;
}
/**
* Extracts a human readable label for the file attachment to use as
* link text.
*
* @param {Object} content The "content" key of the matrix event.
* @param {boolean} withSize Whether to include size information. Default true.
* @return {string} the human readable link text for the attachment.
*/
export function presentableTextForFile(content: IContent, withSize = true): string {
let linkText = _t("Attachment");
if (content.body && content.body.length > 0) {
// The content body should be the name of the file including a
// file extension.
linkText = content.body;
}
if (content.info && content.info.size && withSize) {
// If we know the size of the file then add it as human readable
// string to the end of the link text so that the user knows how
// big a file they are downloading.
// The content.info also contains a MIME-type but we don't display
// it since it is "ugly", users generally aren't aware what it
// means and the type of the attachment can usually be inferrered
// from the file extension.
linkText += ' (' + filesize(content.info.size) + ')';
}
return linkText;
}
interface IProps extends IBodyProps {
/* whether or not to show the default placeholder for the file. Defaults to true. */
showGenericPlaceholder: boolean;
@ -171,14 +142,14 @@ export default class MFileBody extends React.Component<IProps, IState> {
let placeholder = null;
if (this.props.showGenericPlaceholder) {
placeholder = (
<div className="mx_MFileBody_info">
<div className="mx_MediaBody mx_MFileBody_info">
<span className="mx_MFileBody_info_icon">
{ this.props.forExport ?
<img alt="Attachment" className="mx_export_attach_icon" src="icons/attach.svg" />
: null }
</span>
<span className="mx_MFileBody_info_filename">
{ presentableTextForFile(content, false) }
{ presentableTextForFile(content, _t("Attachment"), false) }
</span>
</div>
);

View file

@ -308,7 +308,10 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
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}
@ -342,8 +345,11 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
// which has the same width as the timeline
// mx_MImageBody_thumbnail resizes img to exactly container size
img = (
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref={this.image}
style={{ maxWidth: maxWidth + "px" }}
<img
className="mx_MImageBody_thumbnail"
src={thumbUrl}
ref={this.image}
style={{ maxWidth: `min(100%, ${maxWidth}px)` }}
alt={content.body}
onError={this.onImageError}
onLoad={this.onImageLoad}
@ -362,14 +368,15 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
}
const thumbnail = (
<div className="mx_MImageBody_thumbnail_container" style={{ maxHeight: maxHeight + "px", maxWidth: maxWidth + "px" }} >
{ /* Calculate aspect ratio, using %padding will size _container correctly */ }
<div style={{ paddingBottom: forcedHeight ? (forcedHeight + "px") : ((100 * infoHeight / infoWidth) + '%') }} />
<div className="mx_MImageBody_thumbnail_container" style={{ maxHeight: maxHeight + "px", maxWidth: maxWidth + "px" }}>
{ showPlaceholder &&
<div className="mx_MImageBody_thumbnail" style={{
// Constrain width here so that spinner appears central to the loaded thumbnail
maxWidth: infoWidth + "px",
}}>
<div
className="mx_MImageBody_thumbnail"
style={{
// Constrain width here so that spinner appears central to the loaded thumbnail
maxWidth: `min(100%, ${infoWidth}px)`,
}}
>
{ placeholder }
</div>
}
@ -408,7 +415,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
}
// Overidden by MStickerBody
protected getFileBody(): JSX.Element {
protected getFileBody(): string | JSX.Element {
if (this.props.forExport) return null;
// We only ever need the download bar if we're appearing outside of the timeline
if (this.props.tileShape) {
@ -421,10 +428,10 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
if (this.state.error !== null) {
return (
<span className="mx_MImageBody">
<div className="mx_MImageBody">
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
{ _t("Error decrypting image") }
</span>
</div>
);
}
@ -439,10 +446,10 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
const thumbnail = this.messageContent(contentUrl, thumbUrl, content);
const fileBody = this.getFileBody();
return <span className="mx_MImageBody">
return <div className="mx_MImageBody">
{ thumbnail }
{ fileBody }
</span>;
</div>;
}
}
@ -457,7 +464,7 @@ export class HiddenImagePlaceholder extends React.PureComponent<PlaceholderIProp
let className = 'mx_HiddenImagePlaceholder';
if (this.props.hover) className += ' mx_HiddenImagePlaceholder_hover';
return (
<div className={className} style={{ maxWidth: maxWidth }}>
<div className={className} style={{ maxWidth: `min(100%, ${maxWidth}px)` }}>
<div className='mx_HiddenImagePlaceholder_button'>
<span className='mx_HiddenImagePlaceholder_eye' />
<span>{ _t("Show image") }</span>

View file

@ -16,9 +16,11 @@ limitations under the License.
import React from "react";
import MImageBody from "./MImageBody";
import { presentableTextForFile } from "./MFileBody";
import { presentableTextForFile } from "../../../utils/FileUtils";
import { IMediaEventContent } from "../../../customisations/models/IMediaEventContent";
import SenderProfile from "./SenderProfile";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { _t } from "../../../languageHandler";
const FORCED_IMAGE_HEIGHT = 44;
@ -32,8 +34,9 @@ export default class MImageReplyBody extends MImageBody {
}
// Don't show "Download this_file.png ..."
public getFileBody(): JSX.Element {
return <>{ presentableTextForFile(this.props.mxEvent.getContent()) }</>;
public getFileBody(): string {
const sticker = this.props.mxEvent.getType() === EventType.Sticker;
return presentableTextForFile(this.props.mxEvent.getContent(), sticker ? _t("Sticker") : _t("Image"), !sticker);
}
render() {

View file

@ -23,16 +23,16 @@ import { BLURHASH_FIELD } from "../../../ContentMessages";
@replaceableComponent("views.messages.MStickerBody")
export default class MStickerBody extends MImageBody {
// Mostly empty to prevent default behaviour of MImageBody
onClick(ev) {
protected onClick = (ev: React.MouseEvent) => {
ev.preventDefault();
if (!this.state.showImage) {
this.showImage();
}
}
};
// MStickerBody doesn't need a wrapping `<a href=...>`, but it does need extra padding
// which is added by mx_MStickerBody_wrapper
wrapImage(contentUrl, children) {
protected wrapImage(contentUrl: string, children: React.ReactNode): JSX.Element {
let onClick = null;
if (!this.state.showImage) {
onClick = this.onClick;
@ -42,13 +42,13 @@ export default class MStickerBody extends MImageBody {
// Placeholder to show in place of the sticker image if
// img onLoad hasn't fired yet.
getPlaceholder(width, height) {
protected getPlaceholder(width: number, height: number): JSX.Element {
if (this.props.mxEvent.getContent().info[BLURHASH_FIELD]) return super.getPlaceholder(width, height);
return <img src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
}
// Tooltip to show on mouse over
getTooltip() {
protected getTooltip(): JSX.Element {
const content = this.props.mxEvent && this.props.mxEvent.getContent();
if (!content || !content.body || !content.info || !content.info.w) return null;
@ -60,7 +60,7 @@ export default class MStickerBody extends MImageBody {
}
// Don't show "Download this_file.png ..."
getFileBody() {
protected getFileBody() {
return null;
}
}

View file

@ -27,7 +27,6 @@ export default class MVoiceMessageBody extends MAudioBody {
// A voice message is an audio file but rendered in a special way.
public render() {
if (this.state.error) {
// TODO: @@TR: Verify error state
return (
<span className="mx_MVoiceMessageBody">
<img src={require("../../../../res/img/warning.svg")} width="16" height="16" />
@ -37,7 +36,6 @@ export default class MVoiceMessageBody extends MAudioBody {
}
if (!this.state.playback) {
// TODO: @@TR: Verify loading/decrypting state
return (
<span className="mx_MVoiceMessageBody">
<InlineSpinner />

View file

@ -78,8 +78,11 @@ export default class RoomAvatarEvent extends React.Component {
{ senderDisplayName: senderDisplayName },
{
'img': () =>
<AccessibleButton key="avatar" className="mx_RoomAvatarEvent_avatar"
onClick={this.onAvatarClick}>
<AccessibleButton
key="avatar"
className="mx_RoomAvatarEvent_avatar"
onClick={this.onAvatarClick}
>
<RoomAvatar width={14} height={14} oobData={oobData} />
</AccessibleButton>,
})

View file

@ -514,7 +514,7 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
switch (content.msgtype) {
case MsgType.Emote:
return (
<span className="mx_MEmoteBody mx_EventTile_content">
<div className="mx_MEmoteBody mx_EventTile_content">
*&nbsp;
<span
className="mx_MEmoteBody_sender"
@ -525,21 +525,21 @@ export default class TextualBody extends React.Component<IBodyProps, IState> {
&nbsp;
{ body }
{ widgets }
</span>
</div>
);
case MsgType.Notice:
return (
<span className="mx_MNoticeBody mx_EventTile_content">
<div className="mx_MNoticeBody mx_EventTile_content">
{ body }
{ widgets }
</span>
</div>
);
default: // including "m.text"
return (
<span className="mx_MTextBody mx_EventTile_content">
<div className="mx_MTextBody mx_EventTile_content">
{ body }
{ widgets }
</span>
</div>
);
}
}