Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions apps/web/src/utils/UrlPreviewFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import type { UrlPreview } from "@element-hq/web-shared-components";
import { mediaFromMxc } from "../customisations/Media";
import { thumbHeight } from "../ImageUtils";
import { type UnstableBundledUrlPreviewSingle } from "../../@types/url-preview";

const logger = rootLogger.getChild("UrlPreviewFetcher");

Expand Down Expand Up @@ -201,4 +202,50 @@
this.cache.set(link, result);
return result;
}

// Convert an MSC4095 URL preview bundle item to a UrlPreview
public previewFromBundle(single: UnstableBundledUrlPreviewSingle): UrlPreview {

Check warning on line 207 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 207 is not covered by tests
// TODO: missing fields from the bundle are:

Check warning on line 208 in apps/web/src/utils/UrlPreviewFetcher.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=element-web&issues=AZ881Vi-XcjtlmNVjS1V&open=AZ881Vi-XcjtlmNVjS1V&pullRequest=34170
// - siteName (can be computed)
// - siteIcon
// - media is a video or audio?
// TODO: URL previews in encrypted chat?

Check warning on line 212 in apps/web/src/utils/UrlPreviewFetcher.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=element-web&issues=AZ881Vi-XcjtlmNVjS1W&open=AZ881Vi-XcjtlmNVjS1W&pullRequest=34170
const hasImage =
typeof single["og:image"] === "string" &&

Check warning on line 214 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 214 is not covered by tests
typeof single["og:image:type"] === "string" &&
typeof single["og:image:width"] === "number" &&
typeof single["og:image:height"] === "number";

const preview: UrlPreview = {

Check warning on line 219 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 219 is not covered by tests
link: single.matched_url,
title: single["og:title"] ?? single.matched_url,

Check warning on line 221 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 221 is not covered by tests
siteName: new URL(single.matched_url).hostname,
showTooltipOnLink: !!(single.matched_url !== single["og:title"] && this.showTooltips),

Check warning on line 223 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 223 is not covered by tests
description: single["og:description"],
ogUrl: single["og:url"],
};

if (hasImage) {
const media = mediaFromMxc(single["og:image"], this.client);
const thumb = media.getThumbnailOfSourceHttp(PREVIEW_WIDTH_PX, PREVIEW_HEIGHT_PX, "scale");

Check warning on line 230 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Lines 228-230 are not covered by tests

// cannot rule out the mxc:// url is malformed because
// the sender can specify anything
if (media.srcHttp === null || thumb === null) {
return preview;

Check warning on line 235 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Lines 234-235 are not covered by tests
}

preview.image = {

Check warning on line 238 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 238 is not covered by tests
imageThumb: thumb,
imageFull: media.srcHttp,
imageType: single["og:image:type"] as string,
mxcImageFull: single["og:image"] as string,
width: single["og:image:width"] as number,
height: single["og:image:height"] as number,
playable: false, // TODO: do we know?

Check warning on line 245 in apps/web/src/utils/UrlPreviewFetcher.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Complete the task associated to this "TODO" comment.

See more on https://sonarcloud.io/project/issues?id=element-web&issues=AZ881Vi-XcjtlmNVjS1X&open=AZ881Vi-XcjtlmNVjS1X&pullRequest=34170
};
}

return preview;

Check warning on line 249 in apps/web/src/utils/UrlPreviewFetcher.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 249 is not covered by tests
}
}
40 changes: 29 additions & 11 deletions apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Please see LICENSE files in the repository root for full details.
*/

import { type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { MsgType, type MatrixClient, type MatrixEvent } from "matrix-js-sdk/src/matrix";
import {
BaseViewModel,
type UrlPreview,
Expand All @@ -17,6 +17,8 @@
import { PosthogAnalytics } from "../../PosthogAnalytics";
import { isPermalinkHost } from "../../utils/permalinks/Permalinks";
import { UrlPreviewFetcher } from "../../utils/UrlPreviewFetcher";
import { type RoomMessageEventContent } from "../../../@types/url-preview";
import SettingsStore from "../../settings/SettingsStore";

// From https://github.com/matrix-org/matrix-spec-proposals/pull/4095
export const BUNDLED_LINK_PREVIEWS = "com.beeper.linkpreviews";
Expand Down Expand Up @@ -45,8 +47,7 @@

export class UrlPreviewGroupViewModel
extends BaseViewModel<UrlPreviewGroupViewSnapshot, UrlPreviewGroupViewModelProps>
implements UrlPreviewGroupViewActions
{
implements UrlPreviewGroupViewActions {
/**
* Determine if an anchor element can be rendered into a preview.
* If it can, return the value of `href`
Expand Down Expand Up @@ -167,14 +168,31 @@
}

const loadMedia = this.visibility === PreviewVisibility.Visible;
const previews =
this.visibility <= PreviewVisibility.UserHidden
? []
: await Promise.all(
this.links
.slice(0, this.limitPreviews ? MAX_PREVIEWS_WHEN_LIMITED : undefined)
.map((link) => this.fetcher.fetchPreview(link, loadMedia)),
);
let previews: (UrlPreview | null)[] | undefined;

if (this.visibility <= PreviewVisibility.UserHidden) {
previews = [];
}

const content = this.props.mxEvent.getContent();
if (content.msgtype === MsgType.Text && SettingsStore.getValue("feature_msc4095_url_preview_bundle")) {
const messageContent = content as RoomMessageEventContent;

Check warning on line 179 in apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts

View workflow job for this annotation

GitHub Actions / Tests

Uncovered Line

Line 179 is not covered by tests

if (messageContent[BUNDLED_LINK_PREVIEWS] !== undefined) {
previews = messageContent[BUNDLED_LINK_PREVIEWS]
.slice(0, this.limitPreviews ? MAX_PREVIEWS_WHEN_LIMITED : undefined)
.map((preview) => this.fetcher.previewFromBundle(preview));
}
}

if (previews === undefined) {
previews = await Promise.all(
this.links
.slice(0, this.limitPreviews ? MAX_PREVIEWS_WHEN_LIMITED : undefined)
.map((link) => this.fetcher.fetchPreview(link, loadMedia)),
);
}

Check warning on line 194 in apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer using nullish coalescing operator (`??=`) instead of an assignment expression, as it is simpler to read.

See more on https://sonarcloud.io/project/issues?id=element-web&issues=AZ881VX3XcjtlmNVjS1U&open=AZ881VX3XcjtlmNVjS1U&pullRequest=34170

this.snapshot.merge({
previews: previews.filter((p) => !!p),
totalPreviewCount: this.links.length,
Expand Down
Loading