Add option to change the size of images/videos in the timeline (#7017)
Co-authored-by: Šimon Brandner <simon.bra.ag@gmail.com> Co-authored-by: J. Ryan Stinnett <jryans@gmail.com> Co-authored-by: Timo K <toger5@hotmail.de>
This commit is contained in:
parent
816136de97
commit
3c06e7f7a0
35 changed files with 293 additions and 45 deletions
|
@ -35,6 +35,7 @@ import classNames from 'classnames';
|
|||
import { CSSTransition, SwitchTransition } from 'react-transition-group';
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ImageSize, suggestedSize as suggestedImageSize } from "../../../settings/enums/ImageSize";
|
||||
|
||||
interface IState {
|
||||
decryptedUrl?: string;
|
||||
|
@ -58,6 +59,7 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
private unmounted = true;
|
||||
private image = createRef<HTMLImageElement>();
|
||||
private timeout?: number;
|
||||
private sizeWatcher: string;
|
||||
|
||||
constructor(props: IBodyProps) {
|
||||
super(props);
|
||||
|
@ -317,12 +319,17 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
}
|
||||
}, 150);
|
||||
}
|
||||
|
||||
this.sizeWatcher = SettingsStore.watchSetting("Images.size", null, () => {
|
||||
this.forceUpdate(); // we don't really have a reliable thing to update, so just update the whole thing
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unmounted = true;
|
||||
this.context.removeListener('sync', this.onClientSync);
|
||||
this.clearBlurhashTimeout();
|
||||
SettingsStore.unwatchSetting(this.sizeWatcher);
|
||||
}
|
||||
|
||||
protected messageContent(
|
||||
|
@ -367,11 +374,25 @@ export default class MImageBody extends React.Component<IBodyProps, IState> {
|
|||
infoHeight = this.state.loadedImageDimensions.naturalHeight;
|
||||
}
|
||||
|
||||
// The maximum height of the thumbnail as it is rendered as an <img>
|
||||
const maxHeight = forcedHeight || Math.min((this.props.maxImageHeight || 600), infoHeight);
|
||||
// The maximum width of the thumbnail, as dictated by its natural
|
||||
// maximum height.
|
||||
const maxWidth = infoWidth * maxHeight / infoHeight;
|
||||
// The maximum size of the thumbnail as it is rendered as an <img>
|
||||
// check for any height constraints
|
||||
const imageSize = SettingsStore.getValue("Images.size") as ImageSize;
|
||||
const suggestedAndPossibleWidth = Math.min(suggestedImageSize(imageSize).w, infoWidth);
|
||||
const aspectRatio = infoWidth / infoHeight;
|
||||
|
||||
let maxWidth;
|
||||
let maxHeight;
|
||||
const maxHeightConstraint = forcedHeight || this.props.maxImageHeight || undefined;
|
||||
if (maxHeightConstraint && maxHeightConstraint * aspectRatio < suggestedAndPossibleWidth) {
|
||||
// width is dictated by the maximum height that was defined by the props or the function param `forcedHeight`
|
||||
maxWidth = maxHeightConstraint * aspectRatio;
|
||||
// there is no need to check for infoHeight here since this is done with `maxHeightConstraint * aspectRatio < suggestedAndPossibleWidth`
|
||||
maxHeight = maxHeightConstraint;
|
||||
} else {
|
||||
// height is dictated by suggestedWidth (based on the Image.size setting)
|
||||
maxWidth = suggestedAndPossibleWidth;
|
||||
maxHeight = suggestedAndPossibleWidth / aspectRatio;
|
||||
}
|
||||
|
||||
let img = null;
|
||||
let placeholder = null;
|
||||
|
|
|
@ -28,6 +28,7 @@ import { IBodyProps } from "./IBodyProps";
|
|||
import MFileBody from "./MFileBody";
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ImageSize, suggestedSize as suggestedVideoSize } from "../../../settings/enums/ImageSize";
|
||||
|
||||
interface IState {
|
||||
decryptedUrl?: string;
|
||||
|
@ -42,6 +43,7 @@ interface IState {
|
|||
@replaceableComponent("views.messages.MVideoBody")
|
||||
export default class MVideoBody extends React.PureComponent<IBodyProps, IState> {
|
||||
private videoRef = React.createRef<HTMLVideoElement>();
|
||||
private sizeWatcher: string;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -57,7 +59,22 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
};
|
||||
}
|
||||
|
||||
thumbScale(fullWidth: number, fullHeight: number, thumbWidth = 480, thumbHeight = 360) {
|
||||
private get suggestedDimensions(): { w: number, h: number } {
|
||||
return suggestedVideoSize(SettingsStore.getValue("Images.size") as ImageSize);
|
||||
}
|
||||
|
||||
private thumbScale(
|
||||
fullWidth: number,
|
||||
fullHeight: number,
|
||||
thumbWidth?: number,
|
||||
thumbHeight?: number,
|
||||
): number {
|
||||
if (!thumbWidth || !thumbHeight) {
|
||||
const dims = this.suggestedDimensions;
|
||||
thumbWidth = dims.w;
|
||||
thumbHeight = dims.h;
|
||||
}
|
||||
|
||||
if (!fullWidth || !fullHeight) {
|
||||
// Cannot calculate thumbnail height for image: missing w/h in metadata. We can't even
|
||||
// log this because it's spammy
|
||||
|
@ -68,14 +85,8 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
return 1;
|
||||
}
|
||||
const widthMulti = thumbWidth / fullWidth;
|
||||
const heightMulti = thumbHeight / fullHeight;
|
||||
if (widthMulti < heightMulti) {
|
||||
// width is the dominant dimension so scaling will be fixed on that
|
||||
return widthMulti;
|
||||
} else {
|
||||
// height is the dominant dimension so scaling will be fixed on that
|
||||
return heightMulti;
|
||||
}
|
||||
// always scale the videos based on their width.
|
||||
return widthMulti;
|
||||
}
|
||||
|
||||
private getContentUrl(): string|null {
|
||||
|
@ -152,12 +163,16 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
}
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
const autoplay = SettingsStore.getValue("autoplayVideo") as boolean;
|
||||
public async componentDidMount() {
|
||||
this.sizeWatcher = SettingsStore.watchSetting("Images.size", null, () => {
|
||||
this.forceUpdate(); // we don't really have a reliable thing to update, so just update the whole thing
|
||||
});
|
||||
|
||||
this.loadBlurhash();
|
||||
|
||||
if (this.props.mediaEventHelper.media.isEncrypted && this.state.decryptedUrl === null) {
|
||||
try {
|
||||
const autoplay = SettingsStore.getValue("autoplayVideo") as boolean;
|
||||
const thumbnailUrl = await this.props.mediaEventHelper.thumbnailUrl.value;
|
||||
if (autoplay) {
|
||||
logger.log("Preloading video");
|
||||
|
@ -189,6 +204,10 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
SettingsStore.unwatchSetting(this.sizeWatcher);
|
||||
}
|
||||
|
||||
private videoOnPlay = async () => {
|
||||
if (this.hasContentUrl() || this.state.fetchingData || this.state.error) {
|
||||
// We have the file, we are fetching the file, or there is an error.
|
||||
|
@ -249,8 +268,9 @@ export default class MVideoBody extends React.PureComponent<IBodyProps, IState>
|
|||
|
||||
const contentUrl = this.getContentUrl();
|
||||
const thumbUrl = this.getThumbUrl();
|
||||
let height = null;
|
||||
let width = null;
|
||||
const defaultDims = this.suggestedDimensions;
|
||||
let height = defaultDims.h;
|
||||
let width = defaultDims.w;
|
||||
let poster = null;
|
||||
let preload = "metadata";
|
||||
if (content.info) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue