2020-11-05 15:50:42 +00:00
|
|
|
|
/*
|
2021-03-26 11:43:01 +00:00
|
|
|
|
Copyright 2020, 2021 The Matrix.org Foundation C.I.C.
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
2021-06-18 16:21:46 +01:00
|
|
|
|
import React, { useContext } from "react";
|
2023-08-09 08:18:41 +01:00
|
|
|
|
import { EventType, Room, User, MatrixClient } from "matrix-js-sdk/src/matrix";
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
|
|
|
|
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
|
|
|
|
|
import RoomContext from "../../../contexts/RoomContext";
|
|
|
|
|
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
2023-02-27 09:15:27 +00:00
|
|
|
|
import { _t, _td } from "../../../languageHandler";
|
2023-01-12 13:25:14 +00:00
|
|
|
|
import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton";
|
2021-06-18 16:21:46 +01:00
|
|
|
|
import MiniAvatarUploader, { AVATAR_SIZE } from "../elements/MiniAvatarUploader";
|
2020-11-05 15:50:42 +00:00
|
|
|
|
import RoomAvatar from "../avatars/RoomAvatar";
|
|
|
|
|
|
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
2021-06-18 16:21:46 +01:00
|
|
|
|
import { ViewUserPayload } from "../../../dispatcher/payloads/ViewUserPayload";
|
|
|
|
|
|
import { Action } from "../../../dispatcher/actions";
|
2021-11-11 13:07:41 +00:00
|
|
|
|
import SpaceStore from "../../../stores/spaces/SpaceStore";
|
2021-06-18 16:21:46 +01:00
|
|
|
|
import { showSpaceInvite } from "../../../utils/space";
|
2021-06-10 11:14:43 +01:00
|
|
|
|
import EventTileBubble from "../messages/EventTileBubble";
|
2023-04-27 12:55:29 +01:00
|
|
|
|
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
|
2021-08-20 08:37:30 +02:00
|
|
|
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
2021-10-08 16:04:26 -06:00
|
|
|
|
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
|
|
|
|
|
|
import { UIComponent } from "../../../settings/UIFeature";
|
2022-03-24 15:32:22 -06:00
|
|
|
|
import { privateShouldBeEncrypted } from "../../../utils/rooms";
|
2022-07-20 15:07:06 +02:00
|
|
|
|
import { LocalRoom } from "../../../models/LocalRoom";
|
2023-03-06 12:08:04 +01:00
|
|
|
|
import { shouldEncryptRoomWithSingle3rdPartyInvite } from "../../../utils/room/shouldEncryptRoomWithSingle3rdPartyInvite";
|
2021-06-10 11:14:43 +01:00
|
|
|
|
|
2021-06-18 16:29:10 +01:00
|
|
|
|
function hasExpectedEncryptionSettings(matrixClient: MatrixClient, room: Room): boolean {
|
|
|
|
|
|
const isEncrypted: boolean = matrixClient.isRoomEncrypted(room.roomId);
|
2021-04-27 14:47:56 +01:00
|
|
|
|
const isPublic: boolean = room.getJoinRule() === "public";
|
2023-05-23 16:24:12 +01:00
|
|
|
|
return isPublic || !privateShouldBeEncrypted(matrixClient) || isEncrypted;
|
2021-04-27 14:47:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-06 12:08:04 +01:00
|
|
|
|
const determineIntroMessage = (room: Room, encryptedSingle3rdPartyInvite: boolean): string => {
|
|
|
|
|
|
if (room instanceof LocalRoom) {
|
|
|
|
|
|
return _td("Send your first message to invite <displayName/> to chat");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (encryptedSingle3rdPartyInvite) {
|
|
|
|
|
|
return _td("Once everyone has joined, you’ll be able to chat");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return _td("This is the beginning of your direct message history with <displayName/>.");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
const NewRoomIntro: React.FC = () => {
|
2020-11-05 15:50:42 +00:00
|
|
|
|
const cli = useContext(MatrixClientContext);
|
2021-06-29 13:11:58 +01:00
|
|
|
|
const { room, roomId } = useContext(RoomContext);
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
2023-03-06 12:08:04 +01:00
|
|
|
|
if (!room || !roomId) {
|
|
|
|
|
|
throw new Error("Unable to create a NewRoomIntro without room and roomId");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-20 15:07:06 +02:00
|
|
|
|
const isLocalRoom = room instanceof LocalRoom;
|
|
|
|
|
|
const dmPartner = isLocalRoom ? room.targets[0]?.userId : DMRoomMap.shared().getUserIdForRoomId(roomId);
|
|
|
|
|
|
|
2022-09-06 09:54:53 +02:00
|
|
|
|
let body: JSX.Element;
|
2020-11-05 15:50:42 +00:00
|
|
|
|
if (dmPartner) {
|
2023-03-06 12:08:04 +01:00
|
|
|
|
const { shouldEncrypt: encryptedSingle3rdPartyInvite } = shouldEncryptRoomWithSingle3rdPartyInvite(room);
|
|
|
|
|
|
const introMessage = determineIntroMessage(room, encryptedSingle3rdPartyInvite);
|
2022-09-06 09:54:53 +02:00
|
|
|
|
let caption: string | undefined;
|
2022-07-20 15:07:06 +02:00
|
|
|
|
|
2023-03-06 12:08:04 +01:00
|
|
|
|
if (
|
|
|
|
|
|
!(room instanceof LocalRoom) &&
|
|
|
|
|
|
!encryptedSingle3rdPartyInvite &&
|
|
|
|
|
|
room.getJoinedMemberCount() + room.getInvitedMemberCount() === 2
|
|
|
|
|
|
) {
|
2020-11-05 15:50:42 +00:00
|
|
|
|
caption = _t("Only the two of you are in this conversation, unless either of you invites anyone to join.");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const member = room?.getMember(dmPartner);
|
2022-09-06 09:54:53 +02:00
|
|
|
|
const displayName = room?.name || member?.rawDisplayName || dmPartner;
|
2020-11-05 15:50:42 +00:00
|
|
|
|
body = (
|
|
|
|
|
|
<React.Fragment>
|
2021-07-23 10:23:45 +01:00
|
|
|
|
<RoomAvatar
|
|
|
|
|
|
room={room}
|
|
|
|
|
|
width={AVATAR_SIZE}
|
|
|
|
|
|
height={AVATAR_SIZE}
|
|
|
|
|
|
onClick={() => {
|
|
|
|
|
|
defaultDispatcher.dispatch<ViewUserPayload>({
|
2021-08-03 17:07:37 +01:00
|
|
|
|
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),
|
2021-07-23 10:23:45 +01:00
|
|
|
|
});
|
|
|
|
|
|
}}
|
|
|
|
|
|
/>
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
|
|
|
|
|
<h2>{room.name}</h2>
|
|
|
|
|
|
|
2022-07-20 15:07:06 +02:00
|
|
|
|
<p>
|
|
|
|
|
|
{_t(
|
|
|
|
|
|
introMessage,
|
|
|
|
|
|
{},
|
2020-11-05 15:50:42 +00:00
|
|
|
|
{
|
|
|
|
|
|
displayName: () => <b>{displayName}</b>,
|
2021-07-19 22:43:11 +01:00
|
|
|
|
},
|
2022-12-12 12:24:14 +01:00
|
|
|
|
)}
|
2021-07-19 22:43:11 +01:00
|
|
|
|
</p>
|
2020-11-05 15:50:42 +00:00
|
|
|
|
{caption && <p>{caption}</p>}
|
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
2020-12-08 10:31:40 +00:00
|
|
|
|
const inRoom = room && room.getMyMembership() === "join";
|
2020-11-05 15:50:42 +00:00
|
|
|
|
const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
|
2023-03-06 12:08:04 +01:00
|
|
|
|
const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getSafeUserId());
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
const onTopicClick = (): void => {
|
2022-05-03 22:04:37 +01:00
|
|
|
|
defaultDispatcher.dispatch(
|
|
|
|
|
|
{
|
2020-11-05 15:50:42 +00:00
|
|
|
|
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(() => {
|
2023-03-06 12:08:04 +01:00
|
|
|
|
window.document.getElementById("profileTopic")?.focus();
|
2020-11-05 15:50:42 +00:00
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let topicText;
|
|
|
|
|
|
if (canAddTopic && topic) {
|
|
|
|
|
|
topicText = _t(
|
|
|
|
|
|
"Topic: %(topic)s (<a>edit</a>)",
|
|
|
|
|
|
{ topic },
|
|
|
|
|
|
{
|
2022-06-29 22:37:34 +00:00
|
|
|
|
a: (sub) => (
|
2023-03-21 10:55:48 -05:00
|
|
|
|
<AccessibleButton element="a" kind="link_inline" onClick={onTopicClick}>
|
2022-06-29 22:37:34 +00:00
|
|
|
|
{sub}
|
|
|
|
|
|
</AccessibleButton>
|
2022-12-12 12:24:14 +01:00
|
|
|
|
),
|
2022-06-29 22:37:34 +00:00
|
|
|
|
},
|
2020-11-05 15:50:42 +00:00
|
|
|
|
);
|
|
|
|
|
|
} else if (topic) {
|
|
|
|
|
|
topicText = _t("Topic: %(topic)s ", { topic });
|
|
|
|
|
|
} else if (canAddTopic) {
|
|
|
|
|
|
topicText = _t(
|
|
|
|
|
|
"<a>Add a topic</a> to help people know what it is about.",
|
|
|
|
|
|
{},
|
|
|
|
|
|
{
|
2022-06-29 22:37:34 +00:00
|
|
|
|
a: (sub) => (
|
2023-03-21 10:55:48 -05:00
|
|
|
|
<AccessibleButton element="a" kind="link_inline" onClick={onTopicClick}>
|
2022-06-29 22:37:34 +00:00
|
|
|
|
{sub}
|
|
|
|
|
|
</AccessibleButton>
|
2022-12-12 12:24:14 +01:00
|
|
|
|
),
|
2022-06-29 22:37:34 +00:00
|
|
|
|
},
|
2020-11-05 15:50:42 +00:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const creator = room.currentState.getStateEvents(EventType.RoomCreate, "")?.getSender();
|
2023-03-16 11:07:29 +00:00
|
|
|
|
const creatorName = (creator && room?.getMember(creator)?.rawDisplayName) || creator;
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
2023-03-13 15:07:20 +00:00
|
|
|
|
let createdText: string;
|
2020-11-05 15:50:42 +00:00
|
|
|
|
if (creator === cli.getUserId()) {
|
|
|
|
|
|
createdText = _t("You created this room.");
|
|
|
|
|
|
} else {
|
|
|
|
|
|
createdText = _t("%(displayName)s created this room.", {
|
|
|
|
|
|
displayName: creatorName,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-03-13 15:07:20 +00:00
|
|
|
|
let parentSpace: Room | undefined;
|
2021-03-24 15:30:03 +00:00
|
|
|
|
if (
|
2023-03-13 15:07:20 +00:00
|
|
|
|
SpaceStore.instance.activeSpaceRoom?.canInvite(cli.getSafeUserId()) &&
|
2023-03-16 11:07:29 +00:00
|
|
|
|
SpaceStore.instance.isRoomInSpace(SpaceStore.instance.activeSpace!, room.roomId)
|
2021-03-24 15:30:03 +00:00
|
|
|
|
) {
|
2021-11-11 13:07:41 +00:00
|
|
|
|
parentSpace = SpaceStore.instance.activeSpaceRoom;
|
2021-03-24 15:30:03 +00:00
|
|
|
|
}
|
2020-12-08 10:31:40 +00:00
|
|
|
|
|
2023-03-13 15:07:20 +00:00
|
|
|
|
let buttons: JSX.Element | undefined;
|
2022-03-02 10:37:18 -07:00
|
|
|
|
if (parentSpace && shouldShowComponent(UIComponent.InviteUsers)) {
|
2020-12-08 10:31:40 +00:00
|
|
|
|
buttons = (
|
|
|
|
|
|
<div className="mx_NewRoomIntro_buttons">
|
2021-03-24 15:30:03 +00:00
|
|
|
|
<AccessibleButton
|
|
|
|
|
|
className="mx_NewRoomIntro_inviteButton"
|
|
|
|
|
|
kind="primary"
|
|
|
|
|
|
onClick={() => {
|
2023-03-13 15:07:20 +00:00
|
|
|
|
showSpaceInvite(parentSpace!);
|
2021-03-24 15:30:03 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
{_t("Invite to %(spaceName)s", { spaceName: parentSpace.name })}
|
2021-03-24 15:30:03 +00:00
|
|
|
|
</AccessibleButton>
|
2023-03-13 15:07:20 +00:00
|
|
|
|
{room.canInvite(cli.getSafeUserId()) && (
|
2021-03-24 15:30:03 +00:00
|
|
|
|
<AccessibleButton
|
|
|
|
|
|
className="mx_NewRoomIntro_inviteButton"
|
|
|
|
|
|
kind="primary_outline"
|
|
|
|
|
|
onClick={() => {
|
2022-05-03 22:04:37 +01:00
|
|
|
|
defaultDispatcher.dispatch({ action: "view_invite", roomId });
|
2021-03-24 15:30:03 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
{_t("Invite to just this room")}
|
2021-03-24 15:30:03 +00:00
|
|
|
|
</AccessibleButton>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2023-03-13 15:07:20 +00:00
|
|
|
|
} else if (room.canInvite(cli.getSafeUserId()) && shouldShowComponent(UIComponent.InviteUsers)) {
|
2021-03-24 15:30:03 +00:00
|
|
|
|
buttons = (
|
|
|
|
|
|
<div className="mx_NewRoomIntro_buttons">
|
|
|
|
|
|
<AccessibleButton
|
|
|
|
|
|
className="mx_NewRoomIntro_inviteButton"
|
|
|
|
|
|
kind="primary"
|
|
|
|
|
|
onClick={() => {
|
2022-05-03 22:04:37 +01:00
|
|
|
|
defaultDispatcher.dispatch({ action: "view_invite", roomId });
|
2021-03-24 15:30:03 +00:00
|
|
|
|
}}
|
|
|
|
|
|
>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
{_t("Invite to this room")}
|
2020-12-08 10:31:40 +00:00
|
|
|
|
</AccessibleButton>
|
2021-03-24 15:30:03 +00:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
2020-12-08 10:31:40 +00:00
|
|
|
|
}
|
2020-11-11 15:17:34 +00:00
|
|
|
|
|
2020-11-05 15:50:42 +00:00
|
|
|
|
const avatarUrl = room.currentState.getStateEvents(EventType.RoomAvatar, "")?.getContent()?.url;
|
2022-10-21 14:45:38 +01:00
|
|
|
|
let avatar = (
|
|
|
|
|
|
<RoomAvatar room={room} width={AVATAR_SIZE} height={AVATAR_SIZE} viewAvatarOnClick={!!avatarUrl} />
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (!avatarUrl) {
|
|
|
|
|
|
avatar = (
|
|
|
|
|
|
<MiniAvatarUploader
|
|
|
|
|
|
hasAvatar={false}
|
2020-11-05 15:50:42 +00:00
|
|
|
|
noAvatarLabel={_t("Add a photo, so people can easily spot your room.")}
|
|
|
|
|
|
setAvatarUrl={(url) => cli.sendStateEvent(roomId, EventType.RoomAvatar, { url }, "")}
|
|
|
|
|
|
>
|
2022-10-21 14:45:38 +01:00
|
|
|
|
{avatar}
|
|
|
|
|
|
</MiniAvatarUploader>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
body = (
|
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
|
{avatar}
|
2020-11-05 15:50:42 +00:00
|
|
|
|
|
|
|
|
|
|
<h2>{room.name}</h2>
|
|
|
|
|
|
|
2021-07-19 22:43:11 +01:00
|
|
|
|
<p>
|
|
|
|
|
|
{createdText}{" "}
|
|
|
|
|
|
{_t(
|
|
|
|
|
|
"This is the start of <roomName/>.",
|
|
|
|
|
|
{},
|
2020-11-05 15:50:42 +00:00
|
|
|
|
{
|
|
|
|
|
|
roomName: () => <b>{room.name}</b>,
|
2021-07-19 22:43:11 +01:00
|
|
|
|
},
|
2022-12-12 12:24:14 +01:00
|
|
|
|
)}
|
2021-07-19 22:43:11 +01:00
|
|
|
|
</p>
|
|
|
|
|
|
<p>{topicText}</p>
|
2020-12-08 10:31:40 +00:00
|
|
|
|
{buttons}
|
2020-11-05 15:50:42 +00:00
|
|
|
|
</React.Fragment>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-12 13:25:14 +00:00
|
|
|
|
function openRoomSettings(event: ButtonEvent): void {
|
2021-06-10 11:14:43 +01:00
|
|
|
|
event.preventDefault();
|
2022-05-03 22:04:37 +01:00
|
|
|
|
defaultDispatcher.dispatch({
|
2021-06-10 11:14:43 +01:00
|
|
|
|
action: "open_room_settings",
|
2023-04-27 12:55:29 +01:00
|
|
|
|
initial_tab_id: RoomSettingsTab.Security,
|
2021-06-10 11:14:43 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-08-20 08:37:30 +02:00
|
|
|
|
const subText = _t(
|
2021-06-10 14:21:48 +01:00
|
|
|
|
"Your private messages are normally encrypted, but this room isn't. " +
|
|
|
|
|
|
"Usually this is due to an unsupported device or method being used, " +
|
2021-08-20 08:37:30 +02:00
|
|
|
|
"like email invites.",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2023-06-15 08:46:19 +01:00
|
|
|
|
let subButton: JSX.Element | undefined;
|
|
|
|
|
|
if (
|
|
|
|
|
|
room.currentState.mayClientSendStateEvent(EventType.RoomEncryption, MatrixClientPeg.safeGet()) &&
|
|
|
|
|
|
!isLocalRoom
|
|
|
|
|
|
) {
|
2021-08-20 08:37:30 +02:00
|
|
|
|
subButton = (
|
2022-01-07 10:40:53 +01:00
|
|
|
|
<AccessibleButton kind="link_inline" onClick={openRoomSettings}>
|
|
|
|
|
|
{_t("Enable encryption in settings.")}
|
|
|
|
|
|
</AccessibleButton>
|
2021-08-20 08:37:30 +02:00
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const subtitle = (
|
|
|
|
|
|
<span>
|
2022-12-12 12:24:14 +01:00
|
|
|
|
{" "}
|
2021-08-20 08:37:30 +02:00
|
|
|
|
{subText} {subButton}{" "}
|
|
|
|
|
|
</span>
|
2021-06-10 14:21:48 +01:00
|
|
|
|
);
|
2021-06-10 11:14:43 +01:00
|
|
|
|
|
2022-08-01 08:31:14 +01:00
|
|
|
|
return (
|
|
|
|
|
|
<li className="mx_NewRoomIntro">
|
2021-06-18 16:29:10 +01:00
|
|
|
|
{!hasExpectedEncryptionSettings(cli, room) && (
|
2021-06-10 11:14:43 +01:00
|
|
|
|
<EventTileBubble
|
|
|
|
|
|
className="mx_cryptoEvent mx_cryptoEvent_icon_warning"
|
2021-06-10 11:29:10 +01:00
|
|
|
|
title={_t("End-to-end encryption isn't enabled")}
|
2021-08-20 08:37:30 +02:00
|
|
|
|
subtitle={subtitle}
|
2021-06-10 11:14:43 +01:00
|
|
|
|
/>
|
2021-07-19 22:43:11 +01:00
|
|
|
|
)}
|
2021-06-10 11:14:43 +01:00
|
|
|
|
|
2020-11-05 15:50:42 +00:00
|
|
|
|
{body}
|
2022-08-01 08:31:14 +01:00
|
|
|
|
</li>
|
|
|
|
|
|
);
|
2020-11-05 15:50:42 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default NewRoomIntro;
|