UI refresh for uploaded files
Fixes https://github.com/vector-im/element-web/issues/16557 Fixes https://github.com/vector-im/element-web/issues/9482 (technically) There's two changes in this: 1. The actual file body in the timeline now has a placeholder thing. 2. We're intentionally dropping all the "Travis uploaded a file" sender profile states.
This commit is contained in:
parent
c73ad9e594
commit
5d6e3d5711
12 changed files with 88 additions and 23 deletions
|
@ -45,3 +45,46 @@ limitations under the License.
|
||||||
* big the content of the iframe is. */
|
* big the content of the iframe is. */
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MFileBody_info {
|
||||||
|
background-color: $message-body-panel-bg-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
width: 270px;
|
||||||
|
padding: 8px;
|
||||||
|
color: $message-body-panel-fg-color;
|
||||||
|
|
||||||
|
.mx_MFileBody_info_icon {
|
||||||
|
background-color: $message-body-panel-icon-bg-color;
|
||||||
|
border-radius: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 12px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
mask-size: cover;
|
||||||
|
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
|
||||||
|
background-color: $message-body-panel-fg-color;
|
||||||
|
width: 13px;
|
||||||
|
height: 15px;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: 8px;
|
||||||
|
left: 9px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MFileBody_info_filename {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
display: inline-block;
|
||||||
|
width: calc(100% - 32px - 12px); // 32px icon, 12px margin on the icon
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -203,6 +203,10 @@ $breadcrumb-placeholder-bg-color: #272c35;
|
||||||
|
|
||||||
$user-tile-hover-bg-color: $header-panel-bg-color;
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
$message-body-panel-bg-color: #21262c82;
|
||||||
|
$message-body-panel-icon-bg-color: #8e99a4;
|
||||||
|
$message-body-panel-fg-color: $primary-fg-color;
|
||||||
|
|
||||||
// Appearance tab colors
|
// Appearance tab colors
|
||||||
$appearance-tab-border-color: $room-highlight-color;
|
$appearance-tab-border-color: $room-highlight-color;
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,10 @@ $breadcrumb-placeholder-bg-color: #272c35;
|
||||||
|
|
||||||
$user-tile-hover-bg-color: $header-panel-bg-color;
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
$message-body-panel-bg-color: #21262c82;
|
||||||
|
$message-body-panel-icon-bg-color: #8e99a4;
|
||||||
|
$message-body-panel-fg-color: $primary-fg-color;
|
||||||
|
|
||||||
// Appearance tab colors
|
// Appearance tab colors
|
||||||
$appearance-tab-border-color: $room-highlight-color;
|
$appearance-tab-border-color: $room-highlight-color;
|
||||||
|
|
||||||
|
|
|
@ -322,6 +322,10 @@ $breadcrumb-placeholder-bg-color: #e8eef5;
|
||||||
|
|
||||||
$user-tile-hover-bg-color: $header-panel-bg-color;
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
$message-body-panel-bg-color: #e3e8f082;
|
||||||
|
$message-body-panel-icon-bg-color: #ffffff;
|
||||||
|
$message-body-panel-fg-color: $muted-fg-color;
|
||||||
|
|
||||||
// FontSlider colors
|
// FontSlider colors
|
||||||
$appearance-tab-border-color: $input-darker-bg-color;
|
$appearance-tab-border-color: $input-darker-bg-color;
|
||||||
|
|
||||||
|
|
|
@ -323,6 +323,10 @@ $breadcrumb-placeholder-bg-color: #e8eef5;
|
||||||
|
|
||||||
$user-tile-hover-bg-color: $header-panel-bg-color;
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
|
$message-body-panel-bg-color: #e3e8f082;
|
||||||
|
$message-body-panel-icon-bg-color: #ffffff;
|
||||||
|
$message-body-panel-fg-color: $muted-fg-color;
|
||||||
|
|
||||||
// FontSlider colors
|
// FontSlider colors
|
||||||
$appearance-tab-border-color: $input-darker-bg-color;
|
$appearance-tab-border-color: $input-darker-bg-color;
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ export default class MAudioBody extends React.Component {
|
||||||
return (
|
return (
|
||||||
<span className="mx_MAudioBody">
|
<span className="mx_MAudioBody">
|
||||||
<audio src={contentUrl} controls />
|
<audio src={contentUrl} controls />
|
||||||
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
|
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,6 +126,12 @@ export default class MFileBody extends React.Component {
|
||||||
onHeightChanged: PropTypes.func,
|
onHeightChanged: PropTypes.func,
|
||||||
/* the shape of the tile, used */
|
/* the shape of the tile, used */
|
||||||
tileShape: PropTypes.string,
|
tileShape: PropTypes.string,
|
||||||
|
/* whether or not to show the default placeholder for the file. Defaults to true. */
|
||||||
|
showGenericPlaceholder: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
showGenericPlaceholder: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -145,9 +151,10 @@ export default class MFileBody extends React.Component {
|
||||||
* link text.
|
* link text.
|
||||||
*
|
*
|
||||||
* @param {Object} content The "content" key of the matrix event.
|
* @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.
|
* @return {string} the human readable link text for the attachment.
|
||||||
*/
|
*/
|
||||||
presentableTextForFile(content) {
|
presentableTextForFile(content, withSize = true) {
|
||||||
let linkText = _t("Attachment");
|
let linkText = _t("Attachment");
|
||||||
if (content.body && content.body.length > 0) {
|
if (content.body && content.body.length > 0) {
|
||||||
// The content body should be the name of the file including a
|
// The content body should be the name of the file including a
|
||||||
|
@ -155,7 +162,7 @@ export default class MFileBody extends React.Component {
|
||||||
linkText = content.body;
|
linkText = content.body;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (content.info && content.info.size) {
|
if (content.info && content.info.size && withSize) {
|
||||||
// If we know the size of the file then add it as human readable
|
// 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
|
// string to the end of the link text so that the user knows how
|
||||||
// big a file they are downloading.
|
// big a file they are downloading.
|
||||||
|
@ -218,6 +225,16 @@ export default class MFileBody extends React.Component {
|
||||||
const fileSize = content.info ? content.info.size : null;
|
const fileSize = content.info ? content.info.size : null;
|
||||||
const fileType = content.info ? content.info.mimetype : "application/octet-stream";
|
const fileType = content.info ? content.info.mimetype : "application/octet-stream";
|
||||||
|
|
||||||
|
let placeholder = null;
|
||||||
|
if (this.props.showGenericPlaceholder) {
|
||||||
|
placeholder = (
|
||||||
|
<div className="mx_MFileBody_info">
|
||||||
|
<span className="mx_MFileBody_info_icon" />
|
||||||
|
<span className="mx_MFileBody_info_filename">{this.presentableTextForFile(content, false)}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isEncrypted) {
|
if (isEncrypted) {
|
||||||
if (this.state.decryptedBlob === null) {
|
if (this.state.decryptedBlob === null) {
|
||||||
// Need to decrypt the attachment
|
// Need to decrypt the attachment
|
||||||
|
@ -248,6 +265,7 @@ export default class MFileBody extends React.Component {
|
||||||
// but it is not guaranteed between various browsers' settings.
|
// but it is not guaranteed between various browsers' settings.
|
||||||
return (
|
return (
|
||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
|
{placeholder}
|
||||||
<div className="mx_MFileBody_download">
|
<div className="mx_MFileBody_download">
|
||||||
<AccessibleButton onClick={decrypt}>
|
<AccessibleButton onClick={decrypt}>
|
||||||
{ _t("Decrypt %(text)s", { text: text }) }
|
{ _t("Decrypt %(text)s", { text: text }) }
|
||||||
|
@ -278,6 +296,7 @@ export default class MFileBody extends React.Component {
|
||||||
// If the attachment is encrypted then put the link inside an iframe.
|
// If the attachment is encrypted then put the link inside an iframe.
|
||||||
return (
|
return (
|
||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
|
{placeholder}
|
||||||
<div className="mx_MFileBody_download">
|
<div className="mx_MFileBody_download">
|
||||||
<div style={{display: "none"}}>
|
<div style={{display: "none"}}>
|
||||||
{ /*
|
{ /*
|
||||||
|
@ -346,6 +365,7 @@ export default class MFileBody extends React.Component {
|
||||||
if (this.props.tileShape === "file_grid") {
|
if (this.props.tileShape === "file_grid") {
|
||||||
return (
|
return (
|
||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
|
{placeholder}
|
||||||
<div className="mx_MFileBody_download">
|
<div className="mx_MFileBody_download">
|
||||||
<a className="mx_MFileBody_downloadLink" {...downloadProps}>
|
<a className="mx_MFileBody_downloadLink" {...downloadProps}>
|
||||||
{ fileName }
|
{ fileName }
|
||||||
|
@ -359,6 +379,7 @@ export default class MFileBody extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<span className="mx_MFileBody">
|
<span className="mx_MFileBody">
|
||||||
|
{placeholder}
|
||||||
<div className="mx_MFileBody_download">
|
<div className="mx_MFileBody_download">
|
||||||
<a {...downloadProps}>
|
<a {...downloadProps}>
|
||||||
<img src={tintedDownloadImageURL} width="12" height="14" ref={this._downloadImage} />
|
<img src={tintedDownloadImageURL} width="12" height="14" ref={this._downloadImage} />
|
||||||
|
@ -371,6 +392,7 @@ export default class MFileBody extends React.Component {
|
||||||
} else {
|
} else {
|
||||||
const extra = text ? (': ' + text) : '';
|
const extra = text ? (': ' + text) : '';
|
||||||
return <span className="mx_MFileBody">
|
return <span className="mx_MFileBody">
|
||||||
|
{placeholder}
|
||||||
{ _t("Invalid file%(extra)s", { extra: extra }) }
|
{ _t("Invalid file%(extra)s", { extra: extra }) }
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -452,7 +452,7 @@ export default class MImageBody extends React.Component {
|
||||||
|
|
||||||
// Overidden by MStickerBody
|
// Overidden by MStickerBody
|
||||||
getFileBody() {
|
getFileBody() {
|
||||||
return <MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />;
|
return <MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -243,7 +243,7 @@ export default class MVideoBody extends React.PureComponent<IProps, IState> {
|
||||||
onPlay={this.videoOnPlay}
|
onPlay={this.videoOnPlay}
|
||||||
>
|
>
|
||||||
</video>
|
</video>
|
||||||
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} />
|
<MFileBody {...this.props} decryptedBlob={this.state.decryptedBlob} showGenericPlaceholder={false} />
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
export default class SenderProfile extends React.Component {
|
export default class SenderProfile extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
mxEvent: PropTypes.object.isRequired, // event whose sender we're showing
|
mxEvent: PropTypes.object.isRequired, // event whose sender we're showing
|
||||||
text: PropTypes.string, // Text to show. Defaults to sender name
|
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,17 +117,10 @@ export default class SenderProfile extends React.Component {
|
||||||
{ flair }
|
{ flair }
|
||||||
</span>;
|
</span>;
|
||||||
|
|
||||||
const content = this.props.text ?
|
|
||||||
<span>
|
|
||||||
<span className="mx_SenderProfile_aux">
|
|
||||||
{ _t(this.props.text, { senderName: () => nameElem }) }
|
|
||||||
</span>
|
|
||||||
</span> : nameFlair;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}>
|
<div className="mx_SenderProfile" dir="auto" onClick={this.props.onClick}>
|
||||||
<div className="mx_SenderProfile_hover">
|
<div className="mx_SenderProfile_hover">
|
||||||
{ content }
|
{ nameFlair }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -768,15 +768,10 @@ export default class EventTile extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsSenderProfile) {
|
if (needsSenderProfile) {
|
||||||
let text = null;
|
|
||||||
if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') {
|
if (!this.props.tileShape || this.props.tileShape === 'reply' || this.props.tileShape === 'reply_preview') {
|
||||||
if (msgtype === 'm.image') text = _td('%(senderName)s sent an image');
|
|
||||||
else if (msgtype === 'm.video') text = _td('%(senderName)s sent a video');
|
|
||||||
else if (msgtype === 'm.file') text = _td('%(senderName)s uploaded a file');
|
|
||||||
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
sender = <SenderProfile onClick={this.onSenderProfileClick}
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
enableFlair={this.props.enableFlair && !text}
|
enableFlair={this.props.enableFlair} />;
|
||||||
text={text} />;
|
|
||||||
} else {
|
} else {
|
||||||
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={this.props.enableFlair} />;
|
sender = <SenderProfile mxEvent={this.props.mxEvent} enableFlair={this.props.enableFlair} />;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1430,9 +1430,6 @@
|
||||||
"Edit message": "Edit message",
|
"Edit message": "Edit message",
|
||||||
"Mod": "Mod",
|
"Mod": "Mod",
|
||||||
"This event could not be displayed": "This event could not be displayed",
|
"This event could not be displayed": "This event could not be displayed",
|
||||||
"%(senderName)s sent an image": "%(senderName)s sent an image",
|
|
||||||
"%(senderName)s sent a video": "%(senderName)s sent a video",
|
|
||||||
"%(senderName)s uploaded a file": "%(senderName)s uploaded a file",
|
|
||||||
"Your key share request has been sent - please check your other sessions for key share requests.": "Your key share request has been sent - please check your other sessions for key share requests.",
|
"Your key share request has been sent - please check your other sessions for key share requests.": "Your key share request has been sent - please check your other sessions for key share requests.",
|
||||||
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.",
|
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.",
|
||||||
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "If your other sessions do not have the key for this message you will not be able to decrypt them.",
|
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "If your other sessions do not have the key for this message you will not be able to decrypt them.",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue