diff --git a/playwright/snapshots/crypto/crypto.spec.ts/RoomSummaryCard-with-verified-e2ee-linux.png b/playwright/snapshots/crypto/crypto.spec.ts/RoomSummaryCard-with-verified-e2ee-linux.png
index d3c8961391..98c1ff245d 100644
Binary files a/playwright/snapshots/crypto/crypto.spec.ts/RoomSummaryCard-with-verified-e2ee-linux.png and b/playwright/snapshots/crypto/crypto.spec.ts/RoomSummaryCard-with-verified-e2ee-linux.png differ
diff --git a/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-with-user-pill-linux.png b/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-with-user-pill-linux.png
index 9cc13698cf..c8b8dba45b 100644
Binary files a/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-with-user-pill-linux.png and b/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-with-user-pill-linux.png differ
diff --git a/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-without-user-linux.png b/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-without-user-linux.png
index 44d8129404..852cb85518 100644
Binary files a/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-without-user-linux.png and b/playwright/snapshots/invite/invite-dialog.spec.ts/invite-dialog-room-without-user-linux.png differ
diff --git a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png
index 2fc33b1f0b..c85c583a19 100644
Binary files a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png and b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-bubble-layout-linux.png differ
diff --git a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png
index a4c053e7a7..b6990e727e 100644
Binary files a/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png and b/playwright/snapshots/polls/polls.spec.ts/ThreadView-with-a-poll-on-group-layout-linux.png differ
diff --git a/playwright/snapshots/right-panel/file-panel.spec.ts/file-tiles-list-linux.png b/playwright/snapshots/right-panel/file-panel.spec.ts/file-tiles-list-linux.png
index ad49c25abc..b783826727 100644
Binary files a/playwright/snapshots/right-panel/file-panel.spec.ts/file-tiles-list-linux.png and b/playwright/snapshots/right-panel/file-panel.spec.ts/file-tiles-list-linux.png differ
diff --git a/playwright/snapshots/right-panel/right-panel.spec.ts/with-name-and-address-linux.png b/playwright/snapshots/right-panel/right-panel.spec.ts/with-name-and-address-linux.png
index e53470df87..f383a828e2 100644
Binary files a/playwright/snapshots/right-panel/right-panel.spec.ts/with-name-and-address-linux.png and b/playwright/snapshots/right-panel/right-panel.spec.ts/with-name-and-address-linux.png differ
diff --git a/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png b/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png
index 7ef5b40543..9fc79671a1 100644
Binary files a/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png and b/playwright/snapshots/threads/threads.spec.ts/Reply-to-the-location-on-ThreadView-linux.png differ
diff --git a/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png b/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png
index 9d9b431b0c..61ab660157 100644
Binary files a/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png and b/playwright/snapshots/user-view/user-view.spec.ts/user-info-linux.png differ
diff --git a/res/css/views/right_panel/_BaseCard.pcss b/res/css/views/right_panel/_BaseCard.pcss
index 6d17930fce..67eb9b7e49 100644
--- a/res/css/views/right_panel/_BaseCard.pcss
+++ b/res/css/views/right_panel/_BaseCard.pcss
@@ -90,6 +90,7 @@ limitations under the License.
min-height: 0;
width: 100%;
height: 100%;
+ scrollbar-gutter: stable;
}
.mx_BaseCard_Group {
diff --git a/res/css/views/right_panel/_RoomSummaryCard.pcss b/res/css/views/right_panel/_RoomSummaryCard.pcss
index 72b23d860e..4c3ff2f888 100644
--- a/res/css/views/right_panel/_RoomSummaryCard.pcss
+++ b/res/css/views/right_panel/_RoomSummaryCard.pcss
@@ -51,6 +51,52 @@ limitations under the License.
}
}
+ .mx_RoomSummaryCard_topic {
+ padding: 0 12px;
+
+ .mx_Box {
+ width: 100%;
+ }
+
+ .mx_RoomSummaryCard_topic_container {
+ display: flex;
+ }
+
+ .mx_RoomSummaryCard_topic_edit {
+ width: max-content;
+ }
+
+ p {
+ white-space: pre-wrap;
+ width: 100%;
+ min-width: 0;
+ margin: 0;
+ }
+
+ a {
+ cursor: pointer;
+ }
+
+ .mx_RoomSummaryCard_topic_chevron {
+ transition: transform 0.3s;
+ }
+
+ &.mx_RoomSummaryCard_topic_collapsed {
+ p {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: normal;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 2;
+ }
+
+ .mx_RoomSummaryCard_topic_chevron {
+ transform: rotate(-90deg);
+ }
+ }
+ }
+
.mx_RoomSummaryCard_appsGroup {
.mx_RoomSummaryCard_Button {
/* this button is special so we have to override some of the original styling */
diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx
index e7d6ce2890..b63ed1dcf0 100644
--- a/src/HtmlUtils.tsx
+++ b/src/HtmlUtils.tsx
@@ -461,9 +461,13 @@ export function topicToHtml(
emojiBodyElements = formatEmojis(topic, false);
}
- return isFormattedTopic ? (
-
- ) : (
+ if (isFormattedTopic) {
+ if (!safeTopic) return null;
+ return ;
+ }
+
+ if (!emojiBodyElements && !topic) return null;
+ return (
{emojiBodyElements || topic}
diff --git a/src/components/views/elements/RoomTopic.tsx b/src/components/views/elements/RoomTopic.tsx
index fa9fc0fd34..f926ef5cf4 100644
--- a/src/components/views/elements/RoomTopic.tsx
+++ b/src/components/views/elements/RoomTopic.tsx
@@ -36,6 +36,17 @@ interface IProps extends React.HTMLProps {
room: Room;
}
+export function onRoomTopicLinkClick(e: React.MouseEvent): void {
+ const anchor = e.target as HTMLLinkElement;
+ const localHref = tryTransformPermalinkToLocalHref(anchor.href);
+
+ if (localHref !== anchor.href) {
+ // it could be converted to a localHref -> therefore handle locally
+ e.preventDefault();
+ window.location.hash = localHref;
+ }
+}
+
export default function RoomTopic({ room, className, ...props }: IProps): JSX.Element {
const client = useContext(MatrixClientContext);
const ref = useRef(null);
@@ -54,14 +65,7 @@ export default function RoomTopic({ room, className, ...props }: IProps): JSX.El
return;
}
- const anchor = e.target as HTMLLinkElement;
- const localHref = tryTransformPermalinkToLocalHref(anchor.href);
-
- if (localHref !== anchor.href) {
- // it could be converted to a localHref -> therefore handle locally
- e.preventDefault();
- window.location.hash = localHref;
- }
+ onRoomTopicLinkClick(e);
},
[props],
);
diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx
index 13f77eff14..b259d5e3ad 100644
--- a/src/components/views/right_panel/RoomSummaryCard.tsx
+++ b/src/components/views/right_panel/RoomSummaryCard.tsx
@@ -14,9 +14,19 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-import React, { useCallback, useContext, useEffect, useMemo, useState } from "react";
+import React, { SyntheticEvent, useCallback, useContext, useEffect, useMemo, useState } from "react";
import classNames from "classnames";
-import { MenuItem, Tooltip, Separator, ToggleMenuItem, Text, Badge, Heading } from "@vector-im/compound-web";
+import {
+ MenuItem,
+ Tooltip,
+ Separator,
+ ToggleMenuItem,
+ Text,
+ Badge,
+ Heading,
+ IconButton,
+ Link,
+} from "@vector-im/compound-web";
import { Icon as SearchIcon } from "@vector-im/compound-design-tokens/icons/search.svg";
import { Icon as FavouriteIcon } from "@vector-im/compound-design-tokens/icons/favourite.svg";
import { Icon as UserAddIcon } from "@vector-im/compound-design-tokens/icons/user-add.svg";
@@ -32,6 +42,7 @@ import { Icon as LockIcon } from "@vector-im/compound-design-tokens/icons/lock-s
import { Icon as LockOffIcon } from "@vector-im/compound-design-tokens/icons/lock-off.svg";
import { Icon as PublicIcon } from "@vector-im/compound-design-tokens/icons/public.svg";
import { Icon as ErrorIcon } from "@vector-im/compound-design-tokens/icons/error.svg";
+import { Icon as ChevronDownIcon } from "@vector-im/compound-design-tokens/icons/chevron-down.svg";
import { EventType, JoinRule, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
@@ -74,6 +85,10 @@ import { canInviteTo } from "../../../utils/room/canInviteTo";
import { inviteToRoom } from "../../../utils/room/inviteToRoom";
import { useAccountData } from "../../../hooks/useAccountData";
import { useRoomState } from "../../../hooks/useRoomState";
+import { useTopic } from "../../../hooks/room/useTopic";
+import { Linkify, topicToHtml } from "../../../HtmlUtils";
+import { Box } from "../../utils/Box";
+import { onRoomTopicLinkClick } from "../elements/RoomTopic";
interface IProps {
room: Room;
@@ -271,6 +286,84 @@ const onRoomSettingsClick = (ev: Event): void => {
PosthogTrackers.trackInteraction("WebRightPanelRoomInfoSettingsButton", ev);
};
+const RoomTopic: React.FC> = ({ room }): JSX.Element | null => {
+ const [expanded, setExpanded] = useState(false);
+
+ const topic = useTopic(room);
+ const body = topicToHtml(topic?.text, topic?.html);
+
+ const onEditClick = (e: SyntheticEvent): void => {
+ e.preventDefault();
+ e.stopPropagation();
+ defaultDispatcher.dispatch({ action: "open_room_settings" });
+ };
+
+ if (!body) {
+ return (
+
+
+
+
+ {_t("right_panel|add_topic")}
+
+
+
+
+ );
+ }
+
+ const content = expanded ? {body} : body;
+ return (
+
+
+ {
+ if (ev.target instanceof HTMLAnchorElement) {
+ onRoomTopicLinkClick(ev);
+ return;
+ }
+ setExpanded(!expanded);
+ }}
+ >
+ {content}
+
+ setExpanded(!expanded)}
+ >
+
+
+
+ {expanded && (
+
+
+
+ {_t("action|edit")}
+
+
+
+ )}
+
+ );
+};
+
const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose, onSearchClick }) => {
const cli = useContext(MatrixClientContext);
@@ -382,6 +475,8 @@ const RoomSummaryCard: React.FC = ({ room, permalinkCreator, onClose, on
)}
+
+
);
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 9423801774..cdeecd98f6 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -1821,6 +1821,7 @@
},
"right_panel": {
"add_integrations": "Add widgets, bridges & bots",
+ "add_topic": "Add topic",
"edit_integrations": "Edit widgets, bridges & bots",
"export_chat_button": "Export chat",
"files_button": "Files",
diff --git a/test/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/components/structures/__snapshots__/RoomView-test.tsx.snap
index 062aeeffdd..91bfd33e83 100644
--- a/test/components/structures/__snapshots__/RoomView-test.tsx.snap
+++ b/test/components/structures/__snapshots__/RoomView-test.tsx.snap
@@ -47,11 +47,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1
class="mx_LegacyRoomHeader_topic mx_RoomTopic"
dir="auto"
tabindex="0"
- >
-
-
+ />
+ />