diff --git a/src/components/views/rooms/LinkPreviewGroup.tsx b/src/components/views/rooms/LinkPreviewGroup.tsx index ff6fd4afd2..2541b2e375 100644 --- a/src/components/views/rooms/LinkPreviewGroup.tsx +++ b/src/components/views/rooms/LinkPreviewGroup.tsx @@ -14,43 +14,57 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { useEffect } from "react"; +import React, { useContext, useEffect } from "react"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { IPreviewUrlResponse } from "matrix-js-sdk/src/client"; import { useStateToggle } from "../../../hooks/useStateToggle"; import LinkPreviewWidget from "./LinkPreviewWidget"; import AccessibleButton from "../elements/AccessibleButton"; import { _t } from "../../../languageHandler"; +import MatrixClientContext from "../../../contexts/MatrixClientContext"; +import { useAsyncMemo } from "../../../hooks/useAsyncMemo"; const INITIAL_NUM_PREVIEWS = 2; interface IProps { links: string[]; // the URLs to be previewed mxEvent: MatrixEvent; // the Event associated with the preview - onCancelClick?(): void; // called when the preview's cancel ('hide') button is clicked - onHeightChanged?(): void; // called when the preview's contents has loaded + onCancelClick(): void; // called when the preview's cancel ('hide') button is clicked + onHeightChanged(): void; // called when the preview's contents has loaded } const LinkPreviewGroup: React.FC = ({ links, mxEvent, onCancelClick, onHeightChanged }) => { + const cli = useContext(MatrixClientContext); const [expanded, toggleExpanded] = useStateToggle(); + + const ts = mxEvent.getTs(); + const previews = useAsyncMemo<[string, IPreviewUrlResponse][]>(async () => { + return Promise.all<[string, IPreviewUrlResponse] | void>(links.map(link => { + return cli.getUrlPreview(link, ts).then(preview => [link, preview], error => { + console.error("Failed to get URL preview: " + error); + }); + })).then(a => a.filter(Boolean)) as Promise<[string, IPreviewUrlResponse][]>; + }, [links, ts], []); + useEffect(() => { onHeightChanged(); - }, [onHeightChanged, expanded]); + }, [onHeightChanged, expanded, previews]); - const shownLinks = expanded ? links : links.slice(0, INITIAL_NUM_PREVIEWS); + const showPreviews = expanded ? previews : previews.slice(0, INITIAL_NUM_PREVIEWS); - let toggleButton; - if (links.length > INITIAL_NUM_PREVIEWS) { + let toggleButton: JSX.Element; + if (previews.length > INITIAL_NUM_PREVIEWS) { toggleButton = { expanded ? _t("Collapse") - : _t("Show %(count)s other previews", { count: links.length - shownLinks.length }) } + : _t("Show %(count)s other previews", { count: previews.length - showPreviews.length }) } ; } return
- { shownLinks.map((link, i) => ( - + { showPreviews.map(([link, preview], i) => ( + { i === 0 ? ( { - private unmounted = false; +export default class LinkPreviewWidget extends React.Component { private readonly description = createRef(); - constructor(props) { - super(props); - - this.state = { - preview: null, - }; - - MatrixClientPeg.get().getUrlPreview(this.props.link, this.props.mxEvent.getTs()).then((preview) => { - if (this.unmounted) { - return; - } - this.setState({ preview }, this.props.onHeightChanged); - }, (error) => { - console.error("Failed to get URL preview: " + error); - }); - } - componentDidMount() { if (this.description.current) { linkifyElement(this.description.current); @@ -72,12 +49,8 @@ export default class LinkPreviewWidget extends React.Component { } } - componentWillUnmount() { - this.unmounted = true; - } - private onImageClick = ev => { - const p = this.state.preview; + const p = this.props.preview; if (ev.button != 0 || ev.metaKey) return; ev.preventDefault(); @@ -99,7 +72,7 @@ export default class LinkPreviewWidget extends React.Component { }; render() { - const p = this.state.preview; + const p = this.props.preview; if (!p || Object.keys(p).length === 0) { return
; }