diff --git a/components.json b/components.json
index a79d372cf3..e44b3e4254 100644
--- a/components.json
+++ b/components.json
@@ -4,6 +4,7 @@
"src/components/views/auth/AuthPage.tsx": "src/components/views/auth/VectorAuthPage.tsx",
"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/rooms/NewRoomIntro.tsx": "src/components/views/rooms/NewRoomIntro.tsx",
"src/components/views/elements/RoomName.tsx": "src/components/views/elements/RoomName.tsx",
"src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx": "src/components/views/dialogs/spotlight/PublicRoomResultDetails.tsx",
"src/components/views/avatars/BaseAvatar.tsx": "src/components/views/avatars/BaseAvatar.tsx",
diff --git a/res/css/superhero/custom.css b/res/css/superhero/custom.css
index 4993b23167..e916b73850 100644
--- a/res/css/superhero/custom.css
+++ b/res/css/superhero/custom.css
@@ -8,6 +8,12 @@
height: 16px;
margin-right: 4px;
}
+
+h2 .sh_RoomTokenGatedRoomIcon {
+ width: 26px;
+ height: 26px;
+}
+
.mx_QuickSettingsButton.sh_SuperheroDexButton::before {
-webkit-mask-image: url(../../themes/superhero/img/icons/diamond.svg);
mask-image: url(../../themes/superhero/img/icons/diamond.svg);
diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx
new file mode 100644
index 0000000000..fd7612eb3f
--- /dev/null
+++ b/src/components/views/rooms/NewRoomIntro.tsx
@@ -0,0 +1,313 @@
+/*
+Copyright 2020, 2021 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 { EventType, ISendEventResponse, MatrixClient, Room, User } from "matrix-js-sdk/src/matrix";
+import { MatrixClientPeg } from "matrix-react-sdk/src/MatrixClientPeg";
+import RoomAvatar from "matrix-react-sdk/src/components/views/avatars/RoomAvatar";
+import { RoomSettingsTab } from "matrix-react-sdk/src/components/views/dialogs/RoomSettingsDialog";
+import AccessibleButton, { ButtonEvent } from "matrix-react-sdk/src/components/views/elements/AccessibleButton";
+import MiniAvatarUploader, { AVATAR_SIZE } from "matrix-react-sdk/src/components/views/elements/MiniAvatarUploader";
+import EventTileBubble from "matrix-react-sdk/src/components/views/messages/EventTileBubble";
+import MatrixClientContext from "matrix-react-sdk/src/contexts/MatrixClientContext";
+import RoomContext from "matrix-react-sdk/src/contexts/RoomContext";
+import { shouldShowComponent } from "matrix-react-sdk/src/customisations/helpers/UIComponents";
+import { Action } from "matrix-react-sdk/src/dispatcher/actions";
+import defaultDispatcher from "matrix-react-sdk/src/dispatcher/dispatcher";
+import { ViewUserPayload } from "matrix-react-sdk/src/dispatcher/payloads/ViewUserPayload";
+import { TranslationKey, _t, _td } from "matrix-react-sdk/src/languageHandler";
+import { LocalRoom } from "matrix-react-sdk/src/models/LocalRoom";
+import { UIComponent } from "matrix-react-sdk/src/settings/UIFeature";
+import SpaceStore from "matrix-react-sdk/src/stores/spaces/SpaceStore";
+import DMRoomMap from "matrix-react-sdk/src/utils/DMRoomMap";
+import { shouldEncryptRoomWithSingle3rdPartyInvite } from "matrix-react-sdk/src/utils/room/shouldEncryptRoomWithSingle3rdPartyInvite";
+import { privateShouldBeEncrypted } from "matrix-react-sdk/src/utils/rooms";
+import { showSpaceInvite } from "matrix-react-sdk/src/utils/space";
+import React, { useContext } from "react";
+
+import { getRoomName } from "../../../hooks/useRoomName";
+import RoomName from "../elements/RoomName";
+
+function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
+ const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
+ const isPublic: boolean = room.getJoinRule() === "public";
+ return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted;
+}
+
+const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): TranslationKey => {
+ if (room instanceof LocalRoom) {
+ return _td("room|intro|send_message_start_dm");
+ }
+
+ if (encryptedSingle3rdPartyInvite) {
+ return _td("room|intro|encrypted_3pid_dm_pending_join");
+ }
+
+ return _td("room|intro|start_of_dm_history");
+};
+
+const NewRoomIntro: React.FC = () => {
+ const cli = useContext(MatrixClientContext);
+ const { room, roomId } = useContext(RoomContext);
+
+ if (!room || !roomId) {
+ throw new Error("Unable to create a NewRoomIntro without room and roomId");
+ }
+
+ const isLocalRoom = room instanceof LocalRoom;
+ const dmPartner = isLocalRoom ? room.targets[0]?.userId : DMRoomMap.shared().getUserIdForRoomId(roomId);
+
+ let body: JSX.Element;
+ if (dmPartner) {
+ const { shouldEncrypt: encryptedSingle3rdPartyInvite } = shouldEncryptRoomWithSingle3rdPartyInvite(room);
+ const introMessage = determineIntroMessage(room, encryptedSingle3rdPartyInvite);
+ let caption: string | undefined;
+
+ if (
+ !(room instanceof LocalRoom) &&
+ !encryptedSingle3rdPartyInvite &&
+ room.getJoinedMemberCount() + room.getInvitedMemberCount() === 2
+ ) {
+ caption = _t("room|intro|dm_caption");
+ }
+
+ const member = room?.getMember(dmPartner);
+ const displayName = room?.name || member?.rawDisplayName || dmPartner;
+ body = (
+
+ {_t(
+ introMessage,
+ {},
+ {
+ displayName: () => {displayName},
+ },
+ )}
+ {caption}
+
+
+
+ {createdText}{" "} + {_t( + "room|intro|start_of_room", + {}, + { + roomName: () => {getRoomName(room)}, + }, + )} +
+{topicText}
+ {buttons} +