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
This commit is contained in:
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import React, { type JSX } from "react";
|
import React, { type JSX } from "react";
|
||||||
import { type User, type RoomMember } from "matrix-js-sdk/src/matrix";
|
import { type User, type RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import { Heading, Tooltip, Text } from "@vector-im/compound-web";
|
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 { useUserfoHeaderViewModel } from "../../../viewmodels/right_panel/user_info/UserInfoHeaderViewModel";
|
||||||
import MemberAvatar from "../../avatars/MemberAvatar";
|
import MemberAvatar from "../../avatars/MemberAvatar";
|
||||||
@@ -16,6 +16,7 @@ import { Container, type Member, type IDevice } from "../UserInfo";
|
|||||||
import PresenceLabel from "../../rooms/PresenceLabel";
|
import PresenceLabel from "../../rooms/PresenceLabel";
|
||||||
import CopyableText from "../../elements/CopyableText";
|
import CopyableText from "../../elements/CopyableText";
|
||||||
import { UserInfoHeaderVerificationView } from "./UserInfoHeaderVerificationView";
|
import { UserInfoHeaderVerificationView } from "./UserInfoHeaderVerificationView";
|
||||||
|
import { useUserStatus } from "../../../../hooks/useUserStatus";
|
||||||
|
|
||||||
export interface UserInfoHeaderViewProps {
|
export interface UserInfoHeaderViewProps {
|
||||||
member: Member;
|
member: Member;
|
||||||
@@ -33,6 +34,7 @@ export const UserInfoHeaderView: React.FC<UserInfoHeaderViewProps> = ({
|
|||||||
const vm = useUserfoHeaderViewModel({ member, roomId });
|
const vm = useUserfoHeaderViewModel({ member, roomId });
|
||||||
const avatarUrl = (member as User).avatarUrl;
|
const avatarUrl = (member as User).avatarUrl;
|
||||||
const displayName = (member as RoomMember).rawDisplayName;
|
const displayName = (member as RoomMember).rawDisplayName;
|
||||||
|
const userStatus = useUserStatus(member.userId);
|
||||||
|
|
||||||
let presenceLabel: JSX.Element | undefined;
|
let presenceLabel: JSX.Element | undefined;
|
||||||
|
|
||||||
@@ -73,6 +75,7 @@ export const UserInfoHeaderView: React.FC<UserInfoHeaderViewProps> = ({
|
|||||||
{displayName}
|
{displayName}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Heading>
|
</Heading>
|
||||||
|
{userStatus && <StatusTextView status={userStatus} />}
|
||||||
{presenceLabel}
|
{presenceLabel}
|
||||||
{vm.timezoneInfo && (
|
{vm.timezoneInfo && (
|
||||||
<Tooltip label={vm.timezoneInfo?.timezone ?? ""}>
|
<Tooltip label={vm.timezoneInfo?.timezone ?? ""}>
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -92,5 +92,6 @@ export * from "./core/i18n/I18nApi";
|
|||||||
export * from "./core/utils/linkify";
|
export * from "./core/utils/linkify";
|
||||||
export type * from "./core/userStatus.ts";
|
export type * from "./core/userStatus.ts";
|
||||||
export * from "./status/SetStatusView";
|
export * from "./status/SetStatusView";
|
||||||
|
export * from "./status/StatusTextView";
|
||||||
// MVVM
|
// MVVM
|
||||||
export * from "./core/viewmodel";
|
export * from "./core/viewmodel";
|
||||||
|
|||||||
@@ -12,6 +12,10 @@ import { CloseIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
|||||||
import { _t, type UserStatus } from "..";
|
import { _t, type UserStatus } from "..";
|
||||||
import styles from "./StatusPillView.module.css";
|
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<
|
export const StatusPillView = React.forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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<typeof StatusTextView>;
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof meta>;
|
||||||
|
|
||||||
|
export const Default: Story = {};
|
||||||
@@ -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(<Default />);
|
||||||
|
expect(screen.getByText("Flamboyant")).toBeInTheDocument();
|
||||||
|
expect(screen.getByText("🦩")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -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<HTMLDivElement>
|
||||||
|
>(function StatusTextView({ status, ...props }, ref): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div ref={ref} {...props} className={styles.statusText}>
|
||||||
|
<Text as="span" className={styles.menuStatusEmoji}>
|
||||||
|
{status.emoji}
|
||||||
|
</Text>
|
||||||
|
<Text as="span" className={styles.menuStatusText}>
|
||||||
|
{status.text}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user