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 0000000000..dfcada0746 Binary files /dev/null and b/packages/shared-components/__vis__/linux/__baselines__/status/StatusTextView.stories.tsx/default-auto.png differ 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} + +
+ ); +});