From f09b27db7cfdbf51172508618f5aaf4a8e9d29ca Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 10 Jul 2026 15:45:41 +0100 Subject: [PATCH] Add user status in user info right panel card (#34212) * Add user status in user info right panel card Adds a very simple view component to do so, which will be reused in the DM room header. Also comment the other status view so the difference is clear. * Add screenshot * Add test * Rename snapshot --- .../user_info/UserInfoHeaderView.tsx | 5 ++- .../default-auto.png | Bin 0 -> 18693 bytes packages/shared-components/src/index.ts | 1 + .../src/status/StatusPillView.tsx | 4 +++ .../src/status/StatusTextView.module.css | 23 ++++++++++++ .../src/status/StatusTextView.stories.tsx | 24 +++++++++++++ .../src/status/StatusTextView.test.tsx | 23 ++++++++++++ .../src/status/StatusTextView.tsx | 33 ++++++++++++++++++ 8 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 packages/shared-components/__vis__/linux/__baselines__/status/StatusTextView.stories.tsx/default-auto.png create mode 100644 packages/shared-components/src/status/StatusTextView.module.css create mode 100644 packages/shared-components/src/status/StatusTextView.stories.tsx create mode 100644 packages/shared-components/src/status/StatusTextView.test.tsx create mode 100644 packages/shared-components/src/status/StatusTextView.tsx diff --git a/apps/web/src/components/views/right_panel/user_info/UserInfoHeaderView.tsx b/apps/web/src/components/views/right_panel/user_info/UserInfoHeaderView.tsx index 275175198c..1e214223c2 100644 --- a/apps/web/src/components/views/right_panel/user_info/UserInfoHeaderView.tsx +++ b/apps/web/src/components/views/right_panel/user_info/UserInfoHeaderView.tsx @@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details. import React, { type JSX } from "react"; import { type User, type RoomMember } from "matrix-js-sdk/src/matrix"; import { Heading, Tooltip, Text } from "@vector-im/compound-web"; -import { Flex } from "@element-hq/web-shared-components"; +import { Flex, StatusTextView } from "@element-hq/web-shared-components"; import { useUserfoHeaderViewModel } from "../../../viewmodels/right_panel/user_info/UserInfoHeaderViewModel"; import MemberAvatar from "../../avatars/MemberAvatar"; @@ -16,6 +16,7 @@ import { Container, type Member, type IDevice } from "../UserInfo"; import PresenceLabel from "../../rooms/PresenceLabel"; import CopyableText from "../../elements/CopyableText"; import { UserInfoHeaderVerificationView } from "./UserInfoHeaderVerificationView"; +import { useUserStatus } from "../../../../hooks/useUserStatus"; export interface UserInfoHeaderViewProps { member: Member; @@ -33,6 +34,7 @@ export const UserInfoHeaderView: React.FC = ({ const vm = useUserfoHeaderViewModel({ member, roomId }); const avatarUrl = (member as User).avatarUrl; const displayName = (member as RoomMember).rawDisplayName; + const userStatus = useUserStatus(member.userId); let presenceLabel: JSX.Element | undefined; @@ -73,6 +75,7 @@ export const UserInfoHeaderView: React.FC = ({ {displayName} + {userStatus && } {presenceLabel} {vm.timezoneInfo && ( diff --git a/packages/shared-components/__vis__/linux/__baselines__/status/StatusTextView.stories.tsx/default-auto.png b/packages/shared-components/__vis__/linux/__baselines__/status/StatusTextView.stories.tsx/default-auto.png new file mode 100644 index 0000000000000000000000000000000000000000..dfcada0746bf7a3039734799f75a0adb9e8d4140 GIT binary patch literal 18693 zcmZ8pc_7r=7yr)J$e z>GF7o1#3$8lnqZ))iyj|th#dUciGh|4lVRE4?X;75%=@L0nwdsDTE_ zrq`U0(oD=;{!X?$tn;$HLd+MjW(&@x_5nE?xt8D z@nib=pt@(*b7y1)e(dXtF3j${|IViMoV{6COH+-^bN?CjRRdy~ngXrf+P_{tHqO+q z+TEL&*qY;AxXj@Lw0Ldk?+JEμqMz2E)D>nj)~^_^2^(mgf6?t~ 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 literal 0 HcmV?d00001 diff --git a/packages/shared-components/src/index.ts b/packages/shared-components/src/index.ts index 41ef3a54fb..aa638071d8 100644 --- a/packages/shared-components/src/index.ts +++ b/packages/shared-components/src/index.ts @@ -92,5 +92,6 @@ export * from "./core/i18n/I18nApi"; export * from "./core/utils/linkify"; export type * from "./core/userStatus.ts"; export * from "./status/SetStatusView"; +export * from "./status/StatusTextView"; // MVVM export * from "./core/viewmodel"; diff --git a/packages/shared-components/src/status/StatusPillView.tsx b/packages/shared-components/src/status/StatusPillView.tsx index c76bc15f4d..2752a0c77f 100644 --- a/packages/shared-components/src/status/StatusPillView.tsx +++ b/packages/shared-components/src/status/StatusPillView.tsx @@ -12,6 +12,10 @@ import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; import { _t, type UserStatus } from ".."; import styles from "./StatusPillView.module.css"; +/** + * Displays a user's status message in a pill format with a button that can be used + * to clear the status. + */ export const StatusPillView = React.forwardRef< HTMLDivElement, { diff --git a/packages/shared-components/src/status/StatusTextView.module.css b/packages/shared-components/src/status/StatusTextView.module.css new file mode 100644 index 0000000000..2437172197 --- /dev/null +++ b/packages/shared-components/src/status/StatusTextView.module.css @@ -0,0 +1,23 @@ +/* + * 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. + */ + +.statusText { + display: inline-flex; + align-items: center; + gap: var(--cpd-space-1x); + + span { + font: var(--cpd-font-body-md-medium); + color: var(--cpd-color-text-secondary); + } + + .menuStatusText { + overflow: hidden; + min-width: 0; + text-overflow: ellipsis; + } +} diff --git a/packages/shared-components/src/status/StatusTextView.stories.tsx b/packages/shared-components/src/status/StatusTextView.stories.tsx new file mode 100644 index 0000000000..c6613b83b6 --- /dev/null +++ b/packages/shared-components/src/status/StatusTextView.stories.tsx @@ -0,0 +1,24 @@ +/* + * 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 Meta, type StoryObj } from "@storybook/react-vite"; + +import { StatusTextView } from "./StatusTextView"; + +const meta = { + title: "Status/StatusTextView", + component: StatusTextView, + tags: ["autodocs"], + args: { + status: { emoji: "🦩", text: "Flamboyant" }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = {}; diff --git a/packages/shared-components/src/status/StatusTextView.test.tsx b/packages/shared-components/src/status/StatusTextView.test.tsx new file mode 100644 index 0000000000..b3d380fcaf --- /dev/null +++ b/packages/shared-components/src/status/StatusTextView.test.tsx @@ -0,0 +1,23 @@ +/* + * 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 React from "react"; +import { describe, expect, it } from "vitest"; +import { render, screen } from "@test-utils"; + +import * as stories from "./StatusTextView.stories"; + +const { Default } = composeStories(stories); + +describe("StatusTextView", () => { + it("should display the status text and emoji", () => { + render(); + expect(screen.getByText("Flamboyant")).toBeInTheDocument(); + expect(screen.getByText("🦩")).toBeInTheDocument(); + }); +}); diff --git a/packages/shared-components/src/status/StatusTextView.tsx b/packages/shared-components/src/status/StatusTextView.tsx new file mode 100644 index 0000000000..7ec3937d58 --- /dev/null +++ b/packages/shared-components/src/status/StatusTextView.tsx @@ -0,0 +1,33 @@ +/* + * 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 JSX } from "react"; +import { Text } from "@vector-im/compound-web"; + +import { type UserStatus } from ".."; +import styles from "./StatusTextView.module.css"; + +/** + * Displays a user's status message and emoji in simple text format + */ +export const StatusTextView = React.forwardRef< + HTMLDivElement, + { + status: UserStatus; + } & React.HTMLAttributes +>(function StatusTextView({ status, ...props }, ref): JSX.Element { + return ( +
+ + {status.emoji} + + + {status.text} + +
+ ); +});