/* Copyright 2024 New Vector Ltd. Copyright 2020, 2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ import React, { useContext } from "react"; import { EventType, Room, User, MatrixClient } from "matrix-js-sdk/src/matrix"; import { KnownMembership } from "matrix-js-sdk/src/types"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import RoomContext from "../../../contexts/RoomContext"; import DMRoomMap from "../../../utils/DMRoomMap"; import { _t, _td, TranslationKey } from "../../../languageHandler"; import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader"; import RoomAvatar from "../avatars/RoomAvatar"; import defaultDispatcher from "../../../dispatcher/dispatcher"; import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload"; import { Action } from "../../../dispatcher/actions"; import SpaceStore from "../../../stores/spaces/SpaceStore"; import { showSpaceInvite } from "../../../utils/space"; import EventTileBubble from "../messages/EventTileBubble"; import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { shouldShowComponent } from "../../../customisations/helpers/UIComponents"; import { UIComponent } from "../../../settings/UIFeature"; import { privateShouldBeEncrypted } from "../../../utils/rooms"; import { LocalRoom } from "../../../models/LocalRoom"; import { shouldEncryptRoomWithSingle3rdPartyInvite } from "../../../utils/room/shouldEncryptRoomWithSingle3rdPartyInvite"; 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), }); }} />

{room.name}

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

{caption &&

{caption}

}
); } else { const inRoom = room && room.getMyMembership() === KnownMembership.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 setTimeout(() => { 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}

{room.name}

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

{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;