Merge branch 'develop' of ssh://github.com/element-hq/element-web into t3chguy/merge-modules
# Conflicts: # pnpm-lock.yaml # pnpm-workspace.yaml
@@ -88,7 +88,7 @@
|
||||
"app-builder-lib": "26.12.1",
|
||||
"chokidar": "^5.0.0",
|
||||
"detect-libc": "^2.0.0",
|
||||
"electron": "42.3.0",
|
||||
"electron": "42.3.3",
|
||||
"electron-builder": "26.12.1",
|
||||
"electron-builder-squirrel-windows": "26.12.1",
|
||||
"electron-devtools-installer": "^4.0.0",
|
||||
|
||||
@@ -14,8 +14,8 @@ WORKDIR /src
|
||||
# Install dependencies
|
||||
COPY --parents package.json pnpm-lock.yaml pnpm-workspace.yaml patches scripts **/package.json /src/
|
||||
RUN corepack enable
|
||||
RUN --mount=type=bind,source=.git,target=/src/.git /src/scripts/docker-link-repos.sh
|
||||
RUN pnpm install --frozen-lockfile
|
||||
RUN --mount=type=bind,source=.git,target=/src/.git /src/scripts/docker-link-repos.sh
|
||||
|
||||
# Build
|
||||
COPY --link --exclude=.git --exclude=apps/web/docker . /src
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"opus-recorder": "^8.0.3",
|
||||
"pako": "^2.0.3",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"posthog-js": "1.376.6",
|
||||
"posthog-js": "1.381.0",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "catalog:",
|
||||
|
||||
@@ -371,5 +371,33 @@ test.describe("Room list custom sections", () => {
|
||||
// Room is back in the Chats section
|
||||
await assertRoomInSection(page, "Chats", "my room");
|
||||
});
|
||||
|
||||
test("should remove a room from a custom section via the 'Remove from section' menu entry", async ({
|
||||
page,
|
||||
app,
|
||||
}) => {
|
||||
await app.client.createRoom({ name: "my room" });
|
||||
await createCustomSection(page, "Work");
|
||||
|
||||
const roomList = getRoomList(page);
|
||||
|
||||
// Move the room to the Work section
|
||||
let roomItem = roomList.getByRole("row", { name: "Open room my room" });
|
||||
await roomItem.hover();
|
||||
await roomItem.getByRole("button", { name: "More Options" }).click();
|
||||
await page.getByRole("menuitem", { name: "Move to" }).hover();
|
||||
await page.getByRole("menuitem", { name: "Work" }).click();
|
||||
|
||||
await assertRoomInSection(page, "Work", "my room");
|
||||
|
||||
// Open the More Options menu and click "Remove from section"
|
||||
roomItem = roomList.getByRole("row", { name: "Open room my room" });
|
||||
await roomItem.hover();
|
||||
await roomItem.getByRole("button", { name: "More Options" }).click();
|
||||
await page.getByRole("menuitem", { name: "Remove from section" }).click();
|
||||
|
||||
// Room is back in the Chats section
|
||||
await assertRoomInSection(page, "Chats", "my room");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js";
|
||||
|
||||
const DOCKER_IMAGE =
|
||||
"ghcr.io/element-hq/synapse:develop@sha256:3e079baccf55f491bd40c8b218789f0e8b31f466f6e23b14baab4a52ca845284";
|
||||
"ghcr.io/element-hq/synapse:develop@sha256:e5e02e25d3f3c38ae399acf74c359e971c2fd1381b88e0031a1faa9c785a0d91";
|
||||
|
||||
/**
|
||||
* SynapseContainer which freezes the docker digest to stabilise tests,
|
||||
|
||||
@@ -21,3 +21,7 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_CreateSectionDialog_edition > .mx_Dialog_header {
|
||||
margin-bottom: var(--cpd-space-6x);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_DevTools_content {
|
||||
overflow-y: auto;
|
||||
/* Give room for focus outlines */
|
||||
padding-inline-start: var(--cpd-space-0-5x);
|
||||
padding-block-end: var(--cpd-space-0-5x);
|
||||
}
|
||||
|
||||
.mx_DevTools_RoomStateExplorer_query {
|
||||
|
||||
@@ -202,8 +202,18 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
|
||||
OwnProfileStore.instance.on(UPDATE_EVENT, this.refreshBackgroundImage);
|
||||
this.refreshBackgroundImage();
|
||||
}
|
||||
|
||||
this.resizerViewModel = new ResizerViewModel();
|
||||
private getResizerViewModel(): ResizerViewModel {
|
||||
if (!this.resizerViewModel) {
|
||||
this.resizerViewModel = new ResizerViewModel();
|
||||
}
|
||||
return this.resizerViewModel;
|
||||
}
|
||||
|
||||
private disposeResizerViewModel(): void {
|
||||
this.resizerViewModel?.dispose();
|
||||
this.resizerViewModel = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -739,6 +749,8 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
break;
|
||||
default: {
|
||||
if (moduleRenderer) {
|
||||
// Since the view will be removed, remove the vm as well
|
||||
this.disposeResizerViewModel();
|
||||
pageElement = moduleRenderer();
|
||||
} else {
|
||||
console.warn(`Couldn't render page type "${this.props.page_type}"`);
|
||||
@@ -797,14 +809,15 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
const roomView = <div className="mx_RoomView_wrapper">{pageElement}</div>;
|
||||
|
||||
let content: React.ReactNode;
|
||||
if (useNewRoomList && this.resizerViewModel && !moduleRenderer) {
|
||||
const resizerViewModel = !moduleRenderer ? this.getResizerViewModel() : undefined;
|
||||
if (useNewRoomList && resizerViewModel && !moduleRenderer) {
|
||||
// New room list owned by element-web: resizable layout with a draggable separator.
|
||||
// The SpacePanel lives inside GroupView (leftPanel omits it when the new room list is enabled).
|
||||
content = (
|
||||
<GroupView vm={this.resizerViewModel}>
|
||||
<GroupView vm={resizerViewModel}>
|
||||
<SpacePanel />
|
||||
<LeftResizablePanelView
|
||||
vm={this.resizerViewModel}
|
||||
vm={resizerViewModel}
|
||||
className="mx_LeftPanel_panel"
|
||||
minSize="200px"
|
||||
maxSize="370px"
|
||||
@@ -812,7 +825,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||
>
|
||||
{leftPanel}
|
||||
</LeftResizablePanelView>
|
||||
<SeparatorView className="mx_Separator" vm={this.resizerViewModel} />
|
||||
<SeparatorView className="mx_Separator" vm={resizerViewModel} />
|
||||
<Panel className="mx_LeftPanel_panel">{roomView}</Panel>
|
||||
</GroupView>
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import React, { useState, type JSX } from "react";
|
||||
import { Flex } from "@element-hq/web-shared-components";
|
||||
import { Form, Text } from "@vector-im/compound-web";
|
||||
import classNames from "classnames";
|
||||
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
@@ -37,15 +38,19 @@ export function CreateSectionDialog({ onFinished, sectionToEdit }: CreateSection
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
className="mx_CreateSectionDialog"
|
||||
className={classNames("mx_CreateSectionDialog", {
|
||||
mx_CreateSectionDialog_edition: isEdition,
|
||||
})}
|
||||
onFinished={() => onFinished(false, value)}
|
||||
title={isEdition ? _t("create_section_dialog|title_edition") : _t("create_section_dialog|title")}
|
||||
hasCancel={true}
|
||||
>
|
||||
<Flex gap="var(--cpd-space-6x)" direction="column" className="mx_CreateSectionDialog_content">
|
||||
<Text as="span" weight="medium">
|
||||
{_t("create_section_dialog|description")}
|
||||
</Text>
|
||||
{!isEdition && (
|
||||
<Text as="span" weight="medium">
|
||||
{_t("create_section_dialog|description")}
|
||||
</Text>
|
||||
)}
|
||||
<Form.Root
|
||||
className="mx_CreateSectionDialog_form"
|
||||
onSubmit={(e) => {
|
||||
|
||||
@@ -246,7 +246,9 @@
|
||||
"check_code_heading": "Sisesta teises seadmes kuvatav number",
|
||||
"check_code_input_label": "2-kohaline kood",
|
||||
"check_code_mismatch": "Numbrid ei klapi",
|
||||
"choose_desktop_computer": "Vali „%(desktopComputer)s“",
|
||||
"completing_setup": "Lõpetame uue seadme seadistamise",
|
||||
"desktop_computer": "Lauaarvuti",
|
||||
"error_etag_missing": "Tekkis ootamatu viga. Selle põhjuseks võivad olla brauseri lisamoodul, proksiserveri seadistused või koduserveri vigased seadistused.",
|
||||
"error_expired": "Sisselogimine aegus. Palun proovi uuesti.",
|
||||
"error_expired_title": "Sisselogimine ei jõudnud õigeaegselt lõpule",
|
||||
@@ -267,15 +269,22 @@
|
||||
"error_user_declined": "Sa keeldusid teises seadmes sisselogimispäringust.",
|
||||
"error_user_declined_title": "Sa keeldusid sisselogimast",
|
||||
"follow_remaining_instructions": "Teise seadme verifitseerimiseks järgi ülejäänud juhiseid",
|
||||
"open_element_mobile_device": "Ava oma nutiseadmes %(brand)s",
|
||||
"open_element_other_device": "Ava %(brand)s oma teises seadmes",
|
||||
"point_the_camera": "Suuna kaamera siin näidatud QR-koodi peale",
|
||||
"scan_code_instruction": "Skaneeri QR-koodi teise seadmega",
|
||||
"ready_to_scan": "Skaneerimiseks valmis",
|
||||
"scan_code_instruction": "Skaneeri seda QR-koodi",
|
||||
"scan_code_instruction_reciprocate": "Skaneeri seda QR-koodi teise seadmega",
|
||||
"scan_qr_code": "Logi sisse QR-koodi alusel",
|
||||
"security_code_prompt": "Kui seda küsitakse, sisesta teises seadmes allolev kood.",
|
||||
"security_code_prompt": "Sinu teenusepakkuja võib sisselogimise verifitseerimiseks küsida seda koodi.",
|
||||
"security_code_title": "Sinu verifitseerimiskood",
|
||||
"select_qr_code": "Val i „%(scanQRCode)s“",
|
||||
"select_ready_to_scan": "Vali „%(readyToScan)s“ ja skaneeri siinnäidatud QR-koodi",
|
||||
"start_over": "Alusta otsast peale",
|
||||
"tap_avatar_link_new_device": "Klõpsa oma tunnuspilti ja vali „%(linkNewDevice)s“",
|
||||
"unsupported_explainer": "Sinu teenusepakkuja ei toeta võimalust logida sisse QR-koodi abil.",
|
||||
"unsupported_heading": "QR-koodi kasutamine pole toetatud",
|
||||
"waiting_for_device": "Ootame, et teine seade logiks võrku"
|
||||
"waiting_for_device": "Ootame sinu teise seadme järgi"
|
||||
},
|
||||
"register_action": "Loo konto",
|
||||
"registration": {
|
||||
@@ -334,8 +343,10 @@
|
||||
"sign_in_description": "Jätkamaks kasuta oma kontot.",
|
||||
"sign_in_instead": "Pigem logi sisse",
|
||||
"sign_in_instead_prompt": "Sul juba on kasutajakonto olemas? <a>Siis logi siin sisse</a>",
|
||||
"sign_in_manually": "Logi sisse käsitsi",
|
||||
"sign_in_or_register": "Logi sisse või loo uus konto",
|
||||
"sign_in_or_register_description": "Jätkamaks kasuta oma kontot või loo uus konto.",
|
||||
"sign_in_with_qr": "Logi sisse QR-koodi alusel",
|
||||
"sign_in_with_sso": "Logi sisse ühekordse sisselogimise abil",
|
||||
"signing_in": "Login sisse…",
|
||||
"soft_logout": {
|
||||
@@ -1517,6 +1528,7 @@
|
||||
"bridge_state_manager": "Seda võrgusilda haldab <user />.",
|
||||
"bridge_state_workspace": "Tööruum: <networkLink/>",
|
||||
"click_for_info": "Lisateabe jaoks klõpsi",
|
||||
"config_only": "Saab sisselülitada ainult config.json failist.",
|
||||
"currently_experimental": "Parasjagu katsejärgus.",
|
||||
"custom_themes": "Kohandatud kujunduste lisamise võimalus",
|
||||
"dynamic_room_predecessors": "Jututoa dünaamilised eellased",
|
||||
@@ -1561,6 +1573,7 @@
|
||||
"leave_beta_reload": "Beeta-funktsionaalsuste kasutamise lõpetamisel laadime uuesti rakenduse %(brand)s.",
|
||||
"location_share_live": "Asukoha jagamine reaalajas",
|
||||
"location_share_live_description": "Tegemist on ajutise ja esialgse lahendusega: asukohad on jututoa ajaloos näha.",
|
||||
"login_with_qr": "Logi sisse QR-koodiga",
|
||||
"mjolnir": "Uued võimalused osalejate eiramiseks",
|
||||
"msc3531_hide_messages_pending_moderation": "Luba modereerimist ootavate sõnumite peitmist.",
|
||||
"new_room_list": "Võta kasutusele uus jututubade loend",
|
||||
|
||||
@@ -270,11 +270,11 @@
|
||||
"point_the_camera": "Scannez le QR code affiché ici",
|
||||
"scan_code_instruction": "Scannez le code QR",
|
||||
"scan_qr_code": "Se connecter avec un QR code",
|
||||
"security_code_prompt": "Si vous y êtes invité, saisissez le code ci-dessous sur votre autre appareil.",
|
||||
"security_code_prompt": "Votre fournisseur de compte peut vous demander le code suivant pour vérifier la connexion.",
|
||||
"select_qr_code": "Sélectionnez « %(scanQRCode)s »",
|
||||
"unsupported_explainer": "Votre fournisseur de compte ne prend pas en charge la connexion à un nouvel appareil à l’aide d’un code QR.",
|
||||
"unsupported_heading": "Le code QR n'est pas pris en charge",
|
||||
"waiting_for_device": "En attente de connexion de l’appareil"
|
||||
"waiting_for_device": "En attente de votre autre appareil"
|
||||
},
|
||||
"register_action": "Créer un compte",
|
||||
"registration": {
|
||||
|
||||
@@ -246,7 +246,9 @@
|
||||
"check_code_heading": "Wprowadź numer wyświetlany na drugim urządzeniu",
|
||||
"check_code_input_label": "2-cyfrowy kod",
|
||||
"check_code_mismatch": "Liczby się nie zgadzają",
|
||||
"choose_desktop_computer": "Wybierz \"%(desktopComputer)s\"",
|
||||
"completing_setup": "Kończenie konfiguracji nowego urządzenia",
|
||||
"desktop_computer": "Komputer stacjonarny",
|
||||
"error_etag_missing": "Wystąpił nieoczekiwany błąd. Może to być spowodowane rozszerzeniem przeglądarki, serwerem proxy lub błędną konfiguracją serwera.",
|
||||
"error_expired": "Logowanie wygasło. Spróbuj ponownie.",
|
||||
"error_expired_title": "Logowanie nie zostało zakończone na czas",
|
||||
@@ -267,15 +269,22 @@
|
||||
"error_user_declined": "Ty lub Twój dostawca konta odrzucił żądanie logowania.",
|
||||
"error_user_declined_title": "Logowanie odrzucone",
|
||||
"follow_remaining_instructions": "Postępuj zgodnie z pozostałymi instrukcjami, aby zweryfikować Twoje inne urządzenie",
|
||||
"open_element_mobile_device": "Otwórz %(brand)s na swoim urządzeniu mobilnym",
|
||||
"open_element_other_device": "Otwórz %(brand)s na innym urządzeniu",
|
||||
"point_the_camera": "Skieruj kamerę na pokazany tutaj kod QR",
|
||||
"scan_code_instruction": "Skanuj kod QR za pomocą innego urządzenia",
|
||||
"ready_to_scan": "Gotowy do skanowania",
|
||||
"scan_code_instruction": "Skanuj kod QR",
|
||||
"scan_code_instruction_reciprocate": "Zeskanuj kod QR innym urządzeniem",
|
||||
"scan_qr_code": "Zaloguj się kodem QR",
|
||||
"security_code_prompt": "Jeśli zostaniesz poproszony, wprowadź poniższy kod na drugim urządzeniu.",
|
||||
"security_code_prompt": "Twój dostawca konta może poprosić o podany kod w celu weryfikacji logowania.",
|
||||
"security_code_title": "Twój kod weryfikacyjny",
|
||||
"select_qr_code": "Wybierz '%(scanQRCode)s'",
|
||||
"select_ready_to_scan": "Wybierz \"%(readyToScan)s\" i zeskanuj kod QR widoczny tutaj",
|
||||
"start_over": "Zacznij od nowa",
|
||||
"tap_avatar_link_new_device": "Kliknij swój awatar i wybierz \"%(linkNewDevice)s\"",
|
||||
"unsupported_explainer": "Twój dostawca konta nie obsługuje logowania nowego urządzenia za pomocą kodu QR.",
|
||||
"unsupported_heading": "Kod QR nie jest wspierany",
|
||||
"waiting_for_device": "Oczekiwanie na logowanie urządzenia"
|
||||
"waiting_for_device": "Oczekiwanie na drugie urządzenie"
|
||||
},
|
||||
"register_action": "Utwórz konto",
|
||||
"registration": {
|
||||
@@ -334,8 +343,10 @@
|
||||
"sign_in_description": "Użyj swojego konta, aby kontynuować.",
|
||||
"sign_in_instead": "Zamiast tego zaloguj się",
|
||||
"sign_in_instead_prompt": "Masz już konto? <a>Zaloguj się tutaj </a>",
|
||||
"sign_in_manually": "Zaloguj się ręcznie",
|
||||
"sign_in_or_register": "Zaloguj się lub utwórz konto",
|
||||
"sign_in_or_register_description": "Użyj konta lub utwórz nowe, aby kontynuować.",
|
||||
"sign_in_with_qr": "Zaloguj się kodem QR",
|
||||
"sign_in_with_sso": "Zaloguj się za pomocą pojedynczego logowania",
|
||||
"signing_in": "Logowanie…",
|
||||
"soft_logout": {
|
||||
@@ -1528,6 +1539,7 @@
|
||||
"bridge_state_manager": "Ten mostek jest zarządzany przez <user />.",
|
||||
"bridge_state_workspace": "Obszar roboczy: <networkLink/>",
|
||||
"click_for_info": "Kliknij po więcej informacji",
|
||||
"config_only": "Może zostać włączony tylko poprzez config.json.",
|
||||
"currently_experimental": "Aktualnie eksperymentalne.",
|
||||
"custom_themes": "Obsługa dodawania niestandardowych motywów",
|
||||
"dynamic_room_predecessors": "Dynamiczne poprzedniki pokoju",
|
||||
@@ -1572,6 +1584,7 @@
|
||||
"leave_beta_reload": "Opuszczenie bety, wczyta ponownie %(brand)s.",
|
||||
"location_share_live": "Udostępnianie lokalizacji na żywo",
|
||||
"location_share_live_description": "Implementacja tymczasowa. Lokalizacje są zapisywane w historii pokoju.",
|
||||
"login_with_qr": "Zaloguj się za pomocą kodu QR",
|
||||
"mjolnir": "Nowe sposoby na ignorowanie osób",
|
||||
"msc3531_hide_messages_pending_moderation": "Daj moderatorom ukrycie wiadomości które są sprawdzane.",
|
||||
"new_room_list": "Włącz nową listę pokojów",
|
||||
|
||||
@@ -1140,7 +1140,7 @@ export const SETTINGS: Settings = {
|
||||
"urlPreviewsEnabled": {
|
||||
// Enabled by default and client configurable as this setting only allows unencrypted
|
||||
// messages to be previewed.
|
||||
supportedLevels: [SettingLevel.DEVICE, SettingLevel.ACCOUNT, SettingLevel.CONFIG],
|
||||
supportedLevels: [SettingLevel.ROOM_DEVICE, SettingLevel.DEVICE, SettingLevel.ACCOUNT, SettingLevel.CONFIG],
|
||||
supportedLevelsAreOrdered: true,
|
||||
displayName: _td("settings|inline_url_previews_default"),
|
||||
default: true,
|
||||
|
||||
@@ -37,6 +37,8 @@ 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;
|
||||
// From https://github.com/matrix-org/matrix-spec-proposals/pull/4095
|
||||
export const BUNDLED_LINK_PREVIEWS = "com.beeper.linkpreviews";
|
||||
|
||||
export enum PreviewVisibility {
|
||||
/**
|
||||
@@ -387,6 +389,18 @@ export class UrlPreviewGroupViewModel
|
||||
* for the previously-calculated links.
|
||||
*/
|
||||
private async computeSnapshot(): Promise<void> {
|
||||
// This uses MSC4095. If the sender has sent us an empty URL previews bundle
|
||||
// then they do not want to have URL previews be visible.
|
||||
const bundledLinkPreviews = this.props.mxEvent.getContent()[BUNDLED_LINK_PREVIEWS];
|
||||
if (Array.isArray(bundledLinkPreviews) && bundledLinkPreviews.length === 0) {
|
||||
return this.snapshot.merge({
|
||||
previews: [],
|
||||
totalPreviewCount: 0,
|
||||
previewsLimited: false,
|
||||
overPreviewLimit: false,
|
||||
});
|
||||
} // otherwise, we do not support bundled previews yet so will fallback to old behaviour.
|
||||
|
||||
const previews =
|
||||
this.visibility <= PreviewVisibility.UserHidden
|
||||
? []
|
||||
|
||||
@@ -415,6 +415,14 @@ export class RoomListItemViewModel
|
||||
tagRoom(this.props.room, tag);
|
||||
};
|
||||
|
||||
public onRemoveFromSection = (): void => {
|
||||
const roomTags = this.props.room.tags;
|
||||
const sectionTag = RoomListStoreV3.instance.orderedSectionTags.find((tag) => Boolean(roomTags[tag]));
|
||||
if (sectionTag) {
|
||||
tagRoom(this.props.room, sectionTag);
|
||||
}
|
||||
};
|
||||
|
||||
private onOrderedCustomSectionsChange = (): void => {
|
||||
// Rebuild sections list to reflect new order
|
||||
const sections = RoomListItemViewModel.buildSections(this.props.room.tags);
|
||||
|
||||
@@ -9,7 +9,10 @@ import { expect } from "@jest/globals";
|
||||
|
||||
import type { MockedObject } from "jest-mock";
|
||||
import type { MatrixClient, IPreviewUrlResponse } from "matrix-js-sdk/src/matrix";
|
||||
import { UrlPreviewGroupViewModel } from "../../../src/viewmodels/message-body/UrlPreviewGroupViewModel";
|
||||
import {
|
||||
BUNDLED_LINK_PREVIEWS,
|
||||
UrlPreviewGroupViewModel,
|
||||
} from "../../../src/viewmodels/message-body/UrlPreviewGroupViewModel";
|
||||
import type { UrlPreview } from "@element-hq/web-shared-components";
|
||||
import { getMockClientWithEventEmitter, mkEvent } from "../../test-utils";
|
||||
|
||||
@@ -22,7 +25,9 @@ const BASIC_PREVIEW_OGDATA = {
|
||||
"og:site_name": "Example.org",
|
||||
};
|
||||
|
||||
function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible: true }): {
|
||||
function getViewModel(
|
||||
{ mediaVisible, visible, showPreview } = { mediaVisible: true, visible: true, showPreview: true },
|
||||
): {
|
||||
vm: UrlPreviewGroupViewModel;
|
||||
client: MockedObject<MatrixClient>;
|
||||
onImageClicked: jest.Mock<void, [UrlPreview]>;
|
||||
@@ -41,7 +46,9 @@ function getViewModel({ mediaVisible, visible } = { mediaVisible: true, visible:
|
||||
event: true,
|
||||
user: "@foo:bar",
|
||||
type: "m.room.message",
|
||||
content: {},
|
||||
content: {
|
||||
...(showPreview ? undefined : { [BUNDLED_LINK_PREVIEWS]: [] }),
|
||||
},
|
||||
id: "$id",
|
||||
}),
|
||||
});
|
||||
@@ -94,7 +101,7 @@ describe("UrlPreviewGroupViewModel", () => {
|
||||
]);
|
||||
});
|
||||
it("should hide preview when invisible", async () => {
|
||||
const { vm, client } = getViewModel({ visible: false, mediaVisible: true });
|
||||
const { vm, client } = getViewModel({ visible: false, mediaVisible: true, showPreview: true });
|
||||
const msg = document.createElement("div");
|
||||
msg.innerHTML = '<a href="https://example.org">Test</a>';
|
||||
await vm.updateEventElement(msg);
|
||||
@@ -152,7 +159,7 @@ describe("UrlPreviewGroupViewModel", () => {
|
||||
expect(vm.getSnapshot().previews[0].siteIcon).toBeTruthy();
|
||||
});
|
||||
it("should ignore media when mediaVisible is false", async () => {
|
||||
const { vm, client } = getViewModel({ mediaVisible: false, visible: true });
|
||||
const { vm, client } = getViewModel({ mediaVisible: false, visible: true, showPreview: true });
|
||||
client.getUrlPreview.mockResolvedValueOnce({
|
||||
"og:title": "This is an example!",
|
||||
"og:type": "document",
|
||||
@@ -225,6 +232,21 @@ describe("UrlPreviewGroupViewModel", () => {
|
||||
await vm.onShowClick();
|
||||
expect(vm.getSnapshot()).toMatchSnapshot();
|
||||
});
|
||||
it("should hide a preview if the message requests it", async () => {
|
||||
const { vm, client } = getViewModel({ showPreview: false, mediaVisible: true, visible: true });
|
||||
client.getUrlPreview.mockResolvedValueOnce(BASIC_PREVIEW_OGDATA);
|
||||
const msg = document.createElement("div");
|
||||
msg.innerHTML = '<a href="https://example.org">Test</a>';
|
||||
await vm.updateEventElement(msg);
|
||||
expect(vm.getSnapshot()).toMatchInlineSnapshot(`
|
||||
{
|
||||
"overPreviewLimit": false,
|
||||
"previews": [],
|
||||
"previewsLimited": false,
|
||||
"totalPreviewCount": 0,
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe("calculates author", () => {
|
||||
it("should use the profile:username if provided", async () => {
|
||||
|
||||