mirror of https://github.com/vector-im/riot-web
Add an option to hide image previews
Applies to images, stickers, and URL previews. Fixes https://github.com/vector-im/riot-web/issues/10735pull/21833/head
parent
55e834f0ae
commit
59b29e4a7f
|
@ -19,6 +19,21 @@ limitations under the License.
|
|||
margin-right: 34px;
|
||||
}
|
||||
|
||||
.mx_MImageBody_hidden {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
text-align: center;
|
||||
border: 1px dashed $input-border-color;
|
||||
|
||||
// To center the text in the middle of the frame
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mx_MImageBody_thumbnail {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
|
|
@ -22,3 +22,14 @@ limitations under the License.
|
|||
position: absolute;
|
||||
top: 50%;
|
||||
}
|
||||
|
||||
.mx_MStickerBody_hidden {
|
||||
max-width: 220px;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
|
||||
// To center the text in the middle of the frame
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ export default class MImageBody extends React.Component {
|
|||
imgLoaded: false,
|
||||
loadedImageDimensions: null,
|
||||
hover: false,
|
||||
showImage: SettingsStore.getValue("showImages"),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -86,9 +87,19 @@ export default class MImageBody extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
showImage() {
|
||||
localStorage.setItem("mx_ShowImage_" + this.props.mxEvent.getId(), "true");
|
||||
this.setState({showImage: true});
|
||||
}
|
||||
|
||||
onClick(ev) {
|
||||
if (ev.button === 0 && !ev.metaKey) {
|
||||
ev.preventDefault();
|
||||
if (!this.state.showImage) {
|
||||
this.showImage();
|
||||
return;
|
||||
}
|
||||
|
||||
const content = this.props.mxEvent.getContent();
|
||||
const httpUrl = this._getContentUrl();
|
||||
const ImageView = sdk.getComponent("elements.ImageView");
|
||||
|
@ -120,7 +131,7 @@ export default class MImageBody extends React.Component {
|
|||
onImageEnter(e) {
|
||||
this.setState({ hover: true });
|
||||
|
||||
if (!this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
|
||||
if (!this.state.showImage || !this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
|
||||
return;
|
||||
}
|
||||
const imgElement = e.target;
|
||||
|
@ -130,7 +141,7 @@ export default class MImageBody extends React.Component {
|
|||
onImageLeave(e) {
|
||||
this.setState({ hover: false });
|
||||
|
||||
if (!this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
|
||||
if (!this.state.showImage || !this._isGif() || SettingsStore.getValue("autoplayGifsAndVideos")) {
|
||||
return;
|
||||
}
|
||||
const imgElement = e.target;
|
||||
|
@ -280,6 +291,12 @@ export default class MImageBody extends React.Component {
|
|||
});
|
||||
}).done();
|
||||
}
|
||||
|
||||
// Remember that the user wanted to show this particular image
|
||||
if (!this.state.showImage && localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true") {
|
||||
this.setState({showImage: true});
|
||||
}
|
||||
|
||||
this._afterComponentDidMount();
|
||||
}
|
||||
|
||||
|
@ -321,13 +338,21 @@ export default class MImageBody extends React.Component {
|
|||
// By doing this, the image "pops" into the timeline, but is still restricted
|
||||
// by the same width and height logic below.
|
||||
if (!this.state.loadedImageDimensions) {
|
||||
return this.wrapImage(contentUrl,
|
||||
let imageElement = (
|
||||
<img style={{display: 'none'}} src={thumbUrl} ref="image"
|
||||
alt={content.body}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
/>,
|
||||
alt={content.body}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
/>
|
||||
);
|
||||
if (!this.state.showImage) {
|
||||
imageElement = (
|
||||
<div className="mx_MImageBody_hidden">
|
||||
<span>{_t("Click to show image")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return this.wrapImage(contentUrl, imageElement);
|
||||
}
|
||||
infoWidth = this.state.loadedImageDimensions.naturalWidth;
|
||||
infoHeight = this.state.loadedImageDimensions.naturalHeight;
|
||||
|
@ -362,13 +387,22 @@ export default class MImageBody extends React.Component {
|
|||
// Restrict the width of the thumbnail here, otherwise it will fill the container
|
||||
// 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="image"
|
||||
style={{ maxWidth: maxWidth + "px" }}
|
||||
alt={content.body}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
onMouseEnter={this.onImageEnter}
|
||||
onMouseLeave={this.onImageLeave} />;
|
||||
img = (
|
||||
<img className="mx_MImageBody_thumbnail" src={thumbUrl} ref="image"
|
||||
style={{ maxWidth: maxWidth + "px" }}
|
||||
alt={content.body}
|
||||
onError={this.onImageError}
|
||||
onLoad={this.onImageLoad}
|
||||
onMouseEnter={this.onImageEnter}
|
||||
onMouseLeave={this.onImageLeave} />
|
||||
);
|
||||
if (!this.state.showImage) {
|
||||
img = (
|
||||
<div style={{ maxWidth: maxWidth + "px" }} className="mx_MImageBody_hidden">
|
||||
<span>{_t("Click to show image")}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._isGif() && !SettingsStore.getValue("autoplayGifsAndVideos") && !this.state.hover) {
|
||||
|
|
|
@ -19,10 +19,15 @@ limitations under the License.
|
|||
import React from 'react';
|
||||
import MImageBody from './MImageBody';
|
||||
import sdk from '../../../index';
|
||||
import {_t} from "../../../languageHandler";
|
||||
|
||||
export default class MStickerBody extends MImageBody {
|
||||
// Empty to prevent default behaviour of MImageBody
|
||||
onClick() {
|
||||
// Mostly empty to prevent default behaviour of MImageBody
|
||||
onClick(ev) {
|
||||
ev.preventDefault();
|
||||
if (!this.state.showImage) {
|
||||
this.showImage();
|
||||
}
|
||||
}
|
||||
|
||||
// MStickerBody doesn't need a wrapping `<a href=...>`, but it does need extra padding
|
||||
|
@ -34,6 +39,14 @@ export default class MStickerBody extends MImageBody {
|
|||
// Placeholder to show in place of the sticker image if
|
||||
// img onLoad hasn't fired yet.
|
||||
getPlaceholder() {
|
||||
if (!this.state.showImage) {
|
||||
return (
|
||||
<a className="mx_MStickerBody_hidden" onClick={this.onClick} href="#">
|
||||
<span>{_t("Click to show sticker")}</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
const TintableSVG = sdk.getComponent('elements.TintableSvg');
|
||||
return <TintableSVG src={require("../../../../res/img/icons-show-stickers.svg")} width="75" height="75" />;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import createReactClass from 'create-react-class';
|
||||
import { linkifyElement } from '../../../HtmlUtils';
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
|
||||
const sdk = require('../../../index');
|
||||
const MatrixClientPeg = require('../../../MatrixClientPeg');
|
||||
|
@ -102,6 +103,9 @@ module.exports = createReactClass({
|
|||
|
||||
// FIXME: do we want to factor out all image displaying between this and MImageBody - especially for lightboxing?
|
||||
let image = p["og:image"];
|
||||
if (!SettingsStore.getValue("showImages")) {
|
||||
image = null; // Don't render a button to show the image, just hide it outright
|
||||
}
|
||||
const imageMaxWidth = 100; const imageMaxHeight = 100;
|
||||
if (image && image.startsWith("mxc://")) {
|
||||
image = MatrixClientPeg.get().mxcUrlToHttp(image, imageMaxWidth, imageMaxHeight);
|
||||
|
|
|
@ -55,6 +55,7 @@ export default class LabsUserSettingsTab extends React.Component {
|
|||
<SettingsFlag name={"showHiddenEventsInTimeline"} level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name={"lowBandwidth"} level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name={"sendReadReceipts"} level={SettingLevel.ACCOUNT} />
|
||||
<SettingsFlag name={"showImages"} level={SettingLevel.ACCOUNT} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -369,6 +369,7 @@
|
|||
"Low bandwidth mode": "Low bandwidth mode",
|
||||
"Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)",
|
||||
"Send read receipts for messages (requires compatible homeserver to disable)": "Send read receipts for messages (requires compatible homeserver to disable)",
|
||||
"Show previews/thumbnails for images": "Show previews/thumbnails for images",
|
||||
"Collecting app version information": "Collecting app version information",
|
||||
"Collecting logs": "Collecting logs",
|
||||
"Uploading report": "Uploading report",
|
||||
|
@ -1032,7 +1033,9 @@
|
|||
"Decrypt %(text)s": "Decrypt %(text)s",
|
||||
"Download %(text)s": "Download %(text)s",
|
||||
"Invalid file%(extra)s": "Invalid file%(extra)s",
|
||||
"Click to show image": "Click to show image",
|
||||
"Error decrypting image": "Error decrypting image",
|
||||
"Click to show sticker": "Click to show sticker",
|
||||
"Error decrypting video": "Error decrypting video",
|
||||
"Agree": "Agree",
|
||||
"Disagree": "Disagree",
|
||||
|
|
|
@ -409,4 +409,9 @@ export const SETTINGS = {
|
|||
),
|
||||
default: true,
|
||||
},
|
||||
"showImages": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("Show previews/thumbnails for images"),
|
||||
default: true,
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue