Remove redundant code and move presentableTextForFile out of MFileBody
Signed-off-by: Tulir Asokan <tulir@maunium.net>pull/21833/head
parent
09f9916916
commit
330f222dd1
|
@ -89,6 +89,35 @@ function computedStyle(element) {
|
||||||
return cssText;
|
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, withSize = true) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.messages.MFileBody")
|
@replaceableComponent("views.messages.MFileBody")
|
||||||
export default class MFileBody extends React.Component {
|
export default class MFileBody extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -119,35 +148,6 @@ export default class MFileBody extends React.Component {
|
||||||
this._dummyLink = createRef();
|
this._dummyLink = createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
presentableTextForFile(content, withSize = true) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_getContentUrl() {
|
_getContentUrl() {
|
||||||
const media = mediaFromContent(this.props.mxEvent.getContent());
|
const media = mediaFromContent(this.props.mxEvent.getContent());
|
||||||
return media.srcHttp;
|
return media.srcHttp;
|
||||||
|
@ -161,7 +161,7 @@ export default class MFileBody extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const content = this.props.mxEvent.getContent();
|
const content = this.props.mxEvent.getContent();
|
||||||
const text = this.presentableTextForFile(content);
|
const text = presentableTextForFile(content);
|
||||||
const isEncrypted = content.file !== undefined;
|
const isEncrypted = content.file !== undefined;
|
||||||
const fileName = content.body && content.body.length > 0 ? content.body : _t("Attachment");
|
const fileName = content.body && content.body.length > 0 ? content.body : _t("Attachment");
|
||||||
const contentUrl = this._getContentUrl();
|
const contentUrl = this._getContentUrl();
|
||||||
|
@ -173,7 +173,7 @@ export default class MFileBody extends React.Component {
|
||||||
placeholder = (
|
placeholder = (
|
||||||
<div className="mx_MFileBody_info">
|
<div className="mx_MFileBody_info">
|
||||||
<span className="mx_MFileBody_info_icon" />
|
<span className="mx_MFileBody_info_icon" />
|
||||||
<span className="mx_MFileBody_info_filename">{this.presentableTextForFile(content, false)}</span>
|
<span className="mx_MFileBody_info_filename">{presentableTextForFile(content, false)}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 Tulir Asokan <tulir@maunium.net>
|
Copyright 2020-2021 Tulir Asokan <tulir@maunium.net>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -18,7 +18,7 @@ import React from "react";
|
||||||
import {_td} from "../../../languageHandler";
|
import {_td} from "../../../languageHandler";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import MImageBody from './MImageBody';
|
import MImageBody from './MImageBody';
|
||||||
import MFileBody from "./MFileBody";
|
import {presentableTextForFile} from "./MFileBody";
|
||||||
|
|
||||||
export default class MImageReplyBody extends MImageBody {
|
export default class MImageReplyBody extends MImageBody {
|
||||||
onClick(ev) {
|
onClick(ev) {
|
||||||
|
@ -31,7 +31,7 @@ export default class MImageReplyBody extends MImageBody {
|
||||||
|
|
||||||
// Don't show "Download this_file.png ..."
|
// Don't show "Download this_file.png ..."
|
||||||
getFileBody() {
|
getFileBody() {
|
||||||
return MFileBody.prototype.presentableTextForFile.call(this, this.props.mxEvent.getContent());
|
return presentableTextForFile(this.props.mxEvent.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -45,10 +45,12 @@ export default class MImageReplyBody extends MImageBody {
|
||||||
const thumbnail = this._messageContent(contentUrl, this._getThumbUrl(), content);
|
const thumbnail = this._messageContent(contentUrl, this._getThumbUrl(), content);
|
||||||
const fileBody = this.getFileBody();
|
const fileBody = this.getFileBody();
|
||||||
const SenderProfile = sdk.getComponent('messages.SenderProfile');
|
const SenderProfile = sdk.getComponent('messages.SenderProfile');
|
||||||
const sender = <SenderProfile onClick={this.onSenderProfileClick}
|
const sender = <SenderProfile
|
||||||
|
onClick={this.onSenderProfileClick}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
enableFlair={false}
|
enableFlair={false}
|
||||||
text={_td('%(senderName)s sent an image')} />;
|
text={_td('%(senderName)s sent an image')}
|
||||||
|
/>;
|
||||||
|
|
||||||
return <div className="mx_MImageReplyBody">
|
return <div className="mx_MImageReplyBody">
|
||||||
<div className="mx_MImageReplyBody_thumbnail">{thumbnail}</div>
|
<div className="mx_MImageReplyBody_thumbnail">{thumbnail}</div>
|
||||||
|
|
|
@ -247,7 +247,7 @@ interface IProps {
|
||||||
// It could also be done by subclassing EventTile, but that'd be quite
|
// It could also be done by subclassing EventTile, but that'd be quite
|
||||||
// boiilerplatey. So just make the necessary render decisions conditional
|
// boiilerplatey. So just make the necessary render decisions conditional
|
||||||
// for now.
|
// for now.
|
||||||
tileShape?: 'notif' | 'file_grid' | 'reply' | 'reply_preview';
|
tileShape?: 'notif' | 'file_grid';
|
||||||
|
|
||||||
// show twelve hour timestamps
|
// show twelve hour timestamps
|
||||||
isTwelveHour?: boolean;
|
isTwelveHour?: boolean;
|
||||||
|
@ -940,7 +940,7 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsSenderProfile) {
|
if (needsSenderProfile) {
|
||||||
if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') {
|
if (!this.props.tileShape) {
|
||||||
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
enableFlair={this.props.enableFlair}
|
enableFlair={this.props.enableFlair}
|
||||||
|
@ -1087,39 +1087,6 @@ export default class EventTile extends React.Component<IProps, IState> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'reply':
|
|
||||||
case 'reply_preview': {
|
|
||||||
let thread;
|
|
||||||
if (this.props.tileShape === 'reply_preview') {
|
|
||||||
thread = ReplyThread.makeThread(
|
|
||||||
this.props.mxEvent,
|
|
||||||
this.props.onHeightChanged,
|
|
||||||
this.props.permalinkCreator,
|
|
||||||
this.replyThread,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div className={classes} aria-live={ariaLive} aria-atomic="true">
|
|
||||||
{ ircTimestamp }
|
|
||||||
{ avatar }
|
|
||||||
{ sender }
|
|
||||||
{ ircPadlock }
|
|
||||||
<div className="mx_EventTile_reply">
|
|
||||||
{ groupTimestamp }
|
|
||||||
{ groupPadlock }
|
|
||||||
{ thread }
|
|
||||||
<EventTileType ref={this.tile}
|
|
||||||
mxEvent={this.props.mxEvent}
|
|
||||||
highlights={this.props.highlights}
|
|
||||||
highlightLink={this.props.highlightLink}
|
|
||||||
onHeightChanged={this.props.onHeightChanged}
|
|
||||||
replacingEventId={this.props.replacingEventId}
|
|
||||||
showUrlPreview={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: {
|
default: {
|
||||||
const thread = ReplyThread.makeThread(
|
const thread = ReplyThread.makeThread(
|
||||||
this.props.mxEvent,
|
this.props.mxEvent,
|
||||||
|
|
|
@ -87,9 +87,11 @@ export default class ReplyPreview extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_ReplyPreview_clear" />
|
<div className="mx_ReplyPreview_clear" />
|
||||||
<div className="mx_ReplyPreview_tile">
|
<div className="mx_ReplyPreview_tile">
|
||||||
<ReplyTile isRedacted={this.state.event.isRedacted()}
|
<ReplyTile
|
||||||
|
isRedacted={this.state.event.isRedacted()}
|
||||||
mxEvent={this.state.event}
|
mxEvent={this.state.event}
|
||||||
permalinkCreator={this.props.permalinkCreator} />
|
permalinkCreator={this.props.permalinkCreator}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>;
|
</div>;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 Tulir Asokan <tulir@maunium.net>
|
Copyright 2020-2021 Tulir Asokan <tulir@maunium.net>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -26,41 +26,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {MatrixClient} from 'matrix-js-sdk';
|
import {MatrixClient} from 'matrix-js-sdk';
|
||||||
|
|
||||||
import {objectHasDiff} from '../../../utils/objects';
|
import {objectHasDiff} from '../../../utils/objects';
|
||||||
|
import {getHandlerTile} from "./EventTile";
|
||||||
const eventTileTypes = {
|
|
||||||
'm.room.message': 'messages.MessageEvent',
|
|
||||||
'm.sticker': 'messages.MessageEvent',
|
|
||||||
'm.call.invite': 'messages.TextualEvent',
|
|
||||||
'm.call.answer': 'messages.TextualEvent',
|
|
||||||
'm.call.hangup': 'messages.TextualEvent',
|
|
||||||
};
|
|
||||||
|
|
||||||
const stateEventTileTypes = {
|
|
||||||
'm.room.aliases': 'messages.TextualEvent',
|
|
||||||
// 'm.room.aliases': 'messages.RoomAliasesEvent', // too complex
|
|
||||||
'm.room.canonical_alias': 'messages.TextualEvent',
|
|
||||||
'm.room.create': 'messages.RoomCreate',
|
|
||||||
'm.room.member': 'messages.TextualEvent',
|
|
||||||
'm.room.name': 'messages.TextualEvent',
|
|
||||||
'm.room.avatar': 'messages.RoomAvatarEvent',
|
|
||||||
'm.room.third_party_invite': 'messages.TextualEvent',
|
|
||||||
'm.room.history_visibility': 'messages.TextualEvent',
|
|
||||||
'm.room.encryption': 'messages.TextualEvent',
|
|
||||||
'm.room.topic': 'messages.TextualEvent',
|
|
||||||
'm.room.power_levels': 'messages.TextualEvent',
|
|
||||||
'm.room.pinned_events': 'messages.TextualEvent',
|
|
||||||
'm.room.server_acl': 'messages.TextualEvent',
|
|
||||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
|
||||||
'm.room.tombstone': 'messages.TextualEvent',
|
|
||||||
'm.room.join_rules': 'messages.TextualEvent',
|
|
||||||
'm.room.guest_access': 'messages.TextualEvent',
|
|
||||||
'm.room.related_groups': 'messages.TextualEvent',
|
|
||||||
};
|
|
||||||
|
|
||||||
function getHandlerTile(ev) {
|
|
||||||
const type = ev.getType();
|
|
||||||
return ev.isState() ? stateEventTileTypes[type] : eventTileTypes[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
class ReplyTile extends React.Component {
|
class ReplyTile extends React.Component {
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
|
@ -94,7 +60,7 @@ class ReplyTile extends React.Component {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !this._propsEqual(this.props, nextProps);
|
return objectHasDiff(this.props, nextProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
|
@ -108,28 +74,6 @@ class ReplyTile extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_propsEqual(objA, objB) {
|
|
||||||
const keysA = Object.keys(objA);
|
|
||||||
const keysB = Object.keys(objB);
|
|
||||||
|
|
||||||
if (keysA.length !== keysB.length) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < keysA.length; i++) {
|
|
||||||
const key = keysA[i];
|
|
||||||
|
|
||||||
if (!objB.hasOwnProperty(key)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objA[key] !== objB[key]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
// This allows the permalink to be opened in a new tab/window or copied as
|
// This allows the permalink to be opened in a new tab/window or copied as
|
||||||
// matrix.to, but also for it to enable routing within Riot when clicked.
|
// matrix.to, but also for it to enable routing within Riot when clicked.
|
||||||
|
|
Loading…
Reference in New Issue