From 435acf1ba70739dee95aca49a3f0dcffe06de104 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Thu, 14 May 2026 19:42:05 +0530 Subject: [PATCH] Call Tile - Support declined call tile (#33371) * Extract shared types and css * Add CallDeclinedTileView * Add storybook and view tests * Support declined event in view model * Render declined view from tile factory * Update snapshots * Add 10px padding to top and bottom * Distinguish between call declined by us and other users * Support `isCallDeclinedByUs` in view model * Update tests * Add better comments * Rename getInitial to generateSnapshot --- apps/web/src/events/EventTileFactory.tsx | 9 +- .../call/CallStartedTileViewModel.ts | 79 ---------- .../event-tile/call/CallTileViewModel.ts | 141 +++++++++++++++++ .../CallStartedTileViewModel-test.ts | 71 --------- .../event-tiles/CallTileViewModel-test.ts | 145 ++++++++++++++++++ .../call-declined-by-us-auto.png | Bin 0 -> 20448 bytes .../default-auto.png | Bin 0 -> 20177 bytes .../video-call-auto.png | Bin 0 -> 20079 bytes .../voice-call-auto.png | Bin 0 -> 20178 bytes .../src/i18n/strings/en_EN.json | 4 + .../CallDeclinedTileView.stories.tsx | 71 +++++++++ .../CallDeclinedTileView.test.tsx | 34 ++++ .../CallDeclinedTile/CallDeclinedTileView.tsx | 55 +++++++ .../CallDeclinedTileView.test.tsx.snap | 97 ++++++++++++ .../CallStartedTileView.stories.tsx | 5 +- .../CallStartedTile/CallStartedTileView.tsx | 30 +--- .../CallStartedTileView.test.tsx.snap | 16 +- .../CallTileView.module.css} | 1 + .../timeline/event-tile/call/common/types.ts | 39 +++++ .../room/timeline/event-tile/call/index.ts | 2 + 20 files changed, 608 insertions(+), 191 deletions(-) delete mode 100644 apps/web/src/viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel.ts create mode 100644 apps/web/src/viewmodels/room/timeline/event-tile/call/CallTileViewModel.ts delete mode 100644 apps/web/test/viewmodels/event-tiles/CallStartedTileViewModel-test.ts create mode 100644 apps/web/test/viewmodels/event-tiles/CallTileViewModel-test.ts create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/call-declined-by-us-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/default-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/video-call-auto.png create mode 100644 packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/voice-call-auto.png create mode 100644 packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.test.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.tsx create mode 100644 packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/__snapshots__/CallDeclinedTileView.test.tsx.snap rename packages/shared-components/src/room/timeline/event-tile/call/{CallStartedTile/CallStartedTileView.module.css => common/CallTileView.module.css} (97%) create mode 100644 packages/shared-components/src/room/timeline/event-tile/call/common/types.ts diff --git a/apps/web/src/events/EventTileFactory.tsx b/apps/web/src/events/EventTileFactory.tsx index 830f427614..7c53a97736 100644 --- a/apps/web/src/events/EventTileFactory.tsx +++ b/apps/web/src/events/EventTileFactory.tsx @@ -18,6 +18,7 @@ import { M_POLL_START, } from "matrix-js-sdk/src/matrix"; import { + CallDeclinedTileView, CallStartedTileView, EncryptionEventView, HiddenBodyView, @@ -56,7 +57,7 @@ import { TextualEventViewModel } from "../viewmodels/room/timeline/event-tile/Te import { HiddenBodyViewModel } from "../viewmodels/room/timeline/event-tile/body/HiddenBodyViewModel"; import { ViewSourceEventViewModel } from "../viewmodels/room/timeline/event-tile/body/ViewSourceEventViewModel"; import { ElementCallEventType } from "../call-types"; -import { CallStartedTileViewModel } from "../viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel"; +import { CallTileViewModel } from "../viewmodels/room/timeline/event-tile/call/CallTileViewModel"; // Subset of EventTile's IProps plus some mixins export interface EventTileTypeProps extends Pick< @@ -187,9 +188,9 @@ function RoomAvatarEventWrappedView({ mxEvent, ref }: IBodyProps): JSX.Element { } const RoomAvatarEventFactory: Factory = (ref, props) => ; -function CallStartedTileViewWrapped({ mxEvent }: IBodyProps): JSX.Element { - const vm = useCreateAutoDisposedViewModel(() => new CallStartedTileViewModel({ mxEvent })); - return ; +function CallStartedTileViewWrapped({ mxEvent, getRelationsForEvent }: IBodyProps): JSX.Element { + const vm = useCreateAutoDisposedViewModel(() => new CallTileViewModel({ mxEvent, getRelationsForEvent })); + return vm.isCallDeclined ? : ; } export const CallStartedEventFactory: Factory = (ref, props) => { diff --git a/apps/web/src/viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel.ts b/apps/web/src/viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel.ts deleted file mode 100644 index 514c7c7c89..0000000000 --- a/apps/web/src/viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel.ts +++ /dev/null @@ -1,79 +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. - */ - -import { BaseViewModel, CallType, type CallStartedTileViewSnapshot } from "@element-hq/web-shared-components"; - -import type { MatrixEvent } from "matrix-js-sdk/src/matrix"; -import type { IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc"; -import SettingsStore from "../../../../../settings/SettingsStore"; -import { formatTime } from "../../../../../DateUtils"; -import defaultDispatcher from "../../../../../dispatcher/dispatcher"; -import type { SettingUpdatedPayload } from "../../../../../dispatcher/payloads/SettingUpdatedPayload"; -import type { ActionPayload } from "../../../../../dispatcher/payloads"; -import { Action } from "../../../../../dispatcher/actions"; - -export interface CallStartedTileViewModelProps { - mxEvent: MatrixEvent; -} - -function getIntentFromEvent(event: MatrixEvent): CallStartedTileViewSnapshot["type"] { - const content = event.getContent(); - const intentInContent = content["m.call.intent"]; - switch (intentInContent) { - case "audio": - return CallType.Voice; - case "video": - default: - return CallType.Video; - } -} - -function getTimeFromEvent(event: MatrixEvent, showTwelveHour: boolean): CallStartedTileViewSnapshot["timestamp"] { - const content = event.getContent(); - const senderTs = content["sender_ts"]; - const originServerTs = event.getTs(); - const ts = Math.abs(senderTs - originServerTs) > 20000 ? originServerTs : senderTs; - - const date = new Date(ts); - const timestamp = formatTime(date, showTwelveHour); - return timestamp; -} - -function getInitialSnapshot(event: MatrixEvent): CallStartedTileViewSnapshot { - const type = getIntentFromEvent(event); - const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); - const timestamp = getTimeFromEvent(event, showTwelveHour); - return { type, timestamp }; -} - -function isSettingsChangedPayload(payload: ActionPayload): payload is SettingUpdatedPayload { - return payload.action === Action.SettingUpdated; -} - -/** - * ViewModel for a timeline tile that indicates the start of an element call. - */ -export class CallStartedTileViewModel extends BaseViewModel< - CallStartedTileViewSnapshot, - CallStartedTileViewModelProps -> { - public constructor(props: CallStartedTileViewModelProps) { - super(props, getInitialSnapshot(props.mxEvent)); - SettingsStore.monitorSetting("showTwelveHourTimestamps", null); - const token = defaultDispatcher.register(this.onAction); - this.disposables.track(() => { - defaultDispatcher.unregister(token); - }); - } - - private onAction = (payload: ActionPayload): void => { - if (!isSettingsChangedPayload(payload) || payload.settingName !== "showTwelveHourTimestamps") return; - const showTwelveHour = (payload.newValue as boolean) ?? false; - const timestamp = getTimeFromEvent(this.props.mxEvent, showTwelveHour); - this.snapshot.merge({ timestamp }); - }; -} diff --git a/apps/web/src/viewmodels/room/timeline/event-tile/call/CallTileViewModel.ts b/apps/web/src/viewmodels/room/timeline/event-tile/call/CallTileViewModel.ts new file mode 100644 index 0000000000..cfae52080b --- /dev/null +++ b/apps/web/src/viewmodels/room/timeline/event-tile/call/CallTileViewModel.ts @@ -0,0 +1,141 @@ +/* + * 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 { BaseViewModel, CallType, type CallTileViewSnapshot } from "@element-hq/web-shared-components"; +import { EventType, type MatrixEvent, MatrixEventEvent, RelationType } from "matrix-js-sdk/src/matrix"; + +import type { IRTCNotificationContent } from "matrix-js-sdk/src/matrixrtc"; +import SettingsStore from "../../../../../settings/SettingsStore"; +import { formatTime } from "../../../../../DateUtils"; +import defaultDispatcher from "../../../../../dispatcher/dispatcher"; +import type { SettingUpdatedPayload } from "../../../../../dispatcher/payloads/SettingUpdatedPayload"; +import type { ActionPayload } from "../../../../../dispatcher/payloads"; +import { Action } from "../../../../../dispatcher/actions"; +import type { GetRelationsForEvent } from "../../../../../components/views/rooms/EventTile"; +import { MatrixClientPeg } from "../../../../../MatrixClientPeg"; + +export interface CallTileViewModelProps { + /** + * Event of type `org.matrix.msc4075.rtc.notification`. + */ + mxEvent: MatrixEvent; + /** + * Helper to fetch related events from a given event. + */ + getRelationsForEvent?: GetRelationsForEvent; +} + +function getIntentFromEvent(event: MatrixEvent): CallTileViewSnapshot["type"] { + const content = event.getContent(); + const intentInContent = content["m.call.intent"]; + switch (intentInContent) { + case "audio": + return CallType.Voice; + case "video": + default: + return CallType.Video; + } +} + +function getTs(event: MatrixEvent): number { + if (event.getType() === EventType.RTCNotification) { + /** + * According to the spec: + * Receivers SHOULD use origin_server_ts if |sender_ts - origin_server_ts| > 20000 ms. + */ + const content = event.getContent(); + const senderTs = content["sender_ts"]; + const originServerTs = event.getTs(); + const ts = Math.abs(senderTs - originServerTs) > 20000 ? originServerTs : senderTs; + return ts; + } else return event.getTs(); +} + +function getTimeFromEvent(event: MatrixEvent, showTwelveHour: boolean): CallTileViewSnapshot["timestamp"] { + const ts = getTs(event); + const date = new Date(ts); + const timestamp = formatTime(date, showTwelveHour); + return timestamp; +} + +function generateSnapshot( + event: MatrixEvent, + getRelationsForEvent?: GetRelationsForEvent, +): { snapshot: CallTileViewSnapshot; declineEvent: MatrixEvent | null } { + const type = getIntentFromEvent(event); + const declineEvent = getDeclinedEvent(event, getRelationsForEvent); + let isCallDeclinedByUs: boolean | undefined; + if (declineEvent) { + isCallDeclinedByUs = declineEvent.getSender() === MatrixClientPeg.get()?.getUserId(); + } + const showTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps"); + const timestamp = getTimeFromEvent(declineEvent ?? event, showTwelveHour); + return { snapshot: { type, timestamp, isCallDeclinedByUs }, declineEvent }; +} + +function isSettingsChangedPayload(payload: ActionPayload): payload is SettingUpdatedPayload { + return payload.action === Action.SettingUpdated; +} + +/** + * Get a declined event that is related to the given rtc notification event. + * @param event rtc notification event + */ +function getDeclinedEvent(event: MatrixEvent, getRelationsForEvent?: GetRelationsForEvent): MatrixEvent | null { + const eventId = event.getId(); + if (eventId && getRelationsForEvent) { + const relations = getRelationsForEvent(eventId, RelationType.Reference, EventType.RTCDecline)?.getRelations(); + if (relations) return relations[0]; + } + return null; +} + +/** + * Common view-model for call tiles; currently used to render: + * 1. A tile that indicates that a call occurred (call tombstone). + * 2. A tile that indicates that a call was declined. + */ +export class CallTileViewModel extends BaseViewModel { + /** + * The decline event associated with this call, if any. + */ + private declineEvent: MatrixEvent | null; + + public constructor(props: CallTileViewModelProps) { + const { declineEvent, snapshot } = generateSnapshot(props.mxEvent, props.getRelationsForEvent); + super(props, snapshot); + this.declineEvent = declineEvent; + + // Listen to the changes on settings so that we can update the timestamp format (12H vs 24H). + SettingsStore.monitorSetting("showTwelveHourTimestamps", null); + const token = defaultDispatcher.register(this.onAction); + this.disposables.track(() => { + defaultDispatcher.unregister(token); + }); + + // When a relation is added to the event, recompute the state. + this.disposables.trackListener(props.mxEvent, MatrixEventEvent.RelationsCreated, () => { + const { declineEvent, snapshot } = generateSnapshot(props.mxEvent, props.getRelationsForEvent); + this.declineEvent = declineEvent; + this.snapshot.set(snapshot); + }); + } + + private onAction = (payload: ActionPayload): void => { + if (!isSettingsChangedPayload(payload) || payload.settingName !== "showTwelveHourTimestamps") return; + const showTwelveHour = (payload.newValue as boolean) ?? false; + const timestamp = getTimeFromEvent(this.declineEvent ?? this.props.mxEvent, showTwelveHour); + this.snapshot.merge({ timestamp }); + }; + + /** + * Whether the call associated with this vm has been declined. + */ + public get isCallDeclined(): boolean { + return !!this.declineEvent; + } +} diff --git a/apps/web/test/viewmodels/event-tiles/CallStartedTileViewModel-test.ts b/apps/web/test/viewmodels/event-tiles/CallStartedTileViewModel-test.ts deleted file mode 100644 index 166407da2b..0000000000 --- a/apps/web/test/viewmodels/event-tiles/CallStartedTileViewModel-test.ts +++ /dev/null @@ -1,71 +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. - */ - -import { type MatrixEvent, EventType } from "matrix-js-sdk/src/matrix"; -import { CallType } from "@element-hq/web-shared-components"; -import { waitFor } from "jest-matrix-react"; - -import { mkEvent } from "../../test-utils"; -import { CallStartedTileViewModel } from "../../../src/viewmodels/room/timeline/event-tile/call/CallStartedTileViewModel"; -import SettingsStore from "../../../src/settings/SettingsStore"; -import { SettingLevel } from "../../../src/settings/SettingLevel"; - -function getMockedRtcNotificationEvent(intent: string, senderTs: number, serverTs: number): MatrixEvent { - const mockEvent = mkEvent({ - type: EventType.RTCNotification, - user: "@foo:m.org", - content: { - "m.call.intent": intent, - "sender_ts": senderTs, - }, - ts: serverTs, - event: true, - }); - return mockEvent; -} - -describe("CallStartedTileViewModel", () => { - it("should set voice intent in state", () => { - const mxEvent = getMockedRtcNotificationEvent("audio", 1752583130365, 1752583130365); - const vm = new CallStartedTileViewModel({ mxEvent }); - const { type } = vm.getSnapshot(); - expect(type).toStrictEqual(CallType.Voice); - }); - - it("should set video intent in state", () => { - const mxEvent = getMockedRtcNotificationEvent("video", 1752583130365, 1752583130365); - const vm = new CallStartedTileViewModel({ mxEvent }); - const { type } = vm.getSnapshot(); - expect(type).toStrictEqual(CallType.Video); - }); - - it("should calculate time string correctly", () => { - const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); - const vm = new CallStartedTileViewModel({ mxEvent }); - const { timestamp } = vm.getSnapshot(); - expect(timestamp).toStrictEqual("17:55"); - }); - - it("should calculate time string correctly when configured to use 12 hour format", async () => { - const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); - await SettingsStore.setValue("showTwelveHourTimestamps", null, SettingLevel.DEVICE, true); - const vm = new CallStartedTileViewModel({ mxEvent }); - const { timestamp } = vm.getSnapshot(); - expect(timestamp).toStrictEqual("5:55 PM"); - SettingsStore.reset(); - }); - - it("should change timestamp format when setting is modified", async () => { - const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); - const vm = new CallStartedTileViewModel({ mxEvent }); - expect(vm.getSnapshot().timestamp).toStrictEqual("17:55"); - await SettingsStore.setValue("showTwelveHourTimestamps", null, SettingLevel.DEVICE, true); - await waitFor(() => { - expect(vm.getSnapshot().timestamp).toStrictEqual("5:55 PM"); - }); - }); -}); diff --git a/apps/web/test/viewmodels/event-tiles/CallTileViewModel-test.ts b/apps/web/test/viewmodels/event-tiles/CallTileViewModel-test.ts new file mode 100644 index 0000000000..9da5030d50 --- /dev/null +++ b/apps/web/test/viewmodels/event-tiles/CallTileViewModel-test.ts @@ -0,0 +1,145 @@ +/* + * 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 { EventType, type MatrixEvent, MatrixEventEvent, RelationType } from "matrix-js-sdk/src/matrix"; +import { CallType } from "@element-hq/web-shared-components"; +import { waitFor } from "jest-matrix-react"; + +import { mkEvent, stubClient } from "../../test-utils"; +import { CallTileViewModel } from "../../../src/viewmodels/room/timeline/event-tile/call/CallTileViewModel"; +import SettingsStore from "../../../src/settings/SettingsStore"; +import { SettingLevel } from "../../../src/settings/SettingLevel"; +import { MatrixClientPeg } from "../../../src/MatrixClientPeg"; + +function getMockedRtcNotificationEvent(intent: string, senderTs: number, serverTs: number): MatrixEvent { + const mockEvent = mkEvent({ + type: EventType.RTCNotification, + user: "@foo:m.org", + content: { + "m.call.intent": intent, + "sender_ts": senderTs, + }, + ts: serverTs, + event: true, + }); + return mockEvent; +} + +function getMockedRtcDeclineEvent(rtcNotificationEvent: MatrixEvent, sender = "@foo:m.org"): MatrixEvent { + const mockEvent = mkEvent({ + type: EventType.RTCDecline, + user: sender, + content: { + "m.relates_to": { + rel_type: "m.reference", + event_id: rtcNotificationEvent.getId(), + }, + }, + ts: 924285416000, + event: true, + }); + return mockEvent; +} + +describe("CallTileViewModel", () => { + it("should set voice intent in state", () => { + const mxEvent = getMockedRtcNotificationEvent("audio", 1752583130365, 1752583130365); + const vm = new CallTileViewModel({ mxEvent }); + const { type } = vm.getSnapshot(); + expect(type).toStrictEqual(CallType.Voice); + }); + + it("should set video intent in state", () => { + const mxEvent = getMockedRtcNotificationEvent("video", 1752583130365, 1752583130365); + const vm = new CallTileViewModel({ mxEvent }); + const { type } = vm.getSnapshot(); + expect(type).toStrictEqual(CallType.Video); + }); + + it("should calculate time string correctly", () => { + const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); + const vm = new CallTileViewModel({ mxEvent }); + const { timestamp } = vm.getSnapshot(); + expect(timestamp).toStrictEqual("17:55"); + }); + + it("should calculate time string correctly when configured to use 12 hour format", async () => { + const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); + await SettingsStore.setValue("showTwelveHourTimestamps", null, SettingLevel.DEVICE, true); + const vm = new CallTileViewModel({ mxEvent }); + const { timestamp } = vm.getSnapshot(); + expect(timestamp).toStrictEqual("5:55 PM"); + SettingsStore.reset(); + }); + + it("should change timestamp format when setting is modified", async () => { + const mxEvent = getMockedRtcNotificationEvent("video", 924285348000, 924285348000); + const vm = new CallTileViewModel({ mxEvent }); + expect(vm.getSnapshot().timestamp).toStrictEqual("17:55"); + await SettingsStore.setValue("showTwelveHourTimestamps", null, SettingLevel.DEVICE, true); + await waitFor(() => { + expect(vm.getSnapshot().timestamp).toStrictEqual("5:55 PM"); + }); + SettingsStore.reset(); + }); + + describe("On call declined", () => { + it("should calculate isCallDeclined correctly", () => { + const mxEvent = getMockedRtcNotificationEvent("audio", 1752583130365, 1752583130365); + // When there's no decline event, isCallDeclined = false + const vm1 = new CallTileViewModel({ mxEvent, getRelationsForEvent: jest.fn() }); + expect(vm1.isCallDeclined).toStrictEqual(false); + + // When there's a decline event, isCallDeclined = true + const declineEvent = getMockedRtcDeclineEvent(mxEvent); + const getRelationsForEvent = jest.fn().mockReturnValue({ + getRelations: () => [declineEvent], + }); + const vm2 = new CallTileViewModel({ mxEvent, getRelationsForEvent }); + expect(vm2.isCallDeclined).toStrictEqual(true); + }); + + it("should calculate isCallDeclinedByUs correctly", () => { + const cli = stubClient(); + cli.getUserId = jest.fn().mockReturnValue("@bar:m.org"); + + const mxEvent = getMockedRtcNotificationEvent("audio", 924285348000, 924285348000); + const declineEvent: MatrixEvent[] = []; + const getRelationsForEvent = jest.fn().mockReturnValue({ + getRelations: () => declineEvent, + }); + + // Decline event sent by somebody else + declineEvent.push(getMockedRtcDeclineEvent(mxEvent)); + const vm = new CallTileViewModel({ mxEvent, getRelationsForEvent }); + expect(vm.getSnapshot().isCallDeclinedByUs).toStrictEqual(false); + + // Decline event sent by us + declineEvent.pop(); + declineEvent.push(getMockedRtcDeclineEvent(mxEvent, MatrixClientPeg.get()!.getUserId()!)); + const vm2 = new CallTileViewModel({ mxEvent, getRelationsForEvent }); + expect(vm2.getSnapshot().isCallDeclinedByUs).toStrictEqual(true); + }); + + it("should recompute state when call is declined", () => { + const mxEvent = getMockedRtcNotificationEvent("audio", 924285348000, 924285348000); + const declineEvent: MatrixEvent[] = []; + const getRelationsForEvent = jest.fn().mockReturnValue({ + getRelations: () => declineEvent, + }); + + // No decline event yet, so timestamp should be based on rtc notification event. + const vm = new CallTileViewModel({ mxEvent, getRelationsForEvent }); + expect(vm.getSnapshot().timestamp).toStrictEqual("17:55"); + + // Decline event arrives, timestamp should update to be that of the decline event. + declineEvent.push(getMockedRtcDeclineEvent(mxEvent)); + mxEvent.emit(MatrixEventEvent.RelationsCreated, RelationType.Reference, EventType.RTCDecline); + expect(vm.getSnapshot().timestamp).toStrictEqual("17:56"); + }); + }); +}); diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/call-declined-by-us-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/call-declined-by-us-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..23d6e4af67f66f7ef2bf4747e5e7faa6d3fecbf3 GIT binary patch literal 20448 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 N`(~p}$s6o0{2vMCAvXX3 literal 0 HcmV?d00001 diff --git a/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.stories.tsx/default-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..1b6547d1ca05585812f8ada6d4ef34031348431f GIT binary patch literal 20177 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 { + const vm = useMockedViewModel(rest, {}); + return ; +}; + +const CallDeclinedTileViewWrapper = withViewDocs(CallDeclinedTileViewWrapperImpl, CallDeclinedTileView); + +const meta = { + title: "Timeline/Timeline Event/Call/CallDeclinedTileView", + component: CallDeclinedTileViewWrapper, + tags: ["autodocs"], + argTypes: { + type: { + options: [CallType.Video, CallType.Voice], + control: { type: "select" }, + }, + timestamp: { + control: { type: "text" }, + }, + }, + args: { + type: CallType.Voice, + timestamp: "12:36", + isCallDeclinedByUs: false, + }, + parameters: { + design: { + type: "figma", + url: "https://www.figma.com/design/rTaQE2nIUSLav4Tg3nozq7/Compound-Web-Components?node-id=11217-3914&t=jv0JnUoKJUW1Ko96-4", + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; + +export const VoiceCall: Story = { + args: { + type: CallType.Voice, + }, +}; + +export const VideoCall: Story = { + args: { + type: CallType.Video, + }, +}; + +export const CallDeclinedByUs: Story = { + args: { + type: CallType.Voice, + isCallDeclinedByUs: true, + }, +}; diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.test.tsx b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.test.tsx new file mode 100644 index 0000000000..6fe6250d34 --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.test.tsx @@ -0,0 +1,34 @@ +/* + * 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 { composeStories } from "@storybook/react-vite"; +import { describe, expect, it } from "vitest"; +import React from "react"; +import { render } from "@test-utils"; + +import * as Stories from "./CallDeclinedTileView.stories"; + +const { VideoCall, VoiceCall, CallDeclinedByUs } = composeStories(Stories); + +describe("CallDeclinedTileView", () => { + describe("renders the tile", () => { + it("voice call", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("video call", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("call declined by us", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.tsx b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.tsx new file mode 100644 index 0000000000..f62a124852 --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/CallDeclinedTileView.tsx @@ -0,0 +1,55 @@ +/* + * 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 { + VideoCallDeclinedSolidIcon, + VoiceCallDeclinedSolidIcon, +} from "@vector-im/compound-design-tokens/assets/web/icons"; +import classnames from "classnames"; + +import { useViewModel, type ViewModel } from "../../../../../core/viewmodel"; +import { Flex } from "../../../../../core/utils/Flex"; +import styles from "../common/CallTileView.module.css"; +import { useI18n } from "../../../../../core/i18n/i18nContext"; +import { type CallTileViewSnapshot, CallType } from "../common/types"; + +export type CallDeclinedTileViewModel = ViewModel; + +export interface CallDeclinedTileViewProps { + vm: CallDeclinedTileViewModel; + className?: string; +} + +function getIconForCallType(type: CallType): React.ReactNode { + switch (type) { + case CallType.Video: + return ; + case CallType.Voice: + return ; + } +} + +/** + * View for a timeline tile that indicates that a call was declined. + */ +export function CallDeclinedTileView({ vm, className }: CallDeclinedTileViewProps): React.ReactNode { + const { translate: _t } = useI18n(); + const { type, timestamp, isCallDeclinedByUs } = useViewModel(vm); + const classNames = classnames(className, styles.container); + return ( + + {getIconForCallType(type)} +
+ {isCallDeclinedByUs + ? _t("timeline|call_tile|declined|call_declined_by_us") + : _t("timeline|call_tile|declined|call_declined")} +
+
{timestamp}
+
+ ); +} diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/__snapshots__/CallDeclinedTileView.test.tsx.snap b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/__snapshots__/CallDeclinedTileView.test.tsx.snap new file mode 100644 index 0000000000..ae94ca0ecf --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallDeclinedTile/__snapshots__/CallDeclinedTileView.test.tsx.snap @@ -0,0 +1,97 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CallDeclinedTileView > renders the tile > call declined by us 1`] = ` +
+
+ + + +
+ You declined a call +
+
+ 12:36 +
+
+
+`; + +exports[`CallDeclinedTileView > renders the tile > video call 1`] = ` +
+
+ + + +
+ Call declined +
+
+ 12:36 +
+
+
+`; + +exports[`CallDeclinedTileView > renders the tile > voice call 1`] = ` +
+
+ + + +
+ Call declined +
+
+ 12:36 +
+
+
+`; diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.stories.tsx b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.stories.tsx index 89212b0071..e3bed927c6 100644 --- a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.stories.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.stories.tsx @@ -8,11 +8,12 @@ import React from "react"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import { CallStartedTileView, type CallStartedTileViewSnapshot, CallType } from "./CallStartedTileView"; +import { CallStartedTileView } from "./CallStartedTileView"; import { useMockedViewModel } from "../../../../../core/viewmodel"; import { withViewDocs } from "../../../../../../.storybook/withViewDocs"; +import { CallType, type CallTileViewSnapshot } from "../common/types"; -const CallStartedTileViewWrapperImpl = ({ ...rest }: CallStartedTileViewSnapshot): React.ReactNode => { +const CallStartedTileViewWrapperImpl = ({ ...rest }: CallTileViewSnapshot): React.ReactNode => { const vm = useMockedViewModel(rest, {}); return ; }; diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.tsx b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.tsx index 53e4619aee..a6c2a5fdae 100644 --- a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.tsx +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.tsx @@ -11,35 +11,11 @@ import classnames from "classnames"; import { useViewModel, type ViewModel } from "../../../../../core/viewmodel"; import { Flex } from "../../../../../core/utils/Flex"; -import styles from "./CallStartedTileView.module.css"; +import styles from "../common/CallTileView.module.css"; import { useI18n } from "../../../../../core/i18n/i18nContext"; +import { type CallTileViewSnapshot, CallType } from "../common/types"; -/** - * Represents whether a call is a voice call or video call. - */ -export const enum CallType { - /** - * This is a voice call. - */ - Voice = "voice", - /** - * This is a video call. - */ - Video = "video", -} - -export type CallStartedTileViewSnapshot = { - /** - * What type of call this tile needs to render for. - */ - type: CallType; - /** - * Time when this call was started. - */ - timestamp: string; -}; - -export type CallStartedTileViewModel = ViewModel; +export type CallStartedTileViewModel = ViewModel; export interface CallStartedTileViewProps { vm: CallStartedTileViewModel; diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/__snapshots__/CallStartedTileView.test.tsx.snap b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/__snapshots__/CallStartedTileView.test.tsx.snap index eb67bd154a..83ed1ca614 100644 --- a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/__snapshots__/CallStartedTileView.test.tsx.snap +++ b/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/__snapshots__/CallStartedTileView.test.tsx.snap @@ -3,11 +3,11 @@ exports[`CallStartedTileView > renders the tile > video call 1`] = `
renders the tile > video call 1`] = ` />
Video call
12:36
@@ -35,11 +35,11 @@ exports[`CallStartedTileView > renders the tile > video call 1`] = ` exports[`CallStartedTileView > renders the tile > voice call 1`] = `
renders the tile > voice call 1`] = ` />
Voice call
12:36
diff --git a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.module.css b/packages/shared-components/src/room/timeline/event-tile/call/common/CallTileView.module.css similarity index 97% rename from packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.module.css rename to packages/shared-components/src/room/timeline/event-tile/call/common/CallTileView.module.css index c7b03256f1..94a1287e80 100644 --- a/packages/shared-components/src/room/timeline/event-tile/call/CallStartedTile/CallStartedTileView.module.css +++ b/packages/shared-components/src/room/timeline/event-tile/call/common/CallTileView.module.css @@ -13,6 +13,7 @@ border-radius: var(--cpd-space-2x); padding: var(--cpd-space-2x) var(--cpd-space-3x); box-sizing: border-box; + margin: 10px 0; } .title { diff --git a/packages/shared-components/src/room/timeline/event-tile/call/common/types.ts b/packages/shared-components/src/room/timeline/event-tile/call/common/types.ts new file mode 100644 index 0000000000..087c3b45bf --- /dev/null +++ b/packages/shared-components/src/room/timeline/event-tile/call/common/types.ts @@ -0,0 +1,39 @@ +/* + * 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. + */ + +/** + * Represents whether a call is a voice call or video call. + */ +export const enum CallType { + /** + * This is a voice call. + */ + Voice = "voice", + /** + * This is a video call. + */ + Video = "video", +} + +/** + * The snapshot that both the call started and call declined tiles expect. + */ +export type CallTileViewSnapshot = { + /** + * What type of call this tile needs to render for. + */ + type: CallType; + /** + * Time when this call was started. + */ + timestamp: string; + /** + * Whether this call was declined by our user. + * Undefined if not rendering a declined call tile. + */ + isCallDeclinedByUs?: boolean; +}; diff --git a/packages/shared-components/src/room/timeline/event-tile/call/index.ts b/packages/shared-components/src/room/timeline/event-tile/call/index.ts index fc4fb78c6f..0cca79e866 100644 --- a/packages/shared-components/src/room/timeline/event-tile/call/index.ts +++ b/packages/shared-components/src/room/timeline/event-tile/call/index.ts @@ -6,3 +6,5 @@ */ export * from "./CallStartedTile/CallStartedTileView"; +export * from "./CallDeclinedTile/CallDeclinedTileView"; +export * from "./common/types";