feat: overwrite room name displays
parent
94343e4811
commit
a66d72c77c
|
@ -5,5 +5,7 @@
|
|||
"src/components/views/rooms/Autocomplete.tsx": "src/components/views/rooms/Autocomplete.tsx",
|
||||
"src/components/views/rooms/RoomTile.tsx": "src/components/views/rooms/RoomTile.tsx",
|
||||
"src/components/views/elements/RoomName.tsx": "src/components/views/elements/RoomName.tsx",
|
||||
"src/editor/commands.tsx": "src/editor/commands.tsx"
|
||||
"src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx": "src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx",
|
||||
"src/editor/commands.tsx": "src/editor/commands.tsx",
|
||||
"src/hooks/useRoomName.ts": "src/hooks/useRoomName.ts"
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
.sh_RoomTokenGatedRoom {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.sh_RoomTokenGatedRoomIcon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.mx_RoomTile .mx_RoomTile_titleContainer .mx_RoomTile_title {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { IPublicRoomsChunkRoom } from "matrix-js-sdk/src/matrix";
|
||||
import { linkifyAndSanitizeHtml } from "matrix-react-sdk/src/HtmlUtils";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import React from "react";
|
||||
|
||||
import RoomName from "../../elements/RoomName";
|
||||
|
||||
const MAX_NAME_LENGTH = 80;
|
||||
const MAX_TOPIC_LENGTH = 800;
|
||||
|
||||
interface Props {
|
||||
room: IPublicRoomsChunkRoom;
|
||||
labelId: string;
|
||||
descriptionId: string;
|
||||
detailsId: string;
|
||||
}
|
||||
|
||||
export function PublicRoomResultDetails({ room, labelId, descriptionId, detailsId }: Props): JSX.Element {
|
||||
let topic = room.topic || "";
|
||||
// Additional truncation based on line numbers is done via CSS,
|
||||
// but to ensure that the DOM is not polluted with a huge string
|
||||
// we give it a hard limit before rendering.
|
||||
if (topic.length > MAX_TOPIC_LENGTH) {
|
||||
topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SpotlightDialog_result_publicRoomDetails">
|
||||
<div className="mx_SpotlightDialog_result_publicRoomHeader">
|
||||
<span id={labelId} className="mx_SpotlightDialog_result_publicRoomName">
|
||||
<RoomName room={room} maxLength={MAX_NAME_LENGTH} />
|
||||
</span>
|
||||
<span id={descriptionId} className="mx_SpotlightDialog_result_publicRoomAlias">
|
||||
{room.canonical_alias ?? room.room_id}
|
||||
</span>
|
||||
</div>
|
||||
<div id={detailsId} className="mx_SpotlightDialog_result_publicRoomDescription">
|
||||
<span className="mx_SpotlightDialog_result_publicRoomMemberCount">
|
||||
{_t("spotlight_dialog|count_of_members", {
|
||||
count: room.num_joined_members,
|
||||
})}
|
||||
</span>
|
||||
{topic && (
|
||||
<>
|
||||
·
|
||||
<span
|
||||
className="mx_SpotlightDialog_result_publicRoomTopic"
|
||||
dangerouslySetInnerHTML={{ __html: linkifyAndSanitizeHtml(topic) }}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -14,31 +14,44 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import { useTypedEventEmitter } from "matrix-react-sdk/src/hooks/useEventEmitter";
|
||||
import { IPublicRoomsChunkRoom, Room } from "matrix-js-sdk/src/matrix";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
|
||||
import { getRoomName } from "../../../hooks/useTokengatedRoom";
|
||||
import { Icon as TokenGatedRoomIcon } from "../../../../res/themes/superhero/img/icons/tokengated-room.svg";
|
||||
import { isTokenGatedRoom, useRoomName } from "../../../hooks/useRoomName";
|
||||
|
||||
interface IProps {
|
||||
room?: Room;
|
||||
children?(name: string): JSX.Element;
|
||||
room?: Room | IPublicRoomsChunkRoom;
|
||||
children?(name: JSX.Element): JSX.Element;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `useRoomName.ts` instead
|
||||
*/
|
||||
const RoomName = ({ room, children }: IProps): JSX.Element => {
|
||||
const [name, setName] = useState(getRoomName(room));
|
||||
useTypedEventEmitter(room, RoomEvent.Name, () => {
|
||||
setName(getRoomName(room));
|
||||
});
|
||||
useEffect(() => {
|
||||
setName(getRoomName(room));
|
||||
export const RoomName = ({ room, children, maxLength }: IProps): JSX.Element => {
|
||||
const roomName = useRoomName(room);
|
||||
|
||||
const isVerifiedRoom = useMemo(() => {
|
||||
return isTokenGatedRoom(room);
|
||||
}, [room]);
|
||||
|
||||
if (children) return children(name ?? "");
|
||||
return <>{name || ""}</>;
|
||||
const truncatedRoomName = useMemo(() => {
|
||||
if (maxLength && roomName.length > maxLength) {
|
||||
return `${roomName.substring(0, maxLength)}...`;
|
||||
}
|
||||
return roomName;
|
||||
}, [roomName, maxLength]);
|
||||
|
||||
const renderRoomName = useCallback(
|
||||
() => (
|
||||
<span className="sh_RoomTokenGatedRoom">
|
||||
{isVerifiedRoom && <TokenGatedRoomIcon className="sh_RoomTokenGatedRoomIcon" />}
|
||||
<span dir="auto">{truncatedRoomName}</span>
|
||||
</span>
|
||||
),
|
||||
[truncatedRoomName, isVerifiedRoom],
|
||||
);
|
||||
|
||||
if (children) return children(renderRoomName());
|
||||
return renderRoomName();
|
||||
};
|
||||
|
||||
export default RoomName;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import React from "react";
|
||||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { Icon as TokenGatedRoomIcon } from "../../../../res/themes/superhero/img/icons/tokengated-room.svg";
|
||||
import { useTokenGatedRoom } from "../../../hooks/useTokengatedRoom";
|
||||
|
||||
export interface CustomRoomNameProps {
|
||||
room: Room;
|
||||
}
|
||||
export const CustomRoomName: React.FC<CustomRoomNameProps> = ({ room }) => {
|
||||
const { roomName, isVerifiedRoom } = useTokenGatedRoom(room);
|
||||
return (
|
||||
<>
|
||||
{isVerifiedRoom && <TokenGatedRoomIcon className="sh_RoomTokenGatedRoomIcon" />}
|
||||
<span dir="auto">{roomName}</span>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -17,7 +17,7 @@ limitations under the License.
|
|||
|
||||
import classNames from "classnames";
|
||||
import { Room, RoomEvent } from "matrix-js-sdk/src/matrix";
|
||||
import React, { createRef } from "react";
|
||||
import React, { ReactElement, createRef } from "react";
|
||||
import { getKeyBindingsManager } from "matrix-react-sdk/src/KeyBindingsManager";
|
||||
import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
|
||||
import PosthogTrackers from "matrix-react-sdk/src/PosthogTrackers";
|
||||
|
@ -43,7 +43,6 @@ import defaultDispatcher from "matrix-react-sdk/src/dispatcher/dispatcher";
|
|||
import { ActionPayload } from "matrix-react-sdk/src/dispatcher/payloads";
|
||||
import { ViewRoomPayload } from "matrix-react-sdk/src/dispatcher/payloads/ViewRoomPayload";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import type { Call } from "matrix-react-sdk/src/models/Call";
|
||||
import SettingsStore from "matrix-react-sdk/src/settings/SettingsStore";
|
||||
import { UIComponent } from "matrix-react-sdk/src/settings/UIFeature";
|
||||
import { CallStore, CallStoreEvent } from "matrix-react-sdk/src/stores/CallStore";
|
||||
|
@ -59,8 +58,10 @@ import { MessagePreview, MessagePreviewStore } from "matrix-react-sdk/src/stores
|
|||
import { DefaultTagID, TagID } from "matrix-react-sdk/src/stores/room-list/models";
|
||||
import { isKnockDenied } from "matrix-react-sdk/src/utils/membership";
|
||||
import { useHasRoomLiveVoiceBroadcast } from "matrix-react-sdk/src/voice-broadcast";
|
||||
import { CustomRoomName } from "./CustomRoomName";
|
||||
import { getRoomName } from "../../../hooks/useTokengatedRoom";
|
||||
|
||||
import type { Call } from "matrix-react-sdk/src/models/Call";
|
||||
import { RoomName } from "../elements/RoomName";
|
||||
import { getRoomName } from "../../../hooks/useRoomName";
|
||||
|
||||
interface Props {
|
||||
room: Room;
|
||||
|
@ -358,16 +359,16 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
|||
{...contextMenuBelow(this.state.generalMenuPosition)}
|
||||
onFinished={this.onCloseGeneralMenu}
|
||||
room={this.props.room}
|
||||
onPostFavoriteClick={(ev: ButtonEvent) =>
|
||||
onPostFavoriteClick={(ev: ButtonEvent): void =>
|
||||
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuFavouriteToggle", ev)
|
||||
}
|
||||
onPostInviteClick={(ev: ButtonEvent) =>
|
||||
onPostInviteClick={(ev: ButtonEvent): void =>
|
||||
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuInviteItem", ev)
|
||||
}
|
||||
onPostSettingsClick={(ev: ButtonEvent) =>
|
||||
onPostSettingsClick={(ev: ButtonEvent): void =>
|
||||
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuSettingsItem", ev)
|
||||
}
|
||||
onPostLeaveClick={(ev: ButtonEvent) =>
|
||||
onPostLeaveClick={(ev: ButtonEvent): void =>
|
||||
PosthogTrackers.trackInteraction("WebRoomListRoomTileContextMenuLeaveItem", ev)
|
||||
}
|
||||
/>
|
||||
|
@ -435,7 +436,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
|||
const titleContainer = this.props.isMinimized ? null : (
|
||||
<div className="mx_RoomTile_titleContainer">
|
||||
<div title={name} className={titleClasses} tabIndex={-1}>
|
||||
<CustomRoomName room={this.props.room} />
|
||||
<RoomName room={this.props.room} />
|
||||
</div>
|
||||
{subtitle}
|
||||
</div>
|
||||
|
@ -478,7 +479,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
|
|||
return (
|
||||
<React.Fragment>
|
||||
<RovingTabIndexWrapper inputRef={this.roomTileRef}>
|
||||
{({ onFocus, isActive, ref }) => (
|
||||
{({ onFocus, isActive, ref }): ReactElement => (
|
||||
<Button
|
||||
{...props}
|
||||
onFocus={onFocus}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import { IPublicRoomsChunkRoom, Room } from "matrix-js-sdk/src/matrix";
|
||||
import { getDisplayAliasForAliasSet } from "matrix-react-sdk/src/Rooms";
|
||||
import { _t } from "matrix-react-sdk/src/languageHandler";
|
||||
import { IOOBData } from "matrix-react-sdk/src/stores/ThreepidInviteStore";
|
||||
import { useMemo } from "react";
|
||||
|
||||
/**
|
||||
* Determines the room name from a combination of the room model and potential
|
||||
* @param room - The room model
|
||||
* @param oobData - out-of-band information about the room
|
||||
* @returns {string} the room name
|
||||
*/
|
||||
export function getRoomName(room?: Room | IPublicRoomsChunkRoom, oobName?: IOOBData): string {
|
||||
const roomName =
|
||||
room?.name ||
|
||||
oobName?.name ||
|
||||
getDisplayAliasForAliasSet(
|
||||
(room as IPublicRoomsChunkRoom)?.canonical_alias ?? "",
|
||||
(room as IPublicRoomsChunkRoom)?.aliases ?? [],
|
||||
) ||
|
||||
_t("common|unnamed_room");
|
||||
|
||||
return (roomName || "")
|
||||
.replace(":", ":\u200b") // add a zero-width space to allow linewrapping after the colon (matrix defaults)
|
||||
.replace("[TG]", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a room is a token gated room
|
||||
* @param room - The room model
|
||||
* @returns {boolean} true if the room is token gated
|
||||
*/
|
||||
export function isTokenGatedRoom(room?: Room | IPublicRoomsChunkRoom): boolean {
|
||||
return !!room?.name?.includes("[TG]");
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the room name from a combination of the room model and potential
|
||||
* out-of-band information
|
||||
* @param room - The room model
|
||||
* @param oobData - out-of-band information about the room
|
||||
* @returns {string} the room name
|
||||
*
|
||||
* TODO: check if useTypedEventEmitter is needed
|
||||
*/
|
||||
export function useRoomName(room?: Room | IPublicRoomsChunkRoom, oobData?: IOOBData): string {
|
||||
const name = useMemo(() => {
|
||||
return getRoomName(room, oobData);
|
||||
}, [room, oobData]);
|
||||
|
||||
return name;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
import { Room } from "matrix-js-sdk/src/matrix";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export function getRoomName(room?: Room): string {
|
||||
return (room?.name || "")
|
||||
.replace(":", ":\u200b") // add a zero-width space to allow linewrapping after the colon (matrix defaults)
|
||||
.replace("[TG]", "");
|
||||
}
|
||||
|
||||
export function isTokenGatedRoom(room: Room): boolean {
|
||||
return room?.name?.includes("[TG]");
|
||||
}
|
||||
|
||||
export function useTokenGatedRoom(room: Room): { roomName: string; isVerifiedRoom: boolean } {
|
||||
const roomName = useMemo(() => {
|
||||
return getRoomName(room);
|
||||
}, [room]);
|
||||
|
||||
const isVerifiedRoom = useMemo(() => {
|
||||
return isTokenGatedRoom(room);
|
||||
}, [room]);
|
||||
|
||||
return {
|
||||
roomName,
|
||||
isVerifiedRoom,
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue