diff --git a/Dockerfile b/Dockerfile index 09e0d30984..9b418ac7fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -# syntax=docker.io/docker/dockerfile:1.20-labs@sha256:dbcde2ebc4abc8bb5c3c499b9c9a6876842bf5da243951cd2697f921a7aeb6a9 +# syntax=docker.io/docker/dockerfile:1.21-labs@sha256:2e681d22e86e738a057075f930b81b2ab8bc2a34cd16001484a7453cfa7a03fb # Builder FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:8036dbe5b1f465e3acb8b866031cd06e4f84c31b0e83dabbdc59397a40dbe288 AS builder diff --git a/code_style.md b/code_style.md index b6c1e46e95..bd1fb4371c 100644 --- a/code_style.md +++ b/code_style.md @@ -274,6 +274,7 @@ Inheriting all the rules of TypeScript, the following additionally apply: 20. Do not use `React.Component::forceUpdate`. 21. Prefer to use [compound typography components](https://compound.element.io/?path=/docs/compound-web_typography--docs) instead of raw HTML elements for text. This ensures consistent font usage and letter spacing across the app. 22. If you can't use 21, don't forget to apply the correct CSS classes for font and letter spacing. +23. Prefer to use `Flex` or `Box` components from shared-components for layout instead of raw HTML elements with CSS flexbox styles. ## Stylesheets diff --git a/docs/config.md b/docs/config.md index c8773544fe..5856ce8c43 100644 --- a/docs/config.md +++ b/docs/config.md @@ -581,8 +581,6 @@ Currently, the following UI feature flags are supported: This should only be used if the room history visibility options are managed by the server. - `UIFeature.TimelineEnableRelativeDates` - Display relative date separators (eg: 'Today', 'Yesterday') in the timeline for recent messages. When false day dates will be used. -- `UIFeature.BulkUnverifiedSessionsReminder` - Display popup reminders to verify or remove unverified sessions. Defaults - to true. - `UIFeature.locationSharing` - Whether or not location sharing menus will be shown. - `UIFeature.allowCreatingPublicRooms` - Whether or not public rooms can be created. - `UIFeature.allowCreatingPublicSpaces` - Whether or not public spaces can be created. diff --git a/package.json b/package.json index 4c2d4cb746..491eb9fe56 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@types/react-dom": "19.2.3", "oidc-client-ts": "3.4.1", "jwt-decode": "4.0.0", - "caniuse-lite": "1.0.30001764", + "caniuse-lite": "1.0.30001766", "testcontainers": "^11.0.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0", "wrap-ansi": "npm:wrap-ansi@^7.0.0", diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/default-auto.png new file mode 100644 index 0000000000..9830d14901 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/default-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-date-event-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-date-event-auto.png new file mode 100644 index 0000000000..0c14346698 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-date-event-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-html-child-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-html-child-auto.png new file mode 100644 index 0000000000..a6182a84ab Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-html-child-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-late-event-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-late-event-auto.png new file mode 100644 index 0000000000..9cb04d9757 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/with-late-event-auto.png differ diff --git a/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/without-children-auto.png b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/without-children-auto.png new file mode 100644 index 0000000000..2e132bb28b Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/message-body/TimelineSeparator/TimelineSeparator.stories.tsx/without-children-auto.png differ diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 95c3d1994d..0efa6698f6 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -18,6 +18,7 @@ export * from "./event-tiles/TextualEventView"; export * from "./message-body/MediaBody"; export * from "./message-body/DecryptionFailureBodyView"; export * from "./message-body/ReactionsRowButtonTooltip"; +export * from "./message-body/TimelineSeparator/"; export * from "./pill-input/Pill"; export * from "./pill-input/PillInput"; export * from "./room/RoomStatusBar"; diff --git a/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.module.css b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.module.css new file mode 100644 index 0000000000..fea4615390 --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.module.css @@ -0,0 +1,21 @@ +/* + * 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. + */ + +.timelineSeparator { + clear: both; + margin: var(--cpd-space-1x) 0; + font: var(--cpd-font-body-md-regular); + letter-spacing: var(--cpd-font-letter-spacing-body-md); + color: var(--cpd-color-text-primary); +} + +.timelineSeparator > hr { + flex: 1 1 0; + height: 0; + border: none; + border-bottom: 1px solid var(--cpd-color-gray-400); +} diff --git a/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.stories.tsx b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.stories.tsx new file mode 100644 index 0000000000..cf067f4ecd --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.stories.tsx @@ -0,0 +1,54 @@ +/* + * 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 type { Meta, StoryFn } from "@storybook/react-vite"; +import TimelineSeparator from "./TimelineSeparator"; +import styles from "./TimelineSeparator.module.css"; + +export default { + title: "MessageBody/TimelineSeparator", + component: TimelineSeparator, + tags: ["autodocs"], + args: { + label: "Label Separator", + children: "Timeline Separator", + }, +} as Meta; + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); + +export const WithHtmlChild = Template.bind({}); +WithHtmlChild.args = { + label: "Custom Label", + children: ( + + ), +}; + +export const WithDateEvent = Template.bind({}); +WithDateEvent.args = { + label: "Date Event Separator", + children: "Wednesday", +}; + +export const WithLateEvent = Template.bind({}); +WithLateEvent.args = { + label: "Late Event Separator", + children: "Fri, Jan 9, 2026", +}; + +export const WithoutChildren = Template.bind({}); +WithoutChildren.args = { + children: undefined, + label: "Separator without children", +}; diff --git a/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.test.tsx b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.test.tsx new file mode 100644 index 0000000000..859142b49c --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.test.tsx @@ -0,0 +1,48 @@ +/* + * 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 { render } from "@test-utils"; +import { composeStories } from "@storybook/react-vite"; +import React from "react"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +import * as stories from "./TimelineSeparator.stories.tsx"; + +const { Default, WithHtmlChild, WithoutChildren, WithDateEvent, WithLateEvent } = composeStories(stories); + +describe("TimelineSeparator", () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + describe("Snapshot tests", () => { + it("renders the timeline separator in default state", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders the timeline separator with HTML child", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders the timeline separator with date event", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders the timeline separator with late event", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + + it("renders the timeline separator without children", () => { + const { container } = render(); + expect(container).toMatchSnapshot(); + }); + }); +}); diff --git a/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.tsx b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.tsx new file mode 100644 index 0000000000..cd3af1c1a7 --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/TimelineSeparator.tsx @@ -0,0 +1,54 @@ +/* +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, { type PropsWithChildren } from "react"; +import classNames from "classnames"; + +import styles from "./TimelineSeparator.module.css"; +import { Flex } from "../.."; + +/** + * Timeline separator props + */ +export interface TimelineSeparatorProps { + /** + * Accessible label for the separator (for example: "Today", "Yesterday", or a date). + */ + label: string; + /** + * The CSS class name. + */ + className?: string; + /** + * Optional children to render inside the timeline separator + */ + children?: PropsWithChildren["children"]; +} + +/** + * Generic timeline separator component to render within a MessagePanel + * + * @param label the accessible label string describing the separator + * @param children the children to draw within the timeline separator + */ +const TimelineSeparator: React.FC = ({ label, className, children }) => { + // ARIA treats
s as separators, here we abuse them slightly so manually treat this entire thing as one + return ( + +
+ {children} +
+
+ ); +}; + +export default TimelineSeparator; diff --git a/packages/shared-components/src/message-body/TimelineSeparator/__snapshots__/TimelineSeparator.test.tsx.snap b/packages/shared-components/src/message-body/TimelineSeparator/__snapshots__/TimelineSeparator.test.tsx.snap new file mode 100644 index 0000000000..e45501e14d --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/__snapshots__/TimelineSeparator.test.tsx.snap @@ -0,0 +1,100 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`TimelineSeparator > Snapshot tests > renders the timeline separator in default state 1`] = ` +
+ +
+`; + +exports[`TimelineSeparator > Snapshot tests > renders the timeline separator with HTML child 1`] = ` +
+ +
+`; + +exports[`TimelineSeparator > Snapshot tests > renders the timeline separator with date event 1`] = ` +
+ +
+`; + +exports[`TimelineSeparator > Snapshot tests > renders the timeline separator with late event 1`] = ` +
+ +
+`; + +exports[`TimelineSeparator > Snapshot tests > renders the timeline separator without children 1`] = ` +
+ +
+`; diff --git a/packages/shared-components/src/message-body/TimelineSeparator/index.ts b/packages/shared-components/src/message-body/TimelineSeparator/index.ts new file mode 100644 index 0000000000..c5812abb07 --- /dev/null +++ b/packages/shared-components/src/message-body/TimelineSeparator/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { default as TimelineSeparator, type TimelineSeparatorProps } from "./TimelineSeparator"; diff --git a/packages/shared-components/vitest.config.ts b/packages/shared-components/vitest.config.ts index 32a0f9ae24..2d5d55bc16 100644 --- a/packages/shared-components/vitest.config.ts +++ b/packages/shared-components/vitest.config.ts @@ -81,7 +81,10 @@ export default defineConfig({ configDir: path.join(dirname, ".storybook"), storybookScript: "storybook --ci", }), - storybookVis({}), + storybookVis({ + // 3px of difference allowed before marking as failed + failureThreshold: 3, + }), ], test: { name: "storybook", diff --git a/playwright/testcontainers/synapse.ts b/playwright/testcontainers/synapse.ts index 574f6e6cdc..059ef00c6e 100644 --- a/playwright/testcontainers/synapse.ts +++ b/playwright/testcontainers/synapse.ts @@ -7,7 +7,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 TAG = "develop@sha256:9abab158a1cd7af619d5889fc8c83496e569e647068a8b856f4ad05e8cf342e8"; +const TAG = "develop@sha256:4620e446582e79a3942f5438ebf714da18c281143496e53be318334b4697b449"; /** * SynapseContainer which freezes the docker digest to stabilise tests, diff --git a/res/css/_components.pcss b/res/css/_components.pcss index a47fc0ac82..28458c899f 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -246,7 +246,6 @@ @import "./views/messages/_RedactedBody.pcss"; @import "./views/messages/_RoomAvatarEvent.pcss"; @import "./views/messages/_TextualEvent.pcss"; -@import "./views/messages/_TimelineSeparator.pcss"; @import "./views/messages/_UnknownBody.pcss"; @import "./views/messages/_ViewSourceEvent.pcss"; @import "./views/messages/_common_CryptoEvent.pcss"; diff --git a/res/css/views/messages/_TimelineSeparator.pcss b/res/css/views/messages/_TimelineSeparator.pcss deleted file mode 100644 index aab77d4e03..0000000000 --- a/res/css/views/messages/_TimelineSeparator.pcss +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2017 Vector 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. -*/ - -.mx_TimelineSeparator { - clear: both; - margin: 4px 0; - display: flex; - align-items: center; - font: var(--cpd-font-body-md-regular); - color: var(--cpd-color-text-primary); -} - -.mx_TimelineSeparator > hr { - flex: 1 1 0; - height: 0; - border: none; - border-bottom: 1px solid var(--cpd-color-gray-400); -} diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 410f40add8..e9c723b75d 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -43,7 +43,6 @@ import SdkConfig from "./SdkConfig"; import PlatformPeg from "./PlatformPeg"; import { recordClientInformation, removeClientInformation } from "./utils/device/clientInformation"; import SettingsStore, { type CallbackFn } from "./settings/SettingsStore"; -import { UIFeature } from "./settings/UIFeature"; import { isBulkUnverifiedDeviceReminderSnoozed } from "./utils/device/snoozeBulkUnverifiedDeviceReminder"; import { getUserDeviceIds } from "./utils/crypto/deviceInfo"; import { asyncSomeParallel } from "./utils/arrays.ts"; @@ -125,7 +124,6 @@ export default class DeviceListener extends TypedEventEmitter 0 && - isCurrentDeviceTrusted && - this.enableBulkUnverifiedSessionsReminder && - !isBulkUnverifiedSessionsReminderSnoozed - ) { + if (oldUnverifiedDeviceIds.size > 0 && isCurrentDeviceTrusted && !isBulkUnverifiedSessionsReminderSnoozed) { showBulkUnverifiedSessionsToast(oldUnverifiedDeviceIds); } else { hideBulkUnverifiedSessionsToast(); diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 30f9b474a6..40b69130a7 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -18,6 +18,7 @@ import { } from "matrix-js-sdk/src/matrix"; import { logger } from "matrix-js-sdk/src/logger"; import { isSupportedReceiptType } from "matrix-js-sdk/src/utils"; +import { TimelineSeparator } from "@element-hq/web-shared-components"; import shouldHideEvent from "../../shouldHideEvent"; import { formatDate, wantsDateSeparator } from "../../DateUtils"; @@ -37,7 +38,6 @@ import type LegacyCallEventGrouper from "./LegacyCallEventGrouper"; import WhoIsTypingTile from "../views/rooms/WhoIsTypingTile"; import ScrollPanel, { type IScrollState } from "./ScrollPanel"; import DateSeparator from "../views/messages/DateSeparator"; -import TimelineSeparator, { SeparatorKind } from "../views/messages/TimelineSeparator"; import ErrorBoundary from "../views/elements/ErrorBoundary"; import Spinner from "../views/elements/Spinner"; import { type RoomPermalinkCreator } from "../../utils/permalinks/Permalinks"; @@ -57,6 +57,18 @@ import { getLateEventInfo } from "./grouper/LateEventGrouper"; const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes const continuedTypes = [EventType.Sticker, EventType.RoomMessage]; +/** + * Indicates which separator (if any) should be rendered between timeline events. + */ +export const enum SeparatorKind { + /** No separator should be shown between the two events. */ + None, + /** Insert a date separator (oriented by event date boundaries). */ + Date, + /** Insert a late-event separator when events belong to different late groups. */ + LateEvent, +} + // check if there is a previous event and it has the same sender as this event // and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL export function shouldFormContinuation( @@ -756,7 +768,7 @@ export default class MessagePanel extends React.Component { }); ret.push(
  • - + {text}
  • , diff --git a/src/components/structures/grouper/CreationGrouper.tsx b/src/components/structures/grouper/CreationGrouper.tsx index 009f5bdc26..80c9bfedcd 100644 --- a/src/components/structures/grouper/CreationGrouper.tsx +++ b/src/components/structures/grouper/CreationGrouper.tsx @@ -11,14 +11,13 @@ import { EventType, M_BEACON_INFO, type MatrixEvent } from "matrix-js-sdk/src/ma import { KnownMembership } from "matrix-js-sdk/src/types"; import { BaseGrouper } from "./BaseGrouper"; -import { type WrappedEvent } from "../MessagePanel"; +import { SeparatorKind, type WrappedEvent } from "../MessagePanel"; import type MessagePanel from "../MessagePanel"; import DMRoomMap from "../../../utils/DMRoomMap"; import { _t } from "../../../languageHandler"; import DateSeparator from "../../views/messages/DateSeparator"; import NewRoomIntro from "../../views/rooms/NewRoomIntro"; import GenericEventListSummary from "../../views/elements/GenericEventListSummary"; -import { SeparatorKind } from "../../views/messages/TimelineSeparator"; // Wrap initial room creation events into a GenericEventListSummary // Grouping only events sent by the same user that sent the `m.room.create` and only until diff --git a/src/components/structures/grouper/MainGrouper.tsx b/src/components/structures/grouper/MainGrouper.tsx index e686f1aa81..6766f0e5b7 100644 --- a/src/components/structures/grouper/MainGrouper.tsx +++ b/src/components/structures/grouper/MainGrouper.tsx @@ -10,14 +10,13 @@ import React, { type ReactNode } from "react"; import { EventType, type MatrixEvent } from "matrix-js-sdk/src/matrix"; import type MessagePanel from "../MessagePanel"; -import type { WrappedEvent } from "../MessagePanel"; +import { SeparatorKind, type WrappedEvent } from "../MessagePanel"; import { BaseGrouper } from "./BaseGrouper"; import { hasText } from "../../../TextForEvent"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import DateSeparator from "../../views/messages/DateSeparator"; import HistoryTile from "../../views/rooms/HistoryTile"; import EventListSummary from "../../views/elements/EventListSummary"; -import { SeparatorKind } from "../../views/messages/TimelineSeparator"; const groupedStateEvents = [ EventType.RoomMember, diff --git a/src/components/views/messages/DateSeparator.tsx b/src/components/views/messages/DateSeparator.tsx index 7d49042533..061cc76204 100644 --- a/src/components/views/messages/DateSeparator.tsx +++ b/src/components/views/messages/DateSeparator.tsx @@ -12,6 +12,7 @@ import { Direction, ConnectionError, MatrixError, HTTPError } from "matrix-js-sd import { logger } from "matrix-js-sdk/src/logger"; import { capitalize } from "lodash"; import { ChevronDownIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; +import { TimelineSeparator } from "@element-hq/web-shared-components"; import { _t, getUserLanguage } from "../../../languageHandler"; import { formatFullDateNoDay, formatFullDateNoTime, getDaysArray } from "../../../DateUtils"; @@ -32,7 +33,6 @@ import IconizedContextMenu, { } from "../context_menus/IconizedContextMenu"; import JumpToDatePicker from "./JumpToDatePicker"; import { type ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload"; -import TimelineSeparator from "./TimelineSeparator"; import RoomContext from "../../../contexts/RoomContext"; interface IProps { @@ -335,6 +335,10 @@ export default class DateSeparator extends React.Component { ); } - return {dateHeaderContent}; + return ( + + {dateHeaderContent} + + ); } } diff --git a/src/components/views/messages/TimelineSeparator.tsx b/src/components/views/messages/TimelineSeparator.tsx deleted file mode 100644 index 4735c8e00c..0000000000 --- a/src/components/views/messages/TimelineSeparator.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. - -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, { type ReactNode } from "react"; - -interface Props { - label: string; - children?: ReactNode; -} - -export const enum SeparatorKind { - None, - Date, - LateEvent, -} - -/** - * Generic timeline separator component to render within a MessagePanel - * - * @param label the accessible label string describing the separator - * @param children the children to draw within the timeline separator - */ -const TimelineSeparator: React.FC = ({ label, children }) => { - // ARIA treats
    s as separators, here we abuse them slightly so manually treat this entire thing as one - return ( -
    -
    - {children} -
    -
    - ); -}; - -export default TimelineSeparator; diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 8c57e234ae..b871152d8f 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -1437,10 +1437,6 @@ export const SETTINGS: Settings = { supportedLevels: LEVELS_UI_FEATURE, default: true, }, - [UIFeature.BulkUnverifiedSessionsReminder]: { - supportedLevels: LEVELS_UI_FEATURE, - default: true, - }, [UIFeature.AllowCreatingPublicSpaces]: { supportedLevels: LEVELS_UI_FEATURE, default: true, diff --git a/src/settings/UIFeature.ts b/src/settings/UIFeature.ts index 12b0c7c089..0215b79175 100644 --- a/src/settings/UIFeature.ts +++ b/src/settings/UIFeature.ts @@ -24,7 +24,6 @@ export const enum UIFeature { AdvancedSettings = "UIFeature.advancedSettings", RoomHistorySettings = "UIFeature.roomHistorySettings", TimelineEnableRelativeDates = "UIFeature.timelineEnableRelativeDates", - BulkUnverifiedSessionsReminder = "UIFeature.BulkUnverifiedSessionsReminder", AllowCreatingPublicRooms = "UIFeature.allowCreatingPublicRooms", AllowCreatingPublicSpaces = "UIFeature.allowCreatingPublicSpaces", } diff --git a/src/utils/permalinks/MatrixToPermalinkConstructor.ts b/src/utils/permalinks/MatrixToPermalinkConstructor.ts index 77b4830e19..d7cbc5e927 100644 --- a/src/utils/permalinks/MatrixToPermalinkConstructor.ts +++ b/src/utils/permalinks/MatrixToPermalinkConstructor.ts @@ -63,7 +63,7 @@ export default class MatrixToPermalinkConstructor extends PermalinkConstructor { const entity = parts[0]; if (entity[0] === "@") { // Probably a user, no further parsing needed. - return PermalinkParts.forUser(entity); + return PermalinkParts.forUser(matches[1]); } else if (entity[0] === "#" || entity[0] === "!") { if (parts.length === 1) { // room without event permalink diff --git a/test/unit-tests/DeviceListener-test.ts b/test/unit-tests/DeviceListener-test.ts index 4810d82001..f9339cd38b 100644 --- a/test/unit-tests/DeviceListener-test.ts +++ b/test/unit-tests/DeviceListener-test.ts @@ -35,7 +35,6 @@ import { Action } from "../../src/dispatcher/actions"; import SettingsStore from "../../src/settings/SettingsStore"; import { SettingLevel } from "../../src/settings/SettingLevel"; import { getMockClientWithEventEmitter, mockPlatformPeg } from "../test-utils"; -import { UIFeature } from "../../src/settings/UIFeature"; import { isBulkUnverifiedDeviceReminderSnoozed } from "../../src/utils/device/snoozeBulkUnverifiedDeviceReminder"; import { PosthogAnalytics } from "../../src/PosthogAnalytics"; @@ -653,10 +652,8 @@ describe("DeviceListener", () => { // all devices verified by default mockCrypto!.getDeviceVerificationStatus.mockResolvedValue(deviceTrustVerified); mockClient!.deviceId = currentDevice.deviceId; - jest.spyOn(SettingsStore, "getValue").mockImplementation( - (settingName) => settingName === UIFeature.BulkUnverifiedSessionsReminder, - ); }); + describe("bulk unverified sessions toasts", () => { it("hides toast when cross signing is not ready", async () => { mockCrypto!.isCrossSigningReady.mockResolvedValue(false); @@ -671,24 +668,6 @@ describe("DeviceListener", () => { expect(BulkUnverifiedSessionsToast.showToast).not.toHaveBeenCalled(); }); - it("hides toast when feature is disabled", async () => { - // BulkUnverifiedSessionsReminder set to false - jest.spyOn(SettingsStore, "getValue").mockReturnValue(false); - // currentDevice, device2 are verified, device3 is unverified - // ie if reminder was enabled it should be shown - mockCrypto!.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => { - switch (deviceId) { - case currentDevice.deviceId: - case device2.deviceId: - return deviceTrustVerified; - default: - return deviceTrustUnverified; - } - }); - await createAndStart(); - expect(BulkUnverifiedSessionsToast.hideToast).toHaveBeenCalled(); - }); - it("hides toast when current device is unverified", async () => { // device2 verified, current and device3 unverified mockCrypto!.getDeviceVerificationStatus.mockImplementation(async (_userId, deviceId) => { diff --git a/test/unit-tests/SupportedBrowser-test.ts b/test/unit-tests/SupportedBrowser-test.ts index 3607b35a08..bd5cd9355b 100644 --- a/test/unit-tests/SupportedBrowser-test.ts +++ b/test/unit-tests/SupportedBrowser-test.ts @@ -66,17 +66,17 @@ describe("SupportedBrowser", () => { // Safari 26.0 on macOS "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15", // Latest Firefox on macOS Sonoma - "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:145.0) Gecko/20100101 Firefox/145.0", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 15.7; rv:145.0) Gecko/20100101 Firefox/147.0", // Latest Edge on Windows - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.3595.76", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.3595.76", // Latest Edge on macOS - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36 Edg/142.0.3595.76", + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Edg/144.0.3595.76", // Latest Firefox on Windows - "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:145.0) Gecko/20100101 Firefox/145.0", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0", // Latest Firefox on Linux - "Mozilla/5.0 (X11; Linux i686; rv:145.0) Gecko/20100101 Firefox/145.0", + "Mozilla/5.0 (X11; Linux i686; rv:147.0) Gecko/20100101 Firefox/147.0", // Latest Chrome on Windows - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36", + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36", ])("should not warn for supported browsers", testUserAgentFactory()); it.each([ diff --git a/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap b/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap index 716b0e7257..f31612ae13 100644 --- a/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap +++ b/test/unit-tests/components/structures/__snapshots__/MessagePanel-test.tsx.snap @@ -39,8 +39,9 @@ exports[`MessagePanel should handle lots of membership events quickly 1`] = ` > diff --git a/yarn.lock b/yarn.lock index ce8716d50a..1abcd7335d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1727,8 +1727,18 @@ yaml "^2.7.0" "@element-hq/web-shared-components@link:packages/shared-components": - version "0.0.0" - uid "" + version "0.0.1" + dependencies: + "@element-hq/element-web-module-api" "^1.8.0" + "@matrix-org/spec" "^1.7.0" + "@vector-im/compound-design-tokens" "^6.4.3" + classnames "^2.5.1" + counterpart "^0.18.6" + lodash "^4.17.21" + matrix-web-i18n "3.6.0" + react-merge-refs "^3.0.2" + react-virtuoso "^4.14.0" + temporal-polyfill "^0.3.0" "@emnapi/core@^1.4.3": version "1.7.0" @@ -4500,7 +4510,6 @@ "@vector-im/matrix-wysiwyg-wasm@link:../../Library/Caches/Yarn/v6/npm-@vector-im-matrix-wysiwyg-2.40.0-53c9ca5ea907d91e4515da64f20a82e5586b882c-integrity/node_modules/bindings/wysiwyg-wasm": version "0.0.0" - uid "" "@vector-im/matrix-wysiwyg@2.40.0": version "2.40.0" @@ -5564,10 +5573,10 @@ caniuse-api@^3.0.0: lodash.memoize "^4.1.2" lodash.uniq "^4.5.0" -caniuse-lite@1.0.30001764, caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001760: - version "1.0.30001764" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz#03206c56469f236103b90f9ae10bcb8b9e1f6005" - integrity sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g== +caniuse-lite@1.0.30001766, caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001759, caniuse-lite@^1.0.30001760: + version "1.0.30001766" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz#b6f6b55cb25a2d888d9393104d14751c6a7d6f7a" + integrity sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA== chalk@4.1.2, chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" @@ -9802,9 +9811,9 @@ matrix-web-i18n@3.6.0: walk "^2.3.15" matrix-widget-api@^1.16.1: - version "1.16.1" - resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.16.1.tgz#a447f28f0af07e1bdc960881971de7d1ec9e6464" - integrity sha512-oCfTV4xNPo02qIgveqdkIyKQjOPpsjhF3bmJBotHrhr8TsrhVa7kx8PtuiUPnQTjz0tdBle7falR2Fw8VKsedw== + version "1.17.0" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.17.0.tgz#2336de2186fe70d8bd741c1603c162f60b2099c2" + integrity sha512-5FHoo3iEP3Bdlv5jsYPWOqj+pGdFQNLWnJLiB0V7Ygne7bb+Gsj3ibyFyHWC6BVw+Z+tSW4ljHpO17I9TwStwQ== dependencies: "@types/events" "^3.0.0" events "^3.2.0"