diff --git a/res/css/superhero/custom.css b/res/css/superhero/custom.css
new file mode 100644
index 0000000000..12a8221bcc
--- /dev/null
+++ b/res/css/superhero/custom.css
@@ -0,0 +1,9 @@
+.sh_RoomTokenGatedRoomIcon {
+ width: 16px;
+ height: 16px;
+ margin-right: 4px;
+}
+.mx_RoomTile .mx_RoomTile_titleContainer .mx_RoomTile_title {
+ align-items: center;
+ display: flex;
+}
diff --git a/res/themes/superhero/img/icons/tokengated-room.svg b/res/themes/superhero/img/icons/tokengated-room.svg
new file mode 100644
index 0000000000..d69c4e31c3
--- /dev/null
+++ b/res/themes/superhero/img/icons/tokengated-room.svg
@@ -0,0 +1,6 @@
+
diff --git a/src/components/views/rooms/CustomRoomName.tsx b/src/components/views/rooms/CustomRoomName.tsx
new file mode 100644
index 0000000000..a1ae324cb2
--- /dev/null
+++ b/src/components/views/rooms/CustomRoomName.tsx
@@ -0,0 +1,18 @@
+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 = ({ room }) => {
+ const { roomName, isVerifiedRoom } = useTokenGatedRoom(room);
+ return (
+ <>
+ {isVerifiedRoom && }
+ {roomName}
+ >
+ );
+};
diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx
index c77b85e695..12e966c2be 100644
--- a/src/components/views/rooms/RoomTile.tsx
+++ b/src/components/views/rooms/RoomTile.tsx
@@ -59,6 +59,8 @@ 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";
interface Props {
room: Room;
@@ -399,10 +401,7 @@ export class RoomTile extends React.PureComponent {
mx_RoomTile_minimized: this.props.isMinimized,
});
- let name = this.props.room.name;
- if (typeof name !== "string") name = "";
- name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
-
+ const name = getRoomName(this.props.room);
let badge: React.ReactNode;
if (!this.props.isMinimized && this.notificationState) {
// aria-hidden because we summarise the unread count/highlight status in a manual aria-label below
@@ -436,7 +435,7 @@ export class RoomTile extends React.PureComponent {
const titleContainer = this.props.isMinimized ? null : (
diff --git a/src/hooks/useTokenGatedRoom.tsx b/src/hooks/useTokenGatedRoom.tsx
new file mode 100644
index 0000000000..452ff48370
--- /dev/null
+++ b/src/hooks/useTokenGatedRoom.tsx
@@ -0,0 +1,27 @@
+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): any {
+ const roomName = useMemo(() => {
+ return getRoomName(room);
+ }, [room]);
+
+ const isVerifiedRoom = useMemo(() => {
+ return isTokenGatedRoom(room);
+ }, [room]);
+
+ return {
+ roomName,
+ isVerifiedRoom,
+ };
+}
diff --git a/src/vector/index.ts b/src/vector/index.ts
index 10f7a627b6..a9f653b5de 100644
--- a/src/vector/index.ts
+++ b/src/vector/index.ts
@@ -33,6 +33,7 @@ import "setimmediate";
// in webpack.config.js
require("gfm.css/gfm.css");
require("katex/dist/katex.css");
+require("../../res/css/superhero/custom.css");
/**
* This require is necessary only for purposes of CSS hot-reload, as otherwise