You've already forked element-web
mirror of
https://github.com/element-hq/element-web.git
synced 2025-11-29 21:23:11 +03:00
Apply prettier formatting
This commit is contained in:
@@ -74,7 +74,7 @@ import RoomPreviewCard from "../views/rooms/RoomPreviewCard";
|
||||
import { SpaceFeedbackPrompt } from "../views/spaces/SpaceCreateMenu";
|
||||
import SpacePublicShare from "../views/spaces/SpacePublicShare";
|
||||
import { ChevronFace, ContextMenuButton, useContextMenu } from "./ContextMenu";
|
||||
import MainSplit from './MainSplit';
|
||||
import MainSplit from "./MainSplit";
|
||||
import RightPanel from "./RightPanel";
|
||||
import SpaceHierarchy, { showRoom } from "./SpaceHierarchy";
|
||||
|
||||
@@ -113,93 +113,101 @@ const SpaceLandingAddButton = ({ space }) => {
|
||||
let contextMenu: JSX.Element | null = null;
|
||||
if (menuDisplayed) {
|
||||
const rect = handle.current.getBoundingClientRect();
|
||||
contextMenu = <IconizedContextMenu
|
||||
left={rect.left + window.scrollX + 0}
|
||||
top={rect.bottom + window.scrollY + 8}
|
||||
chevronFace={ChevronFace.None}
|
||||
onFinished={closeMenu}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList first>
|
||||
{ canCreateRoom && <>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("New room")}
|
||||
iconClassName="mx_RoomList_iconNewRoom"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
contextMenu = (
|
||||
<IconizedContextMenu
|
||||
left={rect.left + window.scrollX + 0}
|
||||
top={rect.bottom + window.scrollY + 8}
|
||||
chevronFace={ChevronFace.None}
|
||||
onFinished={closeMenu}
|
||||
className="mx_RoomTile_contextMenu"
|
||||
compact
|
||||
>
|
||||
<IconizedContextMenuOptionList first>
|
||||
{canCreateRoom && (
|
||||
<>
|
||||
<IconizedContextMenuOption
|
||||
label={_t("New room")}
|
||||
iconClassName="mx_RoomList_iconNewRoom"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
|
||||
PosthogTrackers.trackInteraction("WebSpaceHomeCreateRoomButton", e);
|
||||
if (await showCreateNewRoom(space)) {
|
||||
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{ videoRoomsEnabled && (
|
||||
<IconizedContextMenuOption
|
||||
label={_t("New video room")}
|
||||
iconClassName="mx_RoomList_iconNewVideoRoom"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
PosthogTrackers.trackInteraction("WebSpaceHomeCreateRoomButton", e);
|
||||
if (await showCreateNewRoom(space)) {
|
||||
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{videoRoomsEnabled && (
|
||||
<IconizedContextMenuOption
|
||||
label={_t("New video room")}
|
||||
iconClassName="mx_RoomList_iconNewVideoRoom"
|
||||
onClick={async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
|
||||
if (
|
||||
await showCreateNewRoom(
|
||||
space,
|
||||
elementCallVideoRoomsEnabled ? RoomType.UnstableCall : RoomType.ElementVideo,
|
||||
)
|
||||
) {
|
||||
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
) }
|
||||
</> }
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Add existing room")}
|
||||
iconClassName="mx_RoomList_iconAddExistingRoom"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
showAddExistingRooms(space);
|
||||
}}
|
||||
/>
|
||||
{ canCreateSpace &&
|
||||
if (
|
||||
await showCreateNewRoom(
|
||||
space,
|
||||
elementCallVideoRoomsEnabled
|
||||
? RoomType.UnstableCall
|
||||
: RoomType.ElementVideo,
|
||||
)
|
||||
) {
|
||||
defaultDispatcher.fire(Action.UpdateSpaceHierarchy);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Add space")}
|
||||
iconClassName="mx_RoomList_iconPlus"
|
||||
label={_t("Add existing room")}
|
||||
iconClassName="mx_RoomList_iconAddExistingRoom"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
showCreateNewSubspace(space);
|
||||
showAddExistingRooms(space);
|
||||
}}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>;
|
||||
/>
|
||||
{canCreateSpace && (
|
||||
<IconizedContextMenuOption
|
||||
label={_t("Add space")}
|
||||
iconClassName="mx_RoomList_iconPlus"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
closeMenu();
|
||||
showCreateNewSubspace(space);
|
||||
}}
|
||||
>
|
||||
<BetaPill />
|
||||
</IconizedContextMenuOption>
|
||||
)}
|
||||
</IconizedContextMenuOptionList>
|
||||
</IconizedContextMenu>
|
||||
);
|
||||
}
|
||||
|
||||
return <>
|
||||
<ContextMenuButton
|
||||
kind="primary"
|
||||
inputRef={handle}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
label={_t("Add")}
|
||||
>
|
||||
{ _t("Add") }
|
||||
</ContextMenuButton>
|
||||
{ contextMenu }
|
||||
</>;
|
||||
return (
|
||||
<>
|
||||
<ContextMenuButton
|
||||
kind="primary"
|
||||
inputRef={handle}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
label={_t("Add")}
|
||||
>
|
||||
{_t("Add")}
|
||||
</ContextMenuButton>
|
||||
{contextMenu}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SpaceLanding = ({ space }: { space: Room }) => {
|
||||
@@ -208,8 +216,9 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
||||
const userId = cli.getUserId();
|
||||
|
||||
const storeIsShowingSpaceMembers = useCallback(
|
||||
() => RightPanelStore.instance.isOpenForRoom(space.roomId)
|
||||
&& RightPanelStore.instance.currentCardForRoom(space.roomId)?.phase === RightPanelPhases.SpaceMemberList,
|
||||
() =>
|
||||
RightPanelStore.instance.isOpenForRoom(space.roomId) &&
|
||||
RightPanelStore.instance.currentCardForRoom(space.roomId)?.phase === RightPanelPhases.SpaceMemberList,
|
||||
[space.roomId],
|
||||
);
|
||||
const isShowingMembers = useEventEmitterState(RightPanelStore.instance, UPDATE_EVENT, storeIsShowingSpaceMembers);
|
||||
@@ -224,13 +233,13 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
||||
showSpaceInvite(space);
|
||||
}}
|
||||
>
|
||||
{ _t("Invite") }
|
||||
{_t("Invite")}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
||||
const hasAddRoomPermissions = myMembership === "join" &&
|
||||
space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
|
||||
const hasAddRoomPermissions =
|
||||
myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
|
||||
|
||||
let addRoomButton;
|
||||
if (hasAddRoomPermissions) {
|
||||
@@ -239,49 +248,53 @@ const SpaceLanding = ({ space }: { space: Room }) => {
|
||||
|
||||
let settingsButton;
|
||||
if (shouldShowSpaceSettings(space)) {
|
||||
settingsButton = <AccessibleTooltipButton
|
||||
className="mx_SpaceRoomView_landing_settingsButton"
|
||||
onClick={() => {
|
||||
showSpaceSettings(space);
|
||||
}}
|
||||
title={_t("Settings")}
|
||||
/>;
|
||||
settingsButton = (
|
||||
<AccessibleTooltipButton
|
||||
className="mx_SpaceRoomView_landing_settingsButton"
|
||||
onClick={() => {
|
||||
showSpaceSettings(space);
|
||||
}}
|
||||
title={_t("Settings")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const onMembersClick = () => {
|
||||
RightPanelStore.instance.setCard({ phase: RightPanelPhases.SpaceMemberList });
|
||||
};
|
||||
|
||||
return <div className="mx_SpaceRoomView_landing">
|
||||
<div className="mx_SpaceRoomView_landing_header">
|
||||
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
||||
<SpaceFeedbackPrompt />
|
||||
</div>
|
||||
<div className="mx_SpaceRoomView_landing_name">
|
||||
<RoomName room={space}>
|
||||
{ (name) => {
|
||||
const tags = { name: () => <h1>{ name }</h1> };
|
||||
return _t("Welcome to <name/>", {}, tags) as JSX.Element;
|
||||
} }
|
||||
</RoomName>
|
||||
</div>
|
||||
<div className="mx_SpaceRoomView_landing_infoBar">
|
||||
<RoomInfoLine room={space} />
|
||||
<div className="mx_SpaceRoomView_landing_infoBar_interactive">
|
||||
<RoomFacePile
|
||||
room={space}
|
||||
onlyKnownUsers={false}
|
||||
numShown={7}
|
||||
onClick={isShowingMembers ? undefined : onMembersClick}
|
||||
/>
|
||||
{ inviteButton }
|
||||
{ settingsButton }
|
||||
return (
|
||||
<div className="mx_SpaceRoomView_landing">
|
||||
<div className="mx_SpaceRoomView_landing_header">
|
||||
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
||||
<SpaceFeedbackPrompt />
|
||||
</div>
|
||||
</div>
|
||||
<RoomTopic room={space} className="mx_SpaceRoomView_landing_topic" />
|
||||
<div className="mx_SpaceRoomView_landing_name">
|
||||
<RoomName room={space}>
|
||||
{(name) => {
|
||||
const tags = { name: () => <h1>{name}</h1> };
|
||||
return _t("Welcome to <name/>", {}, tags) as JSX.Element;
|
||||
}}
|
||||
</RoomName>
|
||||
</div>
|
||||
<div className="mx_SpaceRoomView_landing_infoBar">
|
||||
<RoomInfoLine room={space} />
|
||||
<div className="mx_SpaceRoomView_landing_infoBar_interactive">
|
||||
<RoomFacePile
|
||||
room={space}
|
||||
onlyKnownUsers={false}
|
||||
numShown={7}
|
||||
onClick={isShowingMembers ? undefined : onMembersClick}
|
||||
/>
|
||||
{inviteButton}
|
||||
{settingsButton}
|
||||
</div>
|
||||
</div>
|
||||
<RoomTopic room={space} className="mx_SpaceRoomView_landing_topic" />
|
||||
|
||||
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
|
||||
</div>;
|
||||
<SpaceHierarchy space={space} showRoom={showRoom} additionalButtons={addRoomButton} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||
@@ -292,18 +305,20 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||
const [roomNames, setRoomName] = useStateArray(numFields, [_t("General"), _t("Random"), ""]);
|
||||
const fields = new Array(numFields).fill(0).map((x, i) => {
|
||||
const name = "roomName" + i;
|
||||
return <Field
|
||||
key={name}
|
||||
name={name}
|
||||
type="text"
|
||||
label={_t("Room name")}
|
||||
placeholder={placeholders[i]}
|
||||
value={roomNames[i]}
|
||||
onChange={ev => setRoomName(i, ev.target.value)}
|
||||
autoFocus={i === 2}
|
||||
disabled={busy}
|
||||
autoComplete="off"
|
||||
/>;
|
||||
return (
|
||||
<Field
|
||||
key={name}
|
||||
name={name}
|
||||
type="text"
|
||||
label={_t("Room name")}
|
||||
placeholder={placeholders[i]}
|
||||
value={roomNames[i]}
|
||||
onChange={(ev) => setRoomName(i, ev.target.value)}
|
||||
autoFocus={i === 2}
|
||||
disabled={busy}
|
||||
autoComplete="off"
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const onNextClick = async (ev: ButtonEvent) => {
|
||||
@@ -313,22 +328,24 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||
setBusy(true);
|
||||
try {
|
||||
const isPublic = space.getJoinRule() === JoinRule.Public;
|
||||
const filteredRoomNames = roomNames.map(name => name.trim()).filter(Boolean);
|
||||
const roomIds = await Promise.all(filteredRoomNames.map(name => {
|
||||
return createRoom({
|
||||
createOpts: {
|
||||
preset: isPublic ? Preset.PublicChat : Preset.PrivateChat,
|
||||
name,
|
||||
},
|
||||
spinner: false,
|
||||
encryption: false,
|
||||
andView: false,
|
||||
inlineErrors: true,
|
||||
parentSpace: space,
|
||||
joinRule: !isPublic ? JoinRule.Restricted : undefined,
|
||||
suggested: true,
|
||||
});
|
||||
}));
|
||||
const filteredRoomNames = roomNames.map((name) => name.trim()).filter(Boolean);
|
||||
const roomIds = await Promise.all(
|
||||
filteredRoomNames.map((name) => {
|
||||
return createRoom({
|
||||
createOpts: {
|
||||
preset: isPublic ? Preset.PublicChat : Preset.PrivateChat,
|
||||
name,
|
||||
},
|
||||
spinner: false,
|
||||
encryption: false,
|
||||
andView: false,
|
||||
inlineErrors: true,
|
||||
parentSpace: space,
|
||||
joinRule: !isPublic ? JoinRule.Restricted : undefined,
|
||||
suggested: true,
|
||||
});
|
||||
}),
|
||||
);
|
||||
onFinished(roomIds[0]);
|
||||
} catch (e) {
|
||||
logger.error("Failed to create initial space rooms", e);
|
||||
@@ -342,55 +359,61 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
||||
onFinished();
|
||||
};
|
||||
let buttonLabel = _t("Skip for now");
|
||||
if (roomNames.some(name => name.trim())) {
|
||||
if (roomNames.some((name) => name.trim())) {
|
||||
onClick = onNextClick;
|
||||
buttonLabel = busy ? _t("Creating rooms...") : _t("Continue");
|
||||
}
|
||||
|
||||
return <div>
|
||||
<h1>{ title }</h1>
|
||||
<div className="mx_SpaceRoomView_description">{ description }</div>
|
||||
return (
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<div className="mx_SpaceRoomView_description">{description}</div>
|
||||
|
||||
{ error && <div className="mx_SpaceRoomView_errorText">{ error }</div> }
|
||||
<form onSubmit={onClick} id="mx_SpaceSetupFirstRooms">
|
||||
{ fields }
|
||||
</form>
|
||||
{error && <div className="mx_SpaceRoomView_errorText">{error}</div>}
|
||||
<form onSubmit={onClick} id="mx_SpaceSetupFirstRooms">
|
||||
{fields}
|
||||
</form>
|
||||
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
disabled={busy}
|
||||
onClick={onClick}
|
||||
element="input"
|
||||
type="submit"
|
||||
form="mx_SpaceSetupFirstRooms"
|
||||
value={buttonLabel}
|
||||
/>
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
disabled={busy}
|
||||
onClick={onClick}
|
||||
element="input"
|
||||
type="submit"
|
||||
form="mx_SpaceSetupFirstRooms"
|
||||
value={buttonLabel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
||||
return <div>
|
||||
<h1>{ _t("What do you want to organise?") }</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{ _t("Pick rooms or conversations to add. This is just a space for you, " +
|
||||
"no one will be informed. You can add more later.") }
|
||||
</div>
|
||||
return (
|
||||
<div>
|
||||
<h1>{_t("What do you want to organise?")}</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{_t(
|
||||
"Pick rooms or conversations to add. This is just a space for you, " +
|
||||
"no one will be informed. You can add more later.",
|
||||
)}
|
||||
</div>
|
||||
|
||||
<AddExistingToSpace
|
||||
space={space}
|
||||
emptySelectionButton={
|
||||
<AccessibleButton kind="primary" onClick={onFinished}>
|
||||
{ _t("Skip for now") }
|
||||
</AccessibleButton>
|
||||
}
|
||||
filterPlaceholder={_t("Search for rooms or spaces")}
|
||||
onFinished={onFinished}
|
||||
roomsRenderer={defaultRoomsRenderer}
|
||||
dmsRenderer={defaultDmsRenderer}
|
||||
/>
|
||||
</div>;
|
||||
<AddExistingToSpace
|
||||
space={space}
|
||||
emptySelectionButton={
|
||||
<AccessibleButton kind="primary" onClick={onFinished}>
|
||||
{_t("Skip for now")}
|
||||
</AccessibleButton>
|
||||
}
|
||||
filterPlaceholder={_t("Search for rooms or spaces")}
|
||||
onFinished={onFinished}
|
||||
roomsRenderer={defaultRoomsRenderer}
|
||||
dmsRenderer={defaultDmsRenderer}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface ISpaceSetupPublicShareProps extends Pick<IProps & IState, "justCreatedOpts" | "space" | "firstRoomId"> {
|
||||
@@ -398,56 +421,68 @@ interface ISpaceSetupPublicShareProps extends Pick<IProps & IState, "justCreated
|
||||
}
|
||||
|
||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId }: ISpaceSetupPublicShareProps) => {
|
||||
return <div className="mx_SpaceRoomView_publicShare">
|
||||
<h1>{ _t("Share %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
}) }</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{ _t("It's just you at the moment, it will be even better with others.") }
|
||||
</div>
|
||||
return (
|
||||
<div className="mx_SpaceRoomView_publicShare">
|
||||
<h1>
|
||||
{_t("Share %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
})}
|
||||
</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{_t("It's just you at the moment, it will be even better with others.")}
|
||||
</div>
|
||||
|
||||
<SpacePublicShare space={space} />
|
||||
<SpacePublicShare space={space} />
|
||||
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton kind="primary" onClick={onFinished}>
|
||||
{ firstRoomId ? _t("Go to my first room") : _t("Go to my space") }
|
||||
</AccessibleButton>
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton kind="primary" onClick={onFinished}>
|
||||
{firstRoomId ? _t("Go to my first room") : _t("Go to my space")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
|
||||
return <div className="mx_SpaceRoomView_privateScope">
|
||||
<h1>{ _t("Who are you working with?") }</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{ _t("Make sure the right people have access to %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
}) }
|
||||
</div>
|
||||
return (
|
||||
<div className="mx_SpaceRoomView_privateScope">
|
||||
<h1>{_t("Who are you working with?")}</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{_t("Make sure the right people have access to %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
})}
|
||||
</div>
|
||||
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_privateScope_justMeButton"
|
||||
onClick={() => { onFinished(false); }}
|
||||
>
|
||||
<h3>{ _t("Just me") }</h3>
|
||||
<div>{ _t("A private space to organise your rooms") }</div>
|
||||
</AccessibleButton>
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_privateScope_meAndMyTeammatesButton"
|
||||
onClick={() => { onFinished(true); }}
|
||||
>
|
||||
<h3>{ _t("Me and my teammates") }</h3>
|
||||
<div>{ _t("A private space for you and your teammates") }</div>
|
||||
</AccessibleButton>
|
||||
</div>;
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_privateScope_justMeButton"
|
||||
onClick={() => {
|
||||
onFinished(false);
|
||||
}}
|
||||
>
|
||||
<h3>{_t("Just me")}</h3>
|
||||
<div>{_t("A private space to organise your rooms")}</div>
|
||||
</AccessibleButton>
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_privateScope_meAndMyTeammatesButton"
|
||||
onClick={() => {
|
||||
onFinished(true);
|
||||
}}
|
||||
>
|
||||
<h3>{_t("Me and my teammates")}</h3>
|
||||
<div>{_t("A private space for you and your teammates")}</div>
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const validateEmailRules = withValidation({
|
||||
rules: [{
|
||||
key: "email",
|
||||
test: ({ value }) => !value || Email.looksValid(value),
|
||||
invalid: () => _t("Doesn't look like a valid email address"),
|
||||
}],
|
||||
rules: [
|
||||
{
|
||||
key: "email",
|
||||
test: ({ value }) => !value || Email.looksValid(value),
|
||||
invalid: () => _t("Doesn't look like a valid email address"),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||
@@ -458,19 +493,21 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||
const [emailAddresses, setEmailAddress] = useStateArray(numFields, "");
|
||||
const fields = new Array(numFields).fill(0).map((x, i) => {
|
||||
const name = "emailAddress" + i;
|
||||
return <Field
|
||||
key={name}
|
||||
name={name}
|
||||
type="text"
|
||||
label={_t("Email address")}
|
||||
placeholder={_t("Email")}
|
||||
value={emailAddresses[i]}
|
||||
onChange={ev => setEmailAddress(i, ev.target.value)}
|
||||
ref={fieldRefs[i]}
|
||||
onValidate={validateEmailRules}
|
||||
autoFocus={i === 0}
|
||||
disabled={busy}
|
||||
/>;
|
||||
return (
|
||||
<Field
|
||||
key={name}
|
||||
name={name}
|
||||
type="text"
|
||||
label={_t("Email address")}
|
||||
placeholder={_t("Email")}
|
||||
value={emailAddresses[i]}
|
||||
onChange={(ev) => setEmailAddress(i, ev.target.value)}
|
||||
ref={fieldRefs[i]}
|
||||
onValidate={validateEmailRules}
|
||||
autoFocus={i === 0}
|
||||
disabled={busy}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const onNextClick = async (ev) => {
|
||||
@@ -480,7 +517,8 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||
for (const fieldRef of fieldRefs) {
|
||||
const valid = await fieldRef.current.validate({ allowEmpty: true });
|
||||
|
||||
if (valid === false) { // true/null are allowed
|
||||
if (valid === false) {
|
||||
// true/null are allowed
|
||||
fieldRef.current.focus();
|
||||
fieldRef.current.validate({ allowEmpty: true, focused: true });
|
||||
return;
|
||||
@@ -488,16 +526,18 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||
}
|
||||
|
||||
setBusy(true);
|
||||
const targetIds = emailAddresses.map(name => name.trim()).filter(Boolean);
|
||||
const targetIds = emailAddresses.map((name) => name.trim()).filter(Boolean);
|
||||
try {
|
||||
const result = await inviteMultipleToRoom(space.roomId, targetIds);
|
||||
|
||||
const failedUsers = Object.keys(result.states).filter(a => result.states[a] === "error");
|
||||
const failedUsers = Object.keys(result.states).filter((a) => result.states[a] === "error");
|
||||
if (failedUsers.length > 0) {
|
||||
logger.log("Failed to invite users to space: ", result);
|
||||
setError(_t("Failed to invite the following users to your space: %(csvUsers)s", {
|
||||
csvUsers: failedUsers.join(", "),
|
||||
}));
|
||||
setError(
|
||||
_t("Failed to invite the following users to your space: %(csvUsers)s", {
|
||||
csvUsers: failedUsers.join(", "),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
onFinished();
|
||||
}
|
||||
@@ -513,53 +553,61 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||
onFinished();
|
||||
};
|
||||
let buttonLabel = _t("Skip for now");
|
||||
if (emailAddresses.some(name => name.trim())) {
|
||||
if (emailAddresses.some((name) => name.trim())) {
|
||||
onClick = onNextClick;
|
||||
buttonLabel = busy ? _t("Inviting...") : _t("Continue");
|
||||
}
|
||||
|
||||
return <div className="mx_SpaceRoomView_inviteTeammates">
|
||||
<h1>{ _t("Invite your teammates") }</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{ _t("Make sure the right people have access. You can invite more later.") }
|
||||
</div>
|
||||
return (
|
||||
<div className="mx_SpaceRoomView_inviteTeammates">
|
||||
<h1>{_t("Invite your teammates")}</h1>
|
||||
<div className="mx_SpaceRoomView_description">
|
||||
{_t("Make sure the right people have access. You can invite more later.")}
|
||||
</div>
|
||||
|
||||
<div className="mx_SpaceRoomView_inviteTeammates_betaDisclaimer">
|
||||
{ _t("<b>This is an experimental feature.</b> For now, " +
|
||||
"new users receiving an invite will have to open the invite on <link/> to actually join.", {}, {
|
||||
b: sub => <b>{ sub }</b>,
|
||||
link: () => <a href="https://app.element.io/" rel="noreferrer noopener" target="_blank">
|
||||
app.element.io
|
||||
</a>,
|
||||
}) }
|
||||
</div>
|
||||
<div className="mx_SpaceRoomView_inviteTeammates_betaDisclaimer">
|
||||
{_t(
|
||||
"<b>This is an experimental feature.</b> For now, " +
|
||||
"new users receiving an invite will have to open the invite on <link/> to actually join.",
|
||||
{},
|
||||
{
|
||||
b: (sub) => <b>{sub}</b>,
|
||||
link: () => (
|
||||
<a href="https://app.element.io/" rel="noreferrer noopener" target="_blank">
|
||||
app.element.io
|
||||
</a>
|
||||
),
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
|
||||
{ error && <div className="mx_SpaceRoomView_errorText">{ error }</div> }
|
||||
<form onSubmit={onClick} id="mx_SpaceSetupPrivateInvite">
|
||||
{ fields }
|
||||
</form>
|
||||
{error && <div className="mx_SpaceRoomView_errorText">{error}</div>}
|
||||
<form onSubmit={onClick} id="mx_SpaceSetupPrivateInvite">
|
||||
{fields}
|
||||
</form>
|
||||
|
||||
<div className="mx_SpaceRoomView_inviteTeammates_buttons">
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_inviteTeammates_inviteDialogButton"
|
||||
onClick={() => showRoomInviteDialog(space.roomId)}
|
||||
>
|
||||
{ _t("Invite by username") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
<div className="mx_SpaceRoomView_inviteTeammates_buttons">
|
||||
<AccessibleButton
|
||||
className="mx_SpaceRoomView_inviteTeammates_inviteDialogButton"
|
||||
onClick={() => showRoomInviteDialog(space.roomId)}
|
||||
>
|
||||
{_t("Invite by username")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
disabled={busy}
|
||||
onClick={onClick}
|
||||
element="input"
|
||||
type="submit"
|
||||
form="mx_SpaceSetupPrivateInvite"
|
||||
value={buttonLabel}
|
||||
/>
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
disabled={busy}
|
||||
onClick={onClick}
|
||||
element="input"
|
||||
type="submit"
|
||||
form="mx_SpaceSetupPrivateInvite"
|
||||
value={buttonLabel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
);
|
||||
};
|
||||
|
||||
export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
@@ -578,8 +626,10 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
const showSetup = this.props.justCreatedOpts && context.getUserId() === this.creator;
|
||||
|
||||
if (showSetup) {
|
||||
phase = this.props.justCreatedOpts.createOpts.preset === Preset.PublicChat
|
||||
? Phase.PublicCreateRooms : Phase.PrivateScope;
|
||||
phase =
|
||||
this.props.justCreatedOpts.createOpts.preset === Preset.PublicChat
|
||||
? Phase.PublicCreateRooms
|
||||
: Phase.PrivateScope;
|
||||
}
|
||||
|
||||
this.state = {
|
||||
@@ -667,76 +717,97 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||
if (this.state.myMembership === "join") {
|
||||
return <SpaceLanding space={this.props.space} />;
|
||||
} else {
|
||||
return <RoomPreviewCard
|
||||
room={this.props.space}
|
||||
onJoinButtonClicked={this.props.onJoinButtonClicked}
|
||||
onRejectButtonClicked={this.props.onRejectButtonClicked}
|
||||
/>;
|
||||
return (
|
||||
<RoomPreviewCard
|
||||
room={this.props.space}
|
||||
onJoinButtonClicked={this.props.onJoinButtonClicked}
|
||||
onRejectButtonClicked={this.props.onRejectButtonClicked}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case Phase.PublicCreateRooms:
|
||||
return <SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What are some things you want to discuss in %(spaceName)s?", {
|
||||
spaceName: this.props.justCreatedOpts?.createOpts?.name || this.props.space.name,
|
||||
})}
|
||||
description={<>
|
||||
{ _t("Let's create a room for each of them.") }
|
||||
<br />
|
||||
{ _t("You can add more later too, including already existing ones.") }
|
||||
</>}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.PublicShare, firstRoomId })}
|
||||
/>;
|
||||
return (
|
||||
<SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What are some things you want to discuss in %(spaceName)s?", {
|
||||
spaceName: this.props.justCreatedOpts?.createOpts?.name || this.props.space.name,
|
||||
})}
|
||||
description={
|
||||
<>
|
||||
{_t("Let's create a room for each of them.")}
|
||||
<br />
|
||||
{_t("You can add more later too, including already existing ones.")}
|
||||
</>
|
||||
}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.PublicShare, firstRoomId })}
|
||||
/>
|
||||
);
|
||||
case Phase.PublicShare:
|
||||
return <SpaceSetupPublicShare
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
space={this.props.space}
|
||||
onFinished={this.goToFirstRoom}
|
||||
firstRoomId={this.state.firstRoomId}
|
||||
/>;
|
||||
return (
|
||||
<SpaceSetupPublicShare
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
space={this.props.space}
|
||||
onFinished={this.goToFirstRoom}
|
||||
firstRoomId={this.state.firstRoomId}
|
||||
/>
|
||||
);
|
||||
|
||||
case Phase.PrivateScope:
|
||||
return <SpaceSetupPrivateScope
|
||||
space={this.props.space}
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
onFinished={(invite: boolean) => {
|
||||
this.setState({ phase: invite ? Phase.PrivateCreateRooms : Phase.PrivateExistingRooms });
|
||||
}}
|
||||
/>;
|
||||
return (
|
||||
<SpaceSetupPrivateScope
|
||||
space={this.props.space}
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
onFinished={(invite: boolean) => {
|
||||
this.setState({ phase: invite ? Phase.PrivateCreateRooms : Phase.PrivateExistingRooms });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case Phase.PrivateInvite:
|
||||
return <SpaceSetupPrivateInvite
|
||||
space={this.props.space}
|
||||
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||
/>;
|
||||
return (
|
||||
<SpaceSetupPrivateInvite
|
||||
space={this.props.space}
|
||||
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||
/>
|
||||
);
|
||||
case Phase.PrivateCreateRooms:
|
||||
return <SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What projects are your team working on?")}
|
||||
description={<>
|
||||
{ _t("We'll create rooms for each of them.") }
|
||||
<br />
|
||||
{ _t("You can add more later too, including already existing ones.") }
|
||||
</>}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.PrivateInvite, firstRoomId })}
|
||||
/>;
|
||||
return (
|
||||
<SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What projects are your team working on?")}
|
||||
description={
|
||||
<>
|
||||
{_t("We'll create rooms for each of them.")}
|
||||
<br />
|
||||
{_t("You can add more later too, including already existing ones.")}
|
||||
</>
|
||||
}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.PrivateInvite, firstRoomId })}
|
||||
/>
|
||||
);
|
||||
case Phase.PrivateExistingRooms:
|
||||
return <SpaceAddExistingRooms
|
||||
space={this.props.space}
|
||||
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||
/>;
|
||||
return (
|
||||
<SpaceAddExistingRooms
|
||||
space={this.props.space}
|
||||
onFinished={() => this.setState({ phase: Phase.Landing })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const rightPanel = this.state.showRightPanel && this.state.phase === Phase.Landing
|
||||
? <RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />
|
||||
: null;
|
||||
const rightPanel =
|
||||
this.state.showRightPanel && this.state.phase === Phase.Landing ? (
|
||||
<RightPanel room={this.props.space} resizeNotifier={this.props.resizeNotifier} />
|
||||
) : null;
|
||||
|
||||
return <main className="mx_SpaceRoomView">
|
||||
<ErrorBoundary>
|
||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||
{ this.renderBody() }
|
||||
</MainSplit>
|
||||
</ErrorBoundary>
|
||||
</main>;
|
||||
return (
|
||||
<main className="mx_SpaceRoomView">
|
||||
<ErrorBoundary>
|
||||
<MainSplit panel={rightPanel} resizeNotifier={this.props.resizeNotifier}>
|
||||
{this.renderBody()}
|
||||
</MainSplit>
|
||||
</ErrorBoundary>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user