diff --git a/apps/web/playwright/e2e/messages/messages.spec.ts b/apps/web/playwright/e2e/messages/messages.spec.ts index 67af9edb42..2115dc3394 100644 --- a/apps/web/playwright/e2e/messages/messages.spec.ts +++ b/apps/web/playwright/e2e/messages/messages.spec.ts @@ -252,6 +252,7 @@ test.describe("Message url previews", () => { "og:title": "A simple site", "og:description": "And with a brief description", "og:image": mxc, + "og:image:alt": "The riot logo", }, }); }); diff --git a/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-basic-linux.png b/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-basic-linux.png index 6d004c8da2..5651405d53 100644 Binary files a/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-basic-linux.png and b/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-basic-linux.png differ diff --git a/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-with-thumb-linux.png b/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-with-thumb-linux.png index aa36fca9e9..fa149ef399 100644 Binary files a/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-with-thumb-linux.png and b/apps/web/playwright/snapshots/messages/messages.spec.ts/preview-with-thumb-linux.png differ diff --git a/apps/web/src/PosthogTrackers.ts b/apps/web/src/PosthogTrackers.ts index cc531bde9b..19bb900b7c 100644 --- a/apps/web/src/PosthogTrackers.ts +++ b/apps/web/src/PosthogTrackers.ts @@ -13,7 +13,6 @@ import { type Interaction as InteractionEvent } from "@matrix-org/analytics-even import { type PinUnpinAction } from "@matrix-org/analytics-events/types/typescript/PinUnpinAction"; import { type RoomListSortingAlgorithmChanged } from "@matrix-org/analytics-events/types/typescript/RoomListSortingAlgorithmChanged"; import { type UrlPreviewRendered } from "@matrix-org/analytics-events/types/typescript/UrlPreviewRendered"; -import { type UrlPreview } from "@element-hq/web-shared-components"; import PageType from "./PageTypes"; import Views from "./Views"; @@ -151,7 +150,7 @@ export default class PosthogTrackers { * @param isEncrypted Whether the event (and effectively the room) was encrypted. * @param previews The previews generated from the event. */ - public trackUrlPreview(eventId: string, isEncrypted: boolean, previews: UrlPreview[]): void { + public trackUrlPreview(eventId: string, isEncrypted: boolean, previews: { image?: unknown }[]): void { // Discount any previews that we have already tracked. if (this.previewedEventIds.get(eventId)) { return; diff --git a/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts index 6d42baf2d5..6cafe3b5af 100644 --- a/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts +++ b/apps/web/src/viewmodels/message-body/UrlPreviewGroupViewModel.ts @@ -34,8 +34,10 @@ export interface UrlPreviewGroupViewModelProps { } export const MAX_PREVIEWS_WHEN_LIMITED = 2; -export const PREVIEW_WIDTH = 100; -export const PREVIEW_HEIGHT = 100; +export const PREVIEW_WIDTH_PX = 478; +export const PREVIEW_HEIGHT_PX = 200; +export const MIN_PREVIEW_PX = 96; +export const MIN_IMAGE_SIZE_BYTES = 8192; export enum PreviewVisibility { /** @@ -100,21 +102,26 @@ export class UrlPreviewGroupViewModel typeof response["og:description"] === "string" && response["og:description"].trim() ? response["og:description"].trim() : undefined; - let siteName = + const siteName = typeof response["og:site_name"] === "string" && response["og:site_name"].trim() ? response["og:site_name"].trim() - : undefined; + : new URL(link).hostname; + // If there is no title, use the description as the title. if (!title && description) { title = description; description = undefined; } else if (!title && siteName) { title = siteName; - siteName = undefined; } else if (!title) { title = link; } + // If the description matches the site name, don't bother with a description. + if (description && description.toLowerCase() === siteName.toLowerCase()) { + description = undefined; + } + return { title, description: description && decode(description), @@ -122,6 +129,50 @@ export class UrlPreviewGroupViewModel }; } + /** + * Calculate the best possible author from an opengraph response. + * @param response The opengraph response + * @returns The author value, or undefined if no valid author could be found. + */ + private static getAuthorFromResponse(response: IPreviewUrlResponse): UrlPreview["author"] { + let calculatedAuthor: string | undefined; + if (response["og:type"] === "article") { + if (typeof response["article:author"] === "string" && response["article:author"]) { + calculatedAuthor = response["article:author"]; + } + // Otherwise fall through to check the profile. + } + if (typeof response["profile:username"] === "string" && response["profile:username"]) { + calculatedAuthor = response["profile:username"]; + } + if (calculatedAuthor && URL.canParse(calculatedAuthor)) { + // Some sites return URLs as authors which doesn't look good in Element, so discard it. + return; + } + return calculatedAuthor; + } + + /** + * Calculate whether the provided image from the preview response is an full size preview or + * a site icon. + * @returns `true` if the image should be used as a preview, otherwise `false` + */ + private static isImagePreview(width?: number, height?: number, bytes?: number): boolean { + // We can't currently distinguish from a preview image and a favicon. Neither OpenGraph nor Matrix + // have a clear distinction, so we're using a heuristic here to check the dimensions & size of the file and + // deciding whether to render it as a full preview or icon. + if (width && width < MIN_PREVIEW_PX) { + return false; + } + if (height && height < MIN_PREVIEW_PX) { + return false; + } + if (bytes && bytes < MIN_IMAGE_SIZE_BYTES) { + return false; + } + return true; + } + /** * Determine if an anchor element can be rendered into a preview. * If it can, return the value of `href` @@ -278,6 +329,7 @@ export class UrlPreviewGroupViewModel } const { title, description, siteName } = UrlPreviewGroupViewModel.getBaseMetadataFromResponse(preview, link); + const author = UrlPreviewGroupViewModel.getAuthorFromResponse(preview); const hasImage = preview["og:image"] && typeof preview?.["og:image"] === "string"; // Ensure we have something relevant to render. // The title must not just be the link, or we must have an image. @@ -285,31 +337,46 @@ export class UrlPreviewGroupViewModel return null; } let image: UrlPreview["image"]; + let siteIcon: string | undefined; if (typeof preview["og:image"] === "string" && this.visibility > PreviewVisibility.MediaHidden) { const media = mediaFromMxc(preview["og:image"], this.client); const declaredHeight = UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["og:image:height"]); const declaredWidth = UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["og:image:width"]); - const width = Math.min(declaredWidth ?? PREVIEW_WIDTH, PREVIEW_WIDTH); - const height = thumbHeight(width, declaredHeight, PREVIEW_WIDTH, PREVIEW_WIDTH) ?? PREVIEW_WIDTH; - const thumb = media.getThumbnailOfSourceHttp(PREVIEW_WIDTH, PREVIEW_HEIGHT, "scale"); - // No thumb, no preview. - if (thumb) { - image = { - imageThumb: thumb, - imageFull: media.srcHttp ?? thumb, - width, - height, - fileSize: UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["matrix:image:size"]), - }; + const imageSize = UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["matrix:image:size"]); + const alt = typeof preview["og:image:alt"] === "string" ? preview["og:image:alt"] : undefined; + + const isImagePreview = UrlPreviewGroupViewModel.isImagePreview(declaredWidth, declaredHeight, imageSize); + if (isImagePreview) { + const width = Math.min(declaredWidth ?? PREVIEW_WIDTH_PX, PREVIEW_WIDTH_PX); + const height = + thumbHeight(width, declaredHeight, PREVIEW_WIDTH_PX, PREVIEW_WIDTH_PX) ?? PREVIEW_WIDTH_PX; + const thumb = media.getThumbnailOfSourceHttp(PREVIEW_WIDTH_PX, PREVIEW_HEIGHT_PX, "scale"); + const playable = !!preview["og:video"] || !!preview["og:video:type"] || !!preview["og:audio"]; + // No thumb, no preview. + if (thumb) { + image = { + imageThumb: thumb, + imageFull: media.srcHttp ?? thumb, + width, + height, + fileSize: UrlPreviewGroupViewModel.getNumberFromOpenGraph(preview["matrix:image:size"]), + alt, + playable, + }; + } + } else if (media.srcHttp) { + siteIcon = media.srcHttp; } } const result = { link, title, + author, description, siteName, - showTooltipOnLink: link !== title && PlatformPeg.get()?.needsUrlTooltips(), + siteIcon, + showTooltipOnLink: !!(link !== title && PlatformPeg.get()?.needsUrlTooltips()), image, } satisfies UrlPreview; this.previewCache.set(link, result); diff --git a/apps/web/test/unit-tests/PosthogTrackers-test.ts b/apps/web/test/unit-tests/PosthogTrackers-test.ts index c835ecacd0..a8904e4080 100644 --- a/apps/web/test/unit-tests/PosthogTrackers-test.ts +++ b/apps/web/test/unit-tests/PosthogTrackers-test.ts @@ -18,20 +18,10 @@ describe("PosthogTrackers", () => { const tracker = new PosthogTrackers(); tracker.trackUrlPreview("$123456", false, [ { - title: "A preview", - image: { - imageThumb: "abc", - imageFull: "abc", - }, - link: "a-link", - }, - ]); - tracker.trackUrlPreview("$123456", false, [ - { - title: "A second preview", - link: "a-link", + image: {}, }, ]); + tracker.trackUrlPreview("$123456", false, [{}]); // Ignores subsequent calls. expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith({ eventName: "UrlPreviewRendered", diff --git a/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts b/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts index 782cc8d7ee..9541e999a8 100644 --- a/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts +++ b/apps/web/test/viewmodels/message-body/UrlPreviewGroupViewModel-test.ts @@ -125,6 +125,32 @@ describe("UrlPreviewGroupViewModel", () => { await vm.updateEventElement(msg); expect(vm.getSnapshot()).toMatchSnapshot(); }); + it.each>([ + { "matrix:image:size": 8191 }, + { "og:image:width": 95 }, + { "og:image:height": 95 }, + ])("should preview a URL with a site icon", async (extraResp) => { + const { vm, client } = getViewModel(); + client.getUrlPreview.mockResolvedValueOnce({ + "og:title": "This is an example!", + "og:type": "document", + "og:url": "https://example.org", + "og:image": IMAGE_MXC, + "og:image:height": 128, + "og:image:width": 128, + "matrix:image:size": 8193, + ...extraResp, + }); + // eslint-disable-next-line no-restricted-properties + client.mxcUrlToHttp.mockImplementation((url) => { + expect(url).toEqual(IMAGE_MXC); + return "https://example.org/image/src"; + }); + const msg = document.createElement("div"); + msg.innerHTML = 'Test'; + await vm.updateEventElement(msg); + expect(vm.getSnapshot().previews[0].siteIcon).toBeTruthy(); + }); it("should ignore media when mediaVisible is false", async () => { const { vm, client } = getViewModel({ mediaVisible: false, visible: true }); client.getUrlPreview.mockResolvedValueOnce({ @@ -200,6 +226,41 @@ describe("UrlPreviewGroupViewModel", () => { expect(vm.getSnapshot()).toMatchSnapshot(); }); + describe("calculates author", () => { + it("should use the profile:username if provided", async () => { + const { vm, client } = getViewModel(); + client.getUrlPreview.mockResolvedValueOnce({ ...BASIC_PREVIEW_OGDATA, "profile:username": "my username" }); + const msg = document.createElement("div"); + msg.innerHTML = 'Test'; + await vm.updateEventElement(msg); + expect(vm.getSnapshot().previews[0].author).toEqual("my username"); + }); + it("should use author if the og:type is an article", async () => { + const { vm, client } = getViewModel(); + client.getUrlPreview.mockResolvedValueOnce({ + ...BASIC_PREVIEW_OGDATA, + "og:type": "article", + "article:author": "my name", + }); + const msg = document.createElement("div"); + msg.innerHTML = 'Test'; + await vm.updateEventElement(msg); + expect(vm.getSnapshot().previews[0].author).toEqual("my name"); + }); + it("should NOT use author if the author is a URL", async () => { + const { vm, client } = getViewModel(); + client.getUrlPreview.mockResolvedValueOnce({ + ...BASIC_PREVIEW_OGDATA, + "og:type": "article", + "article:author": "https://junk.example.org/foo", + }); + const msg = document.createElement("div"); + msg.innerHTML = 'Test'; + await vm.updateEventElement(msg); + expect(vm.getSnapshot().previews[0].author).toBeUndefined(); + }); + }); + it.each([ { text: "", href: "", hasPreview: false }, { text: "test", href: "noprotocol.example.org", hasPreview: false }, @@ -232,7 +293,7 @@ describe("UrlPreviewGroupViewModel", () => { // API *may* return a string, so check we parse correctly. "og:image:height": "500" as unknown as number, "og:image:width": 500, - "matrix:image:size": 1024, + "matrix:image:size": 10000, "og:image": IMAGE_MXC, }, ])("handles different kinds of opengraph responses %s", async (og) => { @@ -251,4 +312,25 @@ describe("UrlPreviewGroupViewModel", () => { await vm.updateEventElement(msg); expect(vm.getSnapshot().previews[0]).toMatchSnapshot(); }); + + it.each(["og:video", "og:video:type", "og:audio"])("detects playable links via %s", async (property) => { + const { vm, client } = getViewModel(); + // eslint-disable-next-line no-restricted-properties + client.mxcUrlToHttp.mockImplementation((url, width) => { + expect(url).toEqual(IMAGE_MXC); + if (width) { + return "https://example.org/image/thumb"; + } + return "https://example.org/image/src"; + }); + client.getUrlPreview.mockResolvedValueOnce({ + ...BASIC_PREVIEW_OGDATA, + "og:image": IMAGE_MXC, + [property]: "anything", + }); + const msg = document.createElement("div"); + msg.innerHTML = `test`; + await vm.updateEventElement(msg); + expect(vm.getSnapshot().previews[0].image?.playable).toEqual(true); + }); }); diff --git a/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap b/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap index 1f412d86cb..e910d1b00d 100644 --- a/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap +++ b/apps/web/test/viewmodels/message-body/__snapshots__/UrlPreviewGroupViewModel-test.ts.snap @@ -2,61 +2,73 @@ exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:description': 'A description',\\n 'og:title': ''\\n} 1`] = ` { + "author": undefined, "description": undefined, "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "example.org", "title": "A description", } `; exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:site_name': 'Site name',\\n 'og:title': ''\\n} 1`] = ` { + "author": undefined, "description": undefined, "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "Site name", "title": "Site name", } `; exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Basic title'\\n} 1`] = ` { + "author": undefined, "description": undefined, "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "example.org", "title": "Basic title", } `; exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Cool blog',\\n 'og:site_name': 'Cool site'\\n} 1`] = ` { + "author": undefined, "description": undefined, "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, "siteName": "Cool site", "title": "Cool blog", } `; -exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 1024,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = ` +exports[`UrlPreviewGroupViewModel handles different kinds of opengraph responses {\\n 'og:url': 'https://example.org',\\n 'og:type': 'document',\\n 'og:title': 'Media test',\\n 'og:image:height': '500',\\n 'og:image:width': 500,\\n 'matrix:image:size': 10000,\\n 'og:image': 'mxc://example.org/abc'\\n} 1`] = ` { + "author": undefined, "description": undefined, "image": { - "fileSize": 1024, - "height": 100, + "alt": undefined, + "fileSize": 10000, + "height": 478, "imageFull": "https://example.org/image/src", "imageThumb": "https://example.org/image/thumb", - "width": 100, + "playable": false, + "width": 478, }, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "example.org", "title": "Media test", } `; @@ -67,10 +79,12 @@ exports[`UrlPreviewGroupViewModel should deduplicate multiple versions of the sa "overPreviewLimit": false, "previews": [ { + "author": undefined, "description": "This is a description", "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, "siteName": "Example.org", "title": "This is an example!", }, @@ -96,10 +110,12 @@ exports[`UrlPreviewGroupViewModel should handle being hidden and shown by the us "overPreviewLimit": false, "previews": [ { + "author": undefined, "description": "This is a description", "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, "siteName": "Example.org", "title": "This is an example!", }, @@ -135,11 +151,13 @@ exports[`UrlPreviewGroupViewModel should ignore media when mediaVisible is false "overPreviewLimit": false, "previews": [ { + "author": undefined, "description": undefined, "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "example.org", "title": "This is an example!", }, ], @@ -154,17 +172,21 @@ exports[`UrlPreviewGroupViewModel should preview a URL with media 1`] = ` "overPreviewLimit": false, "previews": [ { + "author": undefined, "description": undefined, "image": { + "alt": undefined, "fileSize": 10000, - "height": 100, + "height": 128, "imageFull": "https://example.org/image/src", "imageThumb": "https://example.org/image/thumb", - "width": 100, + "playable": false, + "width": 128, }, "link": "https://example.org", - "showTooltipOnLink": undefined, - "siteName": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, + "siteName": "example.org", "title": "This is an example!", }, ], @@ -179,10 +201,12 @@ exports[`UrlPreviewGroupViewModel should preview a single valid URL 1`] = ` "overPreviewLimit": false, "previews": [ { + "author": undefined, "description": "This is a description", "image": undefined, "link": "https://example.org", - "showTooltipOnLink": undefined, + "showTooltipOnLink": false, + "siteIcon": undefined, "siteName": "Example.org", "title": "This is an example!", }, diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png index ff1cfa2b24..fd8d058387 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png index 5767d6aa32..67801f4c04 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png index 390c358959..340540e14a 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png index d4b4048145..e24a3b4422 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png index 263f3b1eef..c3e635491c 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png index b41968503d..8171d99b63 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png index fc3272f314..d618d89d47 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png index 2a5a79992b..8fc73653b8 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png index bca73a6e3b..adfc1670f7 100644 Binary files a/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png and b/packages/shared-components/__vis__/linux/__baselines__/event-tiles/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/article-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/article-auto.png new file mode 100644 index 0000000000..2e57f020f3 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/article-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png new file mode 100644 index 0000000000..4fc1b93e59 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-auto.png new file mode 100644 index 0000000000..c03a2ed302 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-with-image-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-with-image-auto.png new file mode 100644 index 0000000000..d534819e82 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/social-with-image-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png new file mode 100644 index 0000000000..2e55d7ca0f Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-and-description-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png new file mode 100644 index 0000000000..0181507fab Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/title-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/video-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/video-auto.png new file mode 100644 index 0000000000..663f81254e Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/video-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-and-description-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-and-description-auto.png new file mode 100644 index 0000000000..c49a6499a1 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-and-description-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-auto.png new file mode 100644 index 0000000000..17793f85dd Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-site-icon-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png new file mode 100644 index 0000000000..35f640c84d Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-tooltip-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png new file mode 100644 index 0000000000..c973c6a066 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx/with-very-long-text-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png new file mode 100644 index 0000000000..83409d52cd Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png new file mode 100644 index 0000000000..47bec1a162 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-hidden-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png new file mode 100644 index 0000000000..7fc88379ba Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/multiple-previews-visible-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png new file mode 100644 index 0000000000..893a166693 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/UrlPreviewGroupView/UrlPreviewGroupView.stories.tsx/with-compact-view-auto.png differ diff --git a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css deleted file mode 100644 index 86e67b73c7..0000000000 --- a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2026 Element Creations Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -.thumbnail { - /* Thumbnails are always limited to a maximum of 100px */ - max-width: 100px; - max-height: 100px; - /* Ensure we don't stretch the image */ - object-fit: cover; -} - -.link { - color: var(--cpd-color-text-link-external); - text-decoration-line: none; -} - -.container { - display: inline flex; - column-gap: var(--cpd-space-1x); - border-inline-start: 2px solid var(--cpd-color-bg-subtle-primary); - border-radius: 2px; - color: var(--cpd-color-gray-900); - - .wrapImageCaption { - display: inline-flex; - flex-direction: row; - flex-wrap: wrap; - row-gap: var(--cpd-space-2x); - flex: 1; - } - - .image, - .caption { - display: inline-flex; - flex-direction: column; - margin-inline-start: var(--cpd-space-4x); - min-width: 0; /* Prevent blowout */ - } - - .image { - /* Clear default - ); - } - - const anchor = ( - - {preview.title} - - ); - return ( -
-
- {img} -
- - {tooltipCaption ? {anchor} : anchor} - {preview.siteName && ( - - {" - " + preview.siteName} - - )} - - {preview.description && ( - {preview.description} - )} -
-
-
- ); -} diff --git a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/__snapshots__/LinkPreview.test.tsx.snap b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/__snapshots__/LinkPreview.test.tsx.snap deleted file mode 100644 index 1e0d988f1b..0000000000 --- a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/__snapshots__/LinkPreview.test.tsx.snap +++ /dev/null @@ -1,121 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`LinkPreview > renders a preview 1`] = ` -
-
-
- -
-

- - A simple title - - - - Site name - -

-

- A simple description -

-
-
-
-
-`; - -exports[`LinkPreview > renders a preview with just a title 1`] = ` -
-
- -
-
-`; - -exports[`LinkPreview > renders a preview with just a title and description 1`] = ` -
-
-
-
-

- - A simple title - -

-

- A simple description with a link to - - https://matrix.org - -

-
-
-
-
-`; diff --git a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/__snapshots__/UrlPreviewGroupView.test.tsx.snap b/packages/shared-components/src/event-tiles/UrlPreviewGroupView/__snapshots__/UrlPreviewGroupView.test.tsx.snap deleted file mode 100644 index fb9e9494d5..0000000000 --- a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/__snapshots__/UrlPreviewGroupView.test.tsx.snap +++ /dev/null @@ -1,492 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`UrlPreviewGroupView > renders a single preview 1`] = ` -
-
-
-
-
- -
-

- - A simple title - -

-

- A simple description -

-
-
-
-
- -
-
-`; - -exports[`UrlPreviewGroupView > renders multiple previews 1`] = ` -
-
-
-
-
- -
-

- - One - -

-

- A regular square image. -

-
-
-
-
-
- -
-

- - Two - -

-

- This one has a taller image which should crop nicely. -

-
-
-
-
-
- -
-

- - Three - -

-

- One more description -

-
-
-
- -
- -
-
-`; - -exports[`UrlPreviewGroupView > renders multiple previews which are hidden 1`] = ` -
-
-
-
-
- -
-

- - A simple title - -

-

- A simple description -

-
-
-
- -
- -
-
-`; - -exports[`UrlPreviewGroupView > renders with a compact view 1`] = ` -
-
-
-
-
- -
-

- - One - -

-

- A regular square image. -

-
-
-
-
-
- -
-

- - Two - -

-

- This one has a taller image which should crop nicely. -

-
-
-
-
-
- -
-

- - Three - -

-

- One more description -

-
-
-
- -
- -
-
-`; diff --git a/packages/shared-components/src/i18n/strings/en_EN.json b/packages/shared-components/src/i18n/strings/en_EN.json index 31475607c7..a9eb6712d4 100644 --- a/packages/shared-components/src/i18n/strings/en_EN.json +++ b/packages/shared-components/src/i18n/strings/en_EN.json @@ -224,6 +224,7 @@ "message_timestamp_sent_at": "Sent at: %(dateTime)s", "url_preview": { "close": "Close preview", + "open_link": "Open link", "show_n_more": { "one": "Show %(count)s other preview", "other": "Show %(count)s other previews" diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 081ccf0d83..385bef992c 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -12,7 +12,6 @@ export * from "./audio/SeekBar"; export * from "./core/AvatarWithDetails"; export * from "./room/composer/Banner"; export * from "./crypto/SasEmoji"; -export * from "./event-tiles/UrlPreviewGroupView"; export * from "./room/timeline/ReadMarker"; export * from "./room/timeline/event-tile/body/EventContentBodyView"; export * from "./room/timeline/event-tile/body/RedactedBodyView"; @@ -42,6 +41,7 @@ export * from "./room/timeline/event-tile/reactions/ReactionsRow"; export * from "./room/timeline/event-tile/reactions/ReactionsRowButton"; export * from "./room/timeline/event-tile/reactions/ReactionsRowButtonTooltip"; export * from "./room/timeline/event-tile/timestamp/MessageTimestampView"; +export * from "./room/timeline/event-tile/UrlPreviewGroupView"; export * from "./core/rich-list/RichItem"; export * from "./core/rich-list/RichList"; export * from "./room-list/RoomListHeaderView"; diff --git a/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css new file mode 100644 index 0000000000..407f26e06a --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.module.css @@ -0,0 +1,126 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +button.preview { + background: #fff; +} + +.preview { + display: flex; + position: relative; + width: 100%; + height: 200px; + background-size: cover; + background-position: center; + border: none; + padding: 0; + > img { + width: 100%; + object-fit: cover; + } + .playButton[data-kind="primary"] { + padding: 0; + width: 50px; + height: 50px; + margin: auto; + background: var(--cpd-color-text-on-solid-primary); + > svg { + margin: auto; + border-radius: 50px; + color: var(--cpd-color-icon-primary); + } + } +} + +.container { + max-width: 478px; + display: flex; + border: 1px solid var(--cpd-color-bg-subtle-secondary); + border-radius: 12px; /* Get radius from cpd */ + flex-direction: column; + color: var(--cpd-color-gray-900); + overflow: clip; + + background: var(--cpd-color-bg-subtle-secondary); + + &.inline { + flex-direction: row; + gap: var(--cpd-space-4x); + padding: var(--cpd-space-3x) var(--cpd-space-4x); + + .title { + margin: 0; + } + + .siteAvatar { + margin: auto 0; + } + + .siteName { + margin: 0; + } + } + + .textContent { + padding: var(--cpd-space-3x) var(--cpd-space-4x); + &.inline { + padding: 0; + } + display: flex; + flex-direction: column; + gap: var(--cpd-space-1x); + } + + .caption { + display: inline-flex; + flex-direction: column; + min-width: 0; /* Prevent blowout */ + } + .caption { + flex: 1; + overflow: hidden; /* cause it to wrap rather than clip */ + } + + .title, + .description { + display: inline-block; + -webkit-box-orient: vertical; + overflow: hidden; + white-space: normal; + } + + .title { + display: inline-block; + line-clamp: 2; + -webkit-line-clamp: 2; + margin: var(--cpd-space-1x) 0; + font-weight: var(--cpd-font-weight-semibold); + color: var(--cpd-color-text-primary); + text-decoration-line: none; + } + + .description { + font-size: var(--cpd-font-size-body-lg); + margin: 0; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + } + + .siteName { + margin-top: var(--cpd-space-1x); + vertical-align: middle; + display: flex; + gap: var(--cpd-space-1-5x); + > * { + /* Center everything */ + margin: auto 0; + } + } +} diff --git a/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx new file mode 100644 index 0000000000..dceaecc1df --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.stories.tsx @@ -0,0 +1,169 @@ +/* + * Copyright 2026 Element Creations Ltd. + * + * SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial + * Please see LICENSE files in the repository root for full details. + */ + +import React from "react"; +import { fn } from "storybook/test"; + +import type { Meta, StoryFn } from "@storybook/react-vite"; +import { LinkPreview } from "./LinkPreview"; +import { LinkedTextContext } from "../../../../../core/utils/LinkedText"; +import imageFile from "../../../../../../static/element.png"; +import imageFileWide from "../../../../../../static/wideImage.png"; + +export default { + title: "Event/UrlPreviewGroupView/LinkPreview", + component: LinkPreview, + tags: ["autodocs"], + args: { + onImageClick: fn(), + }, + argTypes: { + siteName: { + control: "text", + }, + author: { + control: "text", + }, + siteIcon: { control: { type: "file", accept: ".png" } }, + image: {}, + }, + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/design/sI9A2kV2K4xeiyqJsL7Ey3/Link-Previews?node-id=87-7920", + }, + }, +} satisfies Meta; + +const Template: StoryFn = (args) => ( + + + +); + +export const Default = Template.bind({}); +Default.args = { + title: "A simple title", + description: "A simple description", + link: "https://matrix.org", + siteName: "Site name", + image: { + imageThumb: imageFile, + imageFull: imageFile, + alt: "Element logo", + playable: false, + }, +}; + +export const Title = Template.bind({}); +Title.args = { + title: "A simple title", + link: "https://matrix.org", + siteName: "matrix.org", +}; + +export const TitleAndDescription = Template.bind({}); +TitleAndDescription.args = { + title: "A simple title", + description: "A simple description with a link to https://matrix.org", + link: "https://matrix.org", + siteName: "matrix.org", +}; +export const WithSiteIcon = Template.bind({}); +WithSiteIcon.args = { + title: "A simple title", + link: "https://matrix.org", + siteName: "matrix.org", + siteIcon: imageFile, +}; + +export const WithSiteIconAndDescription = Template.bind({}); +WithSiteIconAndDescription.args = { + title: "A simple title", + description: "A simple description with a link to https://matrix.org", + link: "https://matrix.org", + siteName: "matrix.org", + siteIcon: imageFile, +}; + +export const WithTooltip = Template.bind({}); +WithTooltip.args = { + title: "A simple title", + description: "A simple description", + showTooltipOnLink: true, + link: "https://matrix.org", + siteName: "matrix.org", +}; + +export const Article = Template.bind({}); +Article.args = { + title: "A linked article", + description: + "This is a basic description returned from the linked source, usually with a word or two about what the link contains.", + link: "https://matrix.org", + siteName: "blog.example.org", + image: { + imageThumb: imageFileWide, + imageFull: imageFileWide, + alt: "A dog", + playable: false, + }, +}; + +export const Video = Template.bind({}); +Video.args = { + title: "A linked video", + description: + "This is a link to a video. You cannot play the video inline yet, but you can click the play button to open the link", + link: "https://matrix.org", + siteName: "blog.example.org", + image: { + imageThumb: imageFileWide, + imageFull: imageFileWide, + alt: "A dog", + playable: true, + }, +}; + +export const Social = Template.bind({}); +Social.args = { + description: "Sending a small message", + link: "https://matrix.org", + siteName: "socialsite.example.org", + title: "Test user (@test)", + author: "@test", +}; + +export const SocialWithImage = Template.bind({}); +SocialWithImage.args = { + description: "Sending a message with an attached image.", + title: "Test user (@test)", + link: "https://matrix.org", + siteName: "socialsite.example.org", + author: "@test", + image: { + imageThumb: imageFileWide, + imageFull: imageFileWide, + alt: "A dog", + playable: false, + }, +}; + +export const WithVeryLongText = Template.bind({}); +WithVeryLongText.args = { + title: "GitHub - element-hq/not-a-real-repo: A very very long PR title that should be rendered nicely", + description: + "This PR doesn't actually exist and neither does the repository. It might exist one day if we go into the business of making paradoxical repository names.", + link: "https://matrix.org", + siteName: "GitHub", + image: { + imageThumb: imageFile, + imageFull: imageFile, + alt: "Element logo", + playable: false, + }, +}; diff --git a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx similarity index 77% rename from packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx rename to packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx index 3309fe9888..1fa218b6ca 100644 --- a/packages/shared-components/src/event-tiles/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/UrlPreviewGroupView/LinkPreview/LinkPreview.test.tsx @@ -13,7 +13,7 @@ import userEvent from "@testing-library/user-event"; import * as stories from "./LinkPreview.stories.tsx"; -const { Default, WithTooltip, Title, TitleAndDescription } = composeStories(stories); +const { Default, WithTooltip, Title, TitleAndDescription, Video } = composeStories(stories); describe("LinkPreview", () => { it("renders a preview", () => { @@ -36,4 +36,10 @@ describe("LinkPreview", () => { // Tooltip has the URL expect(await screen.findByText("https://matrix.org/")).toBeVisible(); }); + it("renders a playable preview that can be opened with a click", () => { + const { container } = render(