From 0f2dd90f733e32994b566e70db3d9af9d08057a1 Mon Sep 17 00:00:00 2001 From: Badi Ifaoui Date: Thu, 30 Nov 2023 08:30:30 +0100 Subject: [PATCH] feat: protected aex9 room message --- components.json | 1 + res/css/superhero/custom.css | 6 + src/components/views/rooms/NewRoomIntro.tsx | 313 ++++++++++++++++++++ src/i18n/strings/en_EN.json | 5 +- 4 files changed, 324 insertions(+), 1 deletion(-) create mode 100644 src/components/views/rooms/NewRoomIntro.tsx 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 = ( + + { + defaultDispatcher.dispatch({ + action: Action.ViewUser, + // XXX: We should be using a real member object and not assuming what the receiver wants. + member: member || ({ userId: dmPartner } as User), + }); + }} + /> + +

+ +

+ +

+ {_t( + introMessage, + {}, + { + displayName: () => {displayName}, + }, + )} +

+ {caption &&

{caption}

} +
+ ); + } else { + const inRoom = room && room.getMyMembership() === "join"; + const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic; + const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getSafeUserId()); + + const onTopicClick = (): void => { + defaultDispatcher.dispatch( + { + action: "open_room_settings", + room_id: roomId, + }, + true, + ); + // focus the topic field to help the user find it as it'll gain an outline + setImmediate(() => { + window.document.getElementById("profileTopic")?.focus(); + }); + }; + + let topicText; + if (canAddTopic && topic) { + topicText = _t( + "room|intro|topic_edit", + { topic }, + { + a: (sub) => ( + + {sub} + + ), + }, + ); + } else if (topic) { + topicText = _t("room|intro|topic", { topic }); + } else if (canAddTopic) { + topicText = _t( + "room|intro|no_topic", + {}, + { + a: (sub) => ( + + {sub} + + ), + }, + ); + } + + const creator = room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender(); + const creatorName = (creator && room?.getMember(creator)?.rawDisplayName) || creator; + + let createdText: string; + if (creator === cli.getUserId()) { + createdText = _t("room|intro|you_created"); + } else { + createdText = _t("room|intro|user_created", { + displayName: creatorName, + }); + } + + let parentSpace: Room | undefined; + if ( + SpaceStore.instance.activeSpaceRoom?.canInvite(cli.getSafeUserId()) && + SpaceStore.instance.isRoomInSpace(SpaceStore.instance.activeSpace!, room.roomId) + ) { + parentSpace = SpaceStore.instance.activeSpaceRoom; + } + + let buttons: JSX.Element | undefined; + if (parentSpace && shouldShowComponent(UIComponent.InviteUsers)) { + buttons = ( +
+ { + showSpaceInvite(parentSpace!); + }} + > + {_t("invite|to_space", { spaceName: parentSpace.name })} + + {room.canInvite(cli.getSafeUserId()) && ( + { + defaultDispatcher.dispatch({ action: "view_invite", roomId }); + }} + > + {_t("room|intro|room_invite")} + + )} +
+ ); + } else if (room.canInvite(cli.getSafeUserId()) && shouldShowComponent(UIComponent.InviteUsers)) { + buttons = ( +
+ { + defaultDispatcher.dispatch({ action: "view_invite", roomId }); + }} + > + {_t("room|invite_this_room")} + +
+ ); + } + + const avatarUrl = room.currentState.getStateEvents(EventType.RoomAvatar, "")?.getContent()?.url; + let avatar = ; + + if (!avatarUrl) { + avatar = ( + => + cli.sendStateEvent(roomId, EventType.RoomAvatar, { url }, "") + } + > + {avatar} + + ); + } + + body = ( + + {avatar} + +

+ +

+ +

+ {createdText}{" "} + {_t( + "room|intro|start_of_room", + {}, + { + roomName: () => {getRoomName(room)}, + }, + )} +

+

{topicText}

+ {buttons} +
+ ); + } + + function openRoomSettings(event: ButtonEvent): void { + event.preventDefault(); + defaultDispatcher.dispatch({ + action: "open_room_settings", + initial_tab_id: RoomSettingsTab.Security, + }); + } + + const subText = _t("room|intro|private_unencrypted_warning"); + + let subButton: JSX.Element | undefined; + if ( + room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.safeGet()) && + !isLocalRoom + ) { + subButton = ( + + {_t("room|intro|enable_encryption_prompt")} + + ); + } + + const subtitle = ( + + {" "} + {subText} {subButton}{" "} + + ); + + return ( +
  • + {!hasExpectedEncryptionSettings(cli, room) && ( + + )} + + {body} +
  • + ); +}; + +export default NewRoomIntro; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3888bf012..27588f25bf 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -38,5 +38,8 @@ "web_default_device_name": "%(appName)s: %(browserName)s on %(osName)s", "welcome_to_superhero": "Welcome to Superhero", "superhero_dex": "Superhero DEX", - "mint_a_token": "Mint a token" + "mint_a_token": "Mint a token", + "composer": { + "no_perms_notice": "You need to own the room token to be able to post in this room." + } }