From d421c3203f7c823b9359cc781d0d469678405b04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 22 Jul 2021 21:26:15 +0200 Subject: [PATCH] Initial implementation of loading animation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.tsx | 35 ++++++++++++++----- src/components/views/messages/MImageBody.tsx | 11 ++++++ .../views/rooms/LinkPreviewWidget.tsx | 18 ++++++++-- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index db63dbbfc2..56c81c937e 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -34,6 +34,7 @@ import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { normalizeWheelEvent } from "../../../utils/Mouse"; import { IDialogProps } from '../dialogs/IDialogProps'; +import UIStore from '../../../stores/UIStore'; // Max scale to keep gaps around the image const MAX_SCALE = 0.95; @@ -56,8 +57,15 @@ interface IProps extends IDialogProps { // redactions, senders, timestamps etc. Other descriptors are taken from the explicit // properties above, which let us use lightboxes to display images which aren't associated // with events. - mxEvent: MatrixEvent; - permalinkCreator: RoomPermalinkCreator; + mxEvent?: MatrixEvent; + permalinkCreator?: RoomPermalinkCreator; + + thumbnailInfo?: { + positionX: number; + positionY: number; + width: number; + height: number; + }; } interface IState { @@ -75,13 +83,16 @@ interface IState { export default class ImageView extends React.Component { constructor(props) { super(props); + + const { thumbnailInfo, width } = this.props; + this.state = { - zoom: 0, + zoom: thumbnailInfo?.width / width ?? 0, minZoom: MAX_SCALE, maxZoom: MAX_SCALE, rotation: 0, - translationX: 0, - translationY: 0, + translationX: thumbnailInfo?.positionX + (thumbnailInfo?.width / 2) - (UIStore.instance.windowWidth / 2), + translationY: thumbnailInfo?.positionY + (thumbnailInfo?.height / 2) - (UIStore.instance.windowHeight / 2), moving: false, contextMenuDisplayed: false, }; @@ -105,15 +116,23 @@ export default class ImageView extends React.Component { // We want to recalculate zoom whenever the window's size changes window.addEventListener("resize", this.recalculateZoom); // After the image loads for the first time we want to calculate the zoom - this.image.current.addEventListener("load", this.recalculateZoom); + this.image.current.addEventListener("load", this.imageLoaded); } componentWillUnmount() { this.focusLock.current.removeEventListener('wheel', this.onWheel); window.removeEventListener("resize", this.recalculateZoom); - this.image.current.removeEventListener("load", this.recalculateZoom); + this.image.current.removeEventListener("load", this.imageLoaded); } + private imageLoaded =() => { + this.setZoomAndRotation(); + this.setState({ + translationX: 0, + translationY: 0, + }); + }; + private recalculateZoom = () => { this.setZoomAndRotation(); }; @@ -380,7 +399,7 @@ export default class ImageView extends React.Component { // image causing it translate in the wrong direction. const style = { cursor: cursor, - transition: this.state.moving ? null : "transform 200ms ease 0s", + transition: this.state.moving ? null : "transform 250ms ease 0s", transform: `translateX(${translatePixelsX}) translateY(${translatePixelsY}) scale(${zoom}) diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index 44c15d50e7..c433ddb8e3 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -112,6 +112,17 @@ export default class MImageBody extends React.Component { params.fileSize = content.info.size; } + if (this.image.current) { + const clientRect = this.image.current.getBoundingClientRect(); + + params.thumbnailInfo = { + width: clientRect.width, + height: clientRect.height, + positionX: clientRect.x, + positionY: clientRect.y, + }; + } + Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); } }; diff --git a/src/components/views/rooms/LinkPreviewWidget.tsx b/src/components/views/rooms/LinkPreviewWidget.tsx index 55e123f4e0..5e7154dc8a 100644 --- a/src/components/views/rooms/LinkPreviewWidget.tsx +++ b/src/components/views/rooms/LinkPreviewWidget.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { createRef } from 'react'; +import React, { ComponentProps, createRef } from 'react'; import { AllHtmlEntities } from 'html-entities'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { IPreviewUrlResponse } from 'matrix-js-sdk/src/client'; @@ -36,6 +36,7 @@ interface IProps { @replaceableComponent("views.rooms.LinkPreviewWidget") export default class LinkPreviewWidget extends React.Component { private readonly description = createRef(); + private image = createRef(); componentDidMount() { if (this.description.current) { @@ -59,7 +60,7 @@ export default class LinkPreviewWidget extends React.Component { src = mediaFromMxc(src).srcHttp; } - const params = { + const params: Omit, "onFinished"> = { src: src, width: p["og:image:width"], height: p["og:image:height"], @@ -68,6 +69,17 @@ export default class LinkPreviewWidget extends React.Component { link: this.props.link, }; + if (this.image.current) { + const clientRect = this.image.current.getBoundingClientRect(); + + params.thumbnailInfo = { + width: clientRect.width, + height: clientRect.height, + positionX: clientRect.x, + positionY: clientRect.y, + }; + } + Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); }; @@ -100,7 +112,7 @@ export default class LinkPreviewWidget extends React.Component { let img; if (image) { img =
- +
; }