Merge pull request #5933 from matrix-org/t3chguy/fix/17021
Spaces Beta release
This commit is contained in:
commit
c4118f1059
21 changed files with 521 additions and 33 deletions
|
@ -54,6 +54,7 @@
|
|||
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
|
||||
@import "./views/avatars/_PulsedAvatar.scss";
|
||||
@import "./views/avatars/_WidgetAvatar.scss";
|
||||
@import "./views/beta/_BetaCard.scss";
|
||||
@import "./views/context_menus/_CallContextMenu.scss";
|
||||
@import "./views/context_menus/_IconizedContextMenu.scss";
|
||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||
|
@ -237,6 +238,7 @@
|
|||
@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_LabsUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
|
||||
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
|
||||
|
|
|
@ -56,6 +56,12 @@ limitations under the License.
|
|||
.mx_GroupFilterPanel .mx_TagTile {
|
||||
// opacity: 0.5;
|
||||
position: relative;
|
||||
|
||||
.mx_BetaDot {
|
||||
position: absolute;
|
||||
right: -13px;
|
||||
top: -11px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_GroupFilterPanel .mx_TagTile.mx_TagTile_prototype {
|
||||
|
|
|
@ -17,6 +17,11 @@ limitations under the License.
|
|||
.mx_MyGroups {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.mx_BetaCard {
|
||||
margin: 0 72px;
|
||||
max-width: 760px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MyGroups .mx_RoomHeader_simpleHeader {
|
||||
|
@ -30,7 +35,7 @@ limitations under the License.
|
|||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.mx_MyGroups > :not(.mx_RoomHeader) {
|
||||
.mx_MyGroups > :not(.mx_RoomHeader):not(.mx_BetaCard) {
|
||||
max-width: 960px;
|
||||
margin: 40px;
|
||||
}
|
||||
|
|
|
@ -137,6 +137,44 @@ $SpaceRoomViewInnerWidth: 428px;
|
|||
box-sizing: border-box;
|
||||
box-shadow: 2px 15px 30px $dialog-shadow-color;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
|
||||
// XXX remove this when spaces leaves Beta
|
||||
.mx_BetaCard_betaPill {
|
||||
position: absolute;
|
||||
right: 24px;
|
||||
top: 32px;
|
||||
}
|
||||
// XXX remove this when spaces leaves Beta
|
||||
.mx_SpaceRoomView_preview_spaceBetaPrompt {
|
||||
font-weight: $font-semi-bold;
|
||||
font-size: $font-14px;
|
||||
line-height: $font-24px;
|
||||
color: $primary-fg-color;
|
||||
margin-top: 24px;
|
||||
position: relative;
|
||||
padding-left: 24px;
|
||||
|
||||
.mx_AccessibleButton_kind_link {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: $font-24px;
|
||||
width: 20px;
|
||||
left: 0;
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: contain;
|
||||
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
|
||||
background-color: $secondary-fg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_SpaceRoomView_preview_inviter {
|
||||
display: flex;
|
||||
|
@ -314,6 +352,23 @@ $SpaceRoomViewInnerWidth: 428px;
|
|||
}
|
||||
|
||||
.mx_SpaceRoomView_inviteTeammates {
|
||||
// XXX remove this when spaces leaves Beta
|
||||
.mx_SpaceRoomView_inviteTeammates_betaDisclaimer {
|
||||
padding: 58px 16px 16px;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
background-color: $header-panel-bg-color;
|
||||
max-width: $SpaceRoomViewInnerWidth;
|
||||
margin: 20px 0 30px;
|
||||
box-sizing: border-box;
|
||||
|
||||
.mx_BetaCard_betaPill {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_SpaceRoomView_inviteTeammates_buttons {
|
||||
color: $secondary-fg-color;
|
||||
margin-top: 28px;
|
||||
|
|
112
res/css/views/beta/_BetaCard.scss
Normal file
112
res/css/views/beta/_BetaCard.scss
Normal file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
.mx_BetaCard {
|
||||
margin-bottom: 20px;
|
||||
padding: 24px;
|
||||
background-color: $settings-profile-placeholder-bg-color;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
|
||||
> div {
|
||||
.mx_BetaCard_title {
|
||||
font-weight: $font-semi-bold;
|
||||
font-size: $font-18px;
|
||||
line-height: $font-22px;
|
||||
color: $primary-fg-color;
|
||||
margin: 4px 0 14px;
|
||||
|
||||
.mx_BetaCard_betaPill {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_BetaCard_caption {
|
||||
font-size: $font-15px;
|
||||
line-height: $font-20px;
|
||||
color: $secondary-fg-color;
|
||||
}
|
||||
|
||||
.mx_AccessibleButton {
|
||||
display: block;
|
||||
margin: 20px 0;
|
||||
padding: 12px 40px;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.mx_BetaCard_disclaimer {
|
||||
font-size: $font-12px;
|
||||
line-height: $font-15px;
|
||||
color: $secondary-fg-color;
|
||||
}
|
||||
}
|
||||
|
||||
> img {
|
||||
margin: auto 0 auto 20px;
|
||||
width: 300px;
|
||||
object-fit: contain;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_BetaCard_betaPill {
|
||||
background-color: $accent-color-alt;
|
||||
padding: 4px 10px;
|
||||
border-radius: 8px;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: #FFFFFF;
|
||||
display: inline-block;
|
||||
vertical-align: text-bottom;
|
||||
|
||||
&.mx_BetaCard_betaPill_clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
$pulse-color: $accent-color-alt;
|
||||
$dot-size: 12px;
|
||||
|
||||
.mx_BetaDot {
|
||||
border-radius: 50%;
|
||||
margin: 10px;
|
||||
height: $dot-size;
|
||||
width: $dot-size;
|
||||
transform: scale(1);
|
||||
background: rgba($pulse-color, 1);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
|
||||
animation: mx_Beta_bluePulse 2s infinite;
|
||||
animation-iteration-count: 20;
|
||||
}
|
||||
|
||||
@keyframes mx_Beta_bluePulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba($pulse-color, 0);
|
||||
}
|
||||
}
|
25
res/css/views/settings/tabs/user/_LabsUserSettingsTab.scss
Normal file
25
res/css/views/settings/tabs/user/_LabsUserSettingsTab.scss
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright 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.
|
||||
*/
|
||||
|
||||
.mx_LabsUserSettingsTab {
|
||||
.mx_SettingsTab_section {
|
||||
margin-top: 32px;
|
||||
|
||||
.mx_SettingsFlag {
|
||||
margin-right: 0; // remove right margin to align with beta cards
|
||||
}
|
||||
}
|
||||
}
|
|
@ -29,6 +29,7 @@ $spacePanelWidth: 71px;
|
|||
width: 480px;
|
||||
box-sizing: border-box;
|
||||
background-color: $primary-bg-color;
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
> h2 {
|
||||
|
@ -44,6 +45,13 @@ $spacePanelWidth: 71px;
|
|||
}
|
||||
}
|
||||
|
||||
// XXX remove this when spaces leaves Beta
|
||||
.mx_BetaCard_betaPill {
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
right: 24px;
|
||||
}
|
||||
|
||||
.mx_SpaceCreateMenuType {
|
||||
@mixin SpacePillButton;
|
||||
}
|
||||
|
|
BIN
res/img/betas/spaces.png
Normal file
BIN
res/img/betas/spaces.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 MiB |
|
@ -123,12 +123,19 @@ class GroupFilterPanel extends React.Component {
|
|||
mx_GroupFilterPanel_items_selected: itemsSelected,
|
||||
});
|
||||
|
||||
let betaDot;
|
||||
if (SettingsStore.getBetaInfo("feature_spaces") && !localStorage.getItem("mx_seenSpacesBeta")) {
|
||||
betaDot = <div className="mx_BetaDot" />;
|
||||
}
|
||||
|
||||
let createButton = (
|
||||
<ActionButton
|
||||
tooltip
|
||||
label={_t("Communities")}
|
||||
action="toggle_my_groups"
|
||||
className="mx_TagTile mx_TagTile_plus" />
|
||||
className="mx_TagTile mx_TagTile_plus">
|
||||
{ betaDot }
|
||||
</ActionButton>
|
||||
);
|
||||
|
||||
if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
|
||||
|
|
|
@ -740,6 +740,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
this.showScreenAfterLogin();
|
||||
break;
|
||||
case 'toggle_my_groups':
|
||||
// persist that the user has interacted with this, use it to dismiss the beta dot
|
||||
localStorage.setItem("mx_seenSpacesBeta", "1");
|
||||
// We just dispatch the page change rather than have to worry about
|
||||
// what the logic is for each of these branches.
|
||||
if (this.state.page_type === PageTypes.MyGroups) {
|
||||
|
@ -1684,6 +1686,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
const type = screen === "start_sso" ? "sso" : "cas";
|
||||
PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin());
|
||||
} else if (screen === 'groups') {
|
||||
if (SettingsStore.getValue("feature_spaces")) {
|
||||
dis.dispatch({ action: "view_home_page" });
|
||||
return;
|
||||
}
|
||||
dis.dispatch({
|
||||
action: 'view_my_groups',
|
||||
});
|
||||
|
@ -1767,6 +1773,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
subAction: params.action,
|
||||
});
|
||||
} else if (screen.indexOf('group/') === 0) {
|
||||
if (SettingsStore.getValue("feature_spaces")) {
|
||||
dis.dispatch({ action: "view_home_page" });
|
||||
return;
|
||||
}
|
||||
|
||||
const groupId = screen.substring(6);
|
||||
|
||||
// TODO: Check valid group ID
|
||||
|
|
|
@ -25,6 +25,7 @@ import AccessibleButton from '../views/elements/AccessibleButton';
|
|||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||
import BetaCard from "../views/beta/BetaCard";
|
||||
|
||||
@replaceableComponent("structures.MyGroups")
|
||||
export default class MyGroups extends React.Component {
|
||||
|
@ -139,6 +140,7 @@ export default class MyGroups extends React.Component {
|
|||
</div>
|
||||
</div>*/}
|
||||
</div>
|
||||
<BetaCard featureId="feature_spaces" title={_t("Communities are changing to Spaces")} />
|
||||
<div className="mx_MyGroups_content">
|
||||
{ contentHeader }
|
||||
{ content }
|
||||
|
|
|
@ -1917,7 +1917,7 @@ export default class RoomView extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
if (SettingsStore.getValue("feature_spaces") && this.state.room?.isSpaceRoom()) {
|
||||
if (this.state.room?.isSpaceRoom()) {
|
||||
return <SpaceRoomView
|
||||
space={this.state.room}
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
|
|
|
@ -60,6 +60,10 @@ import IconizedContextMenu, {
|
|||
IconizedContextMenuOptionList,
|
||||
} from "../views/context_menus/IconizedContextMenu";
|
||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||
import {BetaPill} from "../views/beta/BetaCard";
|
||||
import {USER_LABS_TAB} from "../views/dialogs/UserSettingsDialog";
|
||||
import SettingsStore from "../../settings/SettingsStore";
|
||||
import dis from "../../dispatcher/dispatcher";
|
||||
|
||||
interface IProps {
|
||||
space: Room;
|
||||
|
@ -136,15 +140,39 @@ const SpaceInfo = ({ space }) => {
|
|||
</div>
|
||||
};
|
||||
|
||||
const onBetaClick = () => {
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: USER_LABS_TAB,
|
||||
});
|
||||
};
|
||||
|
||||
const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => {
|
||||
const cli = useContext(MatrixClientContext);
|
||||
const myMembership = useMyRoomMembership(space);
|
||||
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
||||
|
||||
let inviterSection;
|
||||
let joinButtons;
|
||||
if (myMembership === "invite") {
|
||||
if (myMembership === "join") {
|
||||
// XXX remove this when spaces leaves Beta
|
||||
joinButtons = (
|
||||
<AccessibleButton
|
||||
kind="danger_outline"
|
||||
onClick={() => {
|
||||
dis.dispatch({
|
||||
action: "leave_room",
|
||||
room_id: space.roomId,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{ _t("Leave") }
|
||||
</AccessibleButton>
|
||||
);
|
||||
} else if (myMembership === "invite") {
|
||||
const inviteSender = space.getMember(cli.getUserId())?.events.member?.getSender();
|
||||
const inviter = inviteSender && space.getMember(inviteSender);
|
||||
|
||||
|
@ -180,6 +208,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
|||
setBusy(true);
|
||||
onJoinButtonClicked();
|
||||
}}
|
||||
disabled={!spacesEnabled}
|
||||
>
|
||||
{ _t("Accept") }
|
||||
</AccessibleButton>
|
||||
|
@ -192,10 +221,11 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
|||
setBusy(true);
|
||||
onJoinButtonClicked();
|
||||
}}
|
||||
disabled={!spacesEnabled}
|
||||
>
|
||||
{ _t("Join") }
|
||||
</AccessibleButton>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (busy) {
|
||||
|
@ -203,6 +233,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
|||
}
|
||||
|
||||
return <div className="mx_SpaceRoomView_preview">
|
||||
<BetaPill onClick={onBetaClick} />
|
||||
{ inviterSection }
|
||||
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
||||
<h1 className="mx_SpaceRoomView_preview_name">
|
||||
|
@ -220,6 +251,20 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
|||
<div className="mx_SpaceRoomView_preview_joinButtons">
|
||||
{ joinButtons }
|
||||
</div>
|
||||
{ !spacesEnabled && <div className="mx_SpaceRoomView_preview_spaceBetaPrompt">
|
||||
{ myMembership === "join"
|
||||
? _t("To view %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
||||
spaceName: space.name,
|
||||
}, {
|
||||
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
||||
})
|
||||
: _t("To join %(spaceName)s, turn on the <a>Spaces beta</a>", {
|
||||
spaceName: space.name,
|
||||
}, {
|
||||
a: sub => <AccessibleButton onClick={onBetaClick} kind="link">{ sub }</AccessibleButton>,
|
||||
})
|
||||
}
|
||||
</div> }
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
@ -513,9 +558,11 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
|||
</div>;
|
||||
};
|
||||
|
||||
const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished }) => {
|
||||
return <div className="mx_SpaceRoomView_publicShare">
|
||||
<h1>{ _t("Share %(name)s", { name: space.name }) }</h1>
|
||||
<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>
|
||||
|
@ -530,11 +577,13 @@ const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
|||
</div>;
|
||||
};
|
||||
|
||||
const SpaceSetupPrivateScope = ({ space, onFinished }) => {
|
||||
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: space.name }) }
|
||||
{ _t("Make sure the right people have access to %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
}) }
|
||||
</div>
|
||||
|
||||
<AccessibleButton
|
||||
|
@ -636,6 +685,17 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
|||
{ _t("Make sure the right people have access. You can invite more later.") }
|
||||
</div>
|
||||
|
||||
<div className="mx_SpaceRoomView_inviteTeammates_betaDisclaimer">
|
||||
<BetaPill onClick={onBetaClick} />
|
||||
{ _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 }
|
||||
|
@ -781,7 +841,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
private renderBody() {
|
||||
switch (this.state.phase) {
|
||||
case Phase.Landing:
|
||||
if (this.state.myMembership === "join") {
|
||||
if (this.state.myMembership === "join" && SettingsStore.getValue("feature_spaces")) {
|
||||
return <SpaceLanding space={this.props.space} />;
|
||||
} else {
|
||||
return <SpacePreview
|
||||
|
@ -794,7 +854,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
return <SpaceSetupFirstRooms
|
||||
space={this.props.space}
|
||||
title={_t("What are some things you want to discuss in %(spaceName)s?", {
|
||||
spaceName: this.props.space.name,
|
||||
spaceName: this.props.justCreatedOpts?.createOpts?.name || this.props.space.name,
|
||||
})}
|
||||
description={
|
||||
_t("Let's create a room for each of them.") + "\n" +
|
||||
|
@ -803,11 +863,16 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
onFinished={() => this.setState({ phase: Phase.PublicShare })}
|
||||
/>;
|
||||
case Phase.PublicShare:
|
||||
return <SpaceSetupPublicShare space={this.props.space} onFinished={this.goToFirstRoom} />;
|
||||
return <SpaceSetupPublicShare
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
space={this.props.space}
|
||||
onFinished={this.goToFirstRoom}
|
||||
/>;
|
||||
|
||||
case Phase.PrivateScope:
|
||||
return <SpaceSetupPrivateScope
|
||||
space={this.props.space}
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
onFinished={(invite: boolean) => {
|
||||
this.setState({ phase: invite ? Phase.PrivateInvite : Phase.PrivateExistingRooms });
|
||||
}}
|
||||
|
|
90
src/components/views/beta/BetaCard.tsx
Normal file
90
src/components/views/beta/BetaCard.tsx
Normal file
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 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 React from "react";
|
||||
import classNames from "classnames";
|
||||
|
||||
import {_t} from "../../../languageHandler";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import {SettingLevel} from "../../../settings/SettingLevel";
|
||||
import TextWithTooltip from "../elements/TextWithTooltip";
|
||||
|
||||
interface IProps {
|
||||
title?: string;
|
||||
featureId: string;
|
||||
}
|
||||
|
||||
export const BetaPill = ({ onClick }: { onClick?: () => void }) => {
|
||||
if (onClick) {
|
||||
return <TextWithTooltip
|
||||
class={classNames("mx_BetaCard_betaPill", {
|
||||
mx_BetaCard_betaPill_clickable: !!onClick,
|
||||
})}
|
||||
tooltip={<div>
|
||||
<div className="mx_Tooltip_title">
|
||||
{ _t("Spaces is a beta feature") }
|
||||
</div>
|
||||
<div className="mx_Tooltip_sub">
|
||||
{ _t("Tap for more info") }
|
||||
</div>
|
||||
</div>}
|
||||
onClick={onClick}
|
||||
tooltipProps={{ yOffset: -10 }}
|
||||
>
|
||||
{ _t("Beta") }
|
||||
</TextWithTooltip>;
|
||||
}
|
||||
|
||||
return <span
|
||||
className={classNames("mx_BetaCard_betaPill", {
|
||||
mx_BetaCard_betaPill_clickable: !!onClick,
|
||||
})}
|
||||
onClick={onClick}
|
||||
>
|
||||
{ _t("Beta") }
|
||||
</span>;
|
||||
};
|
||||
|
||||
const BetaCard = ({ title: titleOverride, featureId }: IProps) => {
|
||||
const info = SettingsStore.getBetaInfo(featureId);
|
||||
if (!info) return null; // Beta is invalid/disabled
|
||||
|
||||
const { title, caption, disclaimer, image } = info;
|
||||
const value = SettingsStore.getValue(featureId);
|
||||
|
||||
return <div className="mx_BetaCard">
|
||||
<div>
|
||||
<h3 className="mx_BetaCard_title">
|
||||
{ titleOverride || _t(title) }
|
||||
<BetaPill />
|
||||
</h3>
|
||||
<span className="mx_BetaCard_caption">{ _t(caption) }</span>
|
||||
<AccessibleButton
|
||||
onClick={() => SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)}
|
||||
kind="primary"
|
||||
>
|
||||
{ value ? _t("Leave the beta") : _t("Join the beta") }
|
||||
</AccessibleButton>
|
||||
{ disclaimer && <div className="mx_BetaCard_disclaimer">
|
||||
{ disclaimer(value) }
|
||||
</div> }
|
||||
</div>
|
||||
<img src={image} alt="" />
|
||||
</div>;
|
||||
};
|
||||
|
||||
export default BetaCard;
|
|
@ -125,7 +125,10 @@ export default class UserSettingsDialog extends React.Component {
|
|||
"mx_UserSettingsDialog_securityIcon",
|
||||
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
||||
));
|
||||
if (SdkConfig.get()['showLabsSettings']) {
|
||||
// Show the Labs tab if enabled or if there are any active betas
|
||||
if (SdkConfig.get()['showLabsSettings']
|
||||
|| SettingsStore.getFeatureSettingNames().some(k => SettingsStore.getBetaInfo(k))
|
||||
) {
|
||||
tabs.push(new Tab(
|
||||
USER_LABS_TAB,
|
||||
_td("Labs"),
|
||||
|
|
|
@ -32,6 +32,7 @@ export default class ActionButton extends React.Component {
|
|||
label: PropTypes.string.isRequired,
|
||||
iconPath: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
|
@ -79,7 +80,8 @@ export default class ActionButton extends React.Component {
|
|||
}
|
||||
|
||||
return (
|
||||
<AccessibleButton className={classNames.join(" ")}
|
||||
<AccessibleButton
|
||||
className={classNames.join(" ")}
|
||||
onClick={this._onClick}
|
||||
onMouseEnter={this._onMouseEnter}
|
||||
onMouseLeave={this._onMouseLeave}
|
||||
|
@ -87,6 +89,7 @@ export default class ActionButton extends React.Component {
|
|||
>
|
||||
{ icon }
|
||||
{ tooltip }
|
||||
{ this.props.children }
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
|||
import * as sdk from "../../../../../index";
|
||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import BetaCard from "../../../beta/BetaCard";
|
||||
|
||||
export class LabsSettingToggle extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -48,14 +50,40 @@ export default class LabsUserSettingsTab extends React.Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const features = SettingsStore.getFeatureSettingNames();
|
||||
const [labs, betas] = features.reduce((arr, f) => {
|
||||
arr[SettingsStore.getBetaInfo(f) ? 1 : 0].push(f);
|
||||
return arr;
|
||||
}, [[], []]);
|
||||
|
||||
let betaSection;
|
||||
if (betas.length) {
|
||||
betaSection = <div className="mx_SettingsTab_section">
|
||||
{ betas.map(f => <BetaCard key={f} featureId={f} /> ) }
|
||||
</div>;
|
||||
}
|
||||
|
||||
let labsSection;
|
||||
if (SdkConfig.get()['showLabsSettings']) {
|
||||
const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag");
|
||||
const flags = SettingsStore.getFeatureSettingNames().map(f => <LabsSettingToggle featureId={f} key={f} />);
|
||||
const flags = labs.map(f => <LabsSettingToggle featureId={f} key={f} />);
|
||||
|
||||
labsSection = <div className="mx_SettingsTab_section">
|
||||
{flags}
|
||||
<SettingsFlag name="enableWidgetScreenshots" level={SettingLevel.ACCOUNT} />
|
||||
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name="lowBandwidth" level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name="advancedRoomListLogging" level={SettingLevel.DEVICE} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab">
|
||||
<div className="mx_SettingsTab mx_LabsUserSettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{_t("Labs")}</div>
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
{
|
||||
_t('Customise your experience with experimental labs features. ' +
|
||||
_t('Feeling experimental? Labs are the best way to get things early, ' +
|
||||
'test out new features and help shape them before they actually launch. ' +
|
||||
'<a>Learn more</a>.', {}, {
|
||||
'a': (sub) => {
|
||||
return <a href="https://github.com/vector-im/element-web/blob/develop/docs/labs.md"
|
||||
|
@ -64,13 +92,8 @@ export default class LabsUserSettingsTab extends React.Component {
|
|||
})
|
||||
}
|
||||
</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
{flags}
|
||||
<SettingsFlag name={"enableWidgetScreenshots"} level={SettingLevel.ACCOUNT} />
|
||||
<SettingsFlag name={"showHiddenEventsInTimeline"} level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name={"lowBandwidth"} level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name={"advancedRoomListLogging"} level={SettingLevel.DEVICE} />
|
||||
</div>
|
||||
{ betaSection }
|
||||
{ labsSection }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
import React, {useContext, useRef, useState} from "react";
|
||||
import classNames from "classnames";
|
||||
import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types/event";
|
||||
import FocusLock from "react-focus-lock";
|
||||
|
||||
import {_t} from "../../../languageHandler";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
|
@ -25,7 +26,10 @@ import createRoom, {IStateEvent, Preset} from "../../../createRoom";
|
|||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||
import {SpaceAvatar} from "./SpaceBasicSettings";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import FocusLock from "react-focus-lock";
|
||||
import {BetaPill} from "../beta/BetaCard";
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
import {USER_LABS_TAB} from "../dialogs/UserSettingsDialog";
|
||||
import Field from "../elements/Field";
|
||||
import withValidation from "../elements/Validation";
|
||||
|
||||
|
@ -131,7 +135,7 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
|||
if (visibility === null) {
|
||||
body = <React.Fragment>
|
||||
<h2>{ _t("Create a space") }</h2>
|
||||
<p>{ _t("Spaces are new ways to group rooms and people. " +
|
||||
<p>{ _t("Spaces are a new way to group rooms and people. " +
|
||||
"To join an existing space you'll need an invite.") }</p>
|
||||
|
||||
<SpaceCreateMenuType
|
||||
|
@ -209,6 +213,13 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
|||
managed={false}
|
||||
>
|
||||
<FocusLock returnFocus={true}>
|
||||
<BetaPill onClick={() => {
|
||||
onFinished();
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: USER_LABS_TAB,
|
||||
});
|
||||
}} />
|
||||
{ body }
|
||||
</FocusLock>
|
||||
</ContextMenu>;
|
||||
|
|
|
@ -785,6 +785,13 @@
|
|||
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||
"Change notification settings": "Change notification settings",
|
||||
"Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.": "Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. Requires compatible homeserver for some features.",
|
||||
"Spaces": "Spaces",
|
||||
"Spaces are a new way to group rooms and people.": "Spaces are a new way to group rooms and people.",
|
||||
"%(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "%(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.",
|
||||
"Beta available for web, desktop and Android. Thank you for trying the beta.": "Beta available for web, desktop and Android. Thank you for trying the beta.",
|
||||
"%(brand)s will reload with Spaces enabled. Communities and custom tags will be hidden.": "%(brand)s will reload with Spaces enabled. Communities and custom tags will be hidden.",
|
||||
"You can leave the beta any time from settings or tapping on a beta badge, like the one above.": "You can leave the beta any time from settings or tapping on a beta badge, like the one above.",
|
||||
"Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.",
|
||||
"Show options to enable 'Do not disturb' mode": "Show options to enable 'Do not disturb' mode",
|
||||
"Send and receive voice messages (in development)": "Send and receive voice messages (in development)",
|
||||
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
||||
|
@ -998,7 +1005,7 @@
|
|||
"Description": "Description",
|
||||
"Please enter a name for the space": "Please enter a name for the space",
|
||||
"Create a space": "Create a space",
|
||||
"Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.",
|
||||
"Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.": "Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.",
|
||||
"Public": "Public",
|
||||
"Open space for anyone, best for communities": "Open space for anyone, best for communities",
|
||||
"Private": "Private",
|
||||
|
@ -1259,7 +1266,7 @@
|
|||
"Copy": "Copy",
|
||||
"Clear cache and reload": "Clear cache and reload",
|
||||
"Labs": "Labs",
|
||||
"Customise your experience with experimental labs features. <a>Learn more</a>.": "Customise your experience with experimental labs features. <a>Learn more</a>.",
|
||||
"Feeling experimental? Labs are the best way to get things early, test out new features and help shape them before they actually launch. <a>Learn more</a>.": "Feeling experimental? Labs are the best way to get things early, test out new features and help shape them before they actually launch. <a>Learn more</a>.",
|
||||
"Ignored/Blocked": "Ignored/Blocked",
|
||||
"Error adding ignored user/server": "Error adding ignored user/server",
|
||||
"Something went wrong. Please try again or view your console for hints.": "Something went wrong. Please try again or view your console for hints.",
|
||||
|
@ -2033,7 +2040,6 @@
|
|||
"%(networkName)s rooms": "%(networkName)s rooms",
|
||||
"Matrix rooms": "Matrix rooms",
|
||||
"Filter your rooms and spaces": "Filter your rooms and spaces",
|
||||
"Spaces": "Spaces",
|
||||
"Direct Messages": "Direct Messages",
|
||||
"Space selection": "Space selection",
|
||||
"Add existing rooms": "Add existing rooms",
|
||||
|
@ -2466,6 +2472,11 @@
|
|||
"Revoke permissions": "Revoke permissions",
|
||||
"Move left": "Move left",
|
||||
"Move right": "Move right",
|
||||
"Spaces is a beta feature": "Spaces is a beta feature",
|
||||
"Tap for more info": "Tap for more info",
|
||||
"Beta": "Beta",
|
||||
"Leave the beta": "Leave the beta",
|
||||
"Join the beta": "Join the beta",
|
||||
"Avatar": "Avatar",
|
||||
"This room is public": "This room is public",
|
||||
"Away": "Away",
|
||||
|
@ -2599,6 +2610,7 @@
|
|||
"Error whilst fetching joined communities": "Error whilst fetching joined communities",
|
||||
"Create a new community": "Create a new community",
|
||||
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
|
||||
"Communities are changing to Spaces": "Communities are changing to Spaces",
|
||||
"You’re all caught up": "You’re all caught up",
|
||||
"You have no visible notifications.": "You have no visible notifications.",
|
||||
"%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.",
|
||||
|
@ -2672,6 +2684,8 @@
|
|||
"Public space": "Public space",
|
||||
"Private space": "Private space",
|
||||
"<inviter/> invites you": "<inviter/> invites you",
|
||||
"To view %(spaceName)s, turn on the <a>Spaces beta</a>": "To view %(spaceName)s, turn on the <a>Spaces beta</a>",
|
||||
"To join %(spaceName)s, turn on the <a>Spaces beta</a>": "To join %(spaceName)s, turn on the <a>Spaces beta</a>",
|
||||
"Welcome to <name/>": "Welcome to <name/>",
|
||||
"Random": "Random",
|
||||
"Support": "Support",
|
||||
|
@ -2696,6 +2710,7 @@
|
|||
"Inviting...": "Inviting...",
|
||||
"Invite your teammates": "Invite your teammates",
|
||||
"Make sure the right people have access. You can invite more later.": "Make sure the right people have access. You can invite more later.",
|
||||
"<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>This is an experimental feature.</b> For now, new users receiving an invite will have to open the invite on <link/> to actually join.",
|
||||
"Invite by username": "Invite by username",
|
||||
"What are some things you want to discuss in %(spaceName)s?": "What are some things you want to discuss in %(spaceName)s?",
|
||||
"Let's create a room for each of them.": "Let's create a room for each of them.",
|
||||
|
|
|
@ -16,8 +16,9 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||
import React, { ReactNode } from "react";
|
||||
|
||||
import { _td } from '../languageHandler';
|
||||
import { _t, _td } from '../languageHandler';
|
||||
import {
|
||||
NotificationBodyEnabledController,
|
||||
NotificationsEnabledController,
|
||||
|
@ -39,6 +40,7 @@ import { OrderedMultiController } from "./controllers/OrderedMultiController";
|
|||
import { Layout } from "./Layout";
|
||||
import ReducedMotionController from './controllers/ReducedMotionController';
|
||||
import IncompatibleController from "./controllers/IncompatibleController";
|
||||
import SdkConfig from "../SdkConfig";
|
||||
|
||||
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
||||
const LEVELS_ROOM_SETTINGS = [
|
||||
|
@ -117,6 +119,13 @@ export interface ISetting {
|
|||
// historical settings which we don't want existing user's values be wiped. Do
|
||||
// not use this for new settings.
|
||||
invertedSettingName?: string;
|
||||
|
||||
betaInfo?: {
|
||||
title: string; // _td
|
||||
caption: string; // _td
|
||||
disclaimer?: (enabled: boolean) => ReactNode;
|
||||
image: string; // require(...)
|
||||
};
|
||||
}
|
||||
|
||||
export const SETTINGS: {[setting: string]: ISetting} = {
|
||||
|
@ -127,6 +136,33 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
supportedLevels: LEVELS_FEATURE,
|
||||
default: false,
|
||||
controller: new ReloadOnChangeController(),
|
||||
betaInfo: {
|
||||
title: _td("Spaces"),
|
||||
caption: _td("Spaces are a new way to group rooms and people."),
|
||||
disclaimer: (enabled) => {
|
||||
if (enabled) {
|
||||
return <>
|
||||
<p>{ _t("%(brand)s will reload with Spaces disabled. " +
|
||||
"Communities and custom tags will be visible again.", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}) }</p>
|
||||
<p>{ _t("Beta available for web, desktop and Android. Thank you for trying the beta.") }</p>
|
||||
</>;
|
||||
}
|
||||
|
||||
return <>
|
||||
<p>{ _t("%(brand)s will reload with Spaces enabled. " +
|
||||
"Communities and custom tags will be hidden.", {
|
||||
brand: SdkConfig.get().brand,
|
||||
}) }</p>
|
||||
<b>{ _t("You can leave the beta any time from settings or tapping on a beta badge, " +
|
||||
"like the one above.") }</b>
|
||||
<p>{ _t("Beta available for web, desktop and Android. " +
|
||||
"Some features may be unavailable on your homeserver.") }</p>
|
||||
</>;
|
||||
},
|
||||
image: require("../../res/img/betas/spaces.png"),
|
||||
},
|
||||
},
|
||||
"feature_dnd": {
|
||||
isFeature: true,
|
|
@ -257,6 +257,15 @@ export default class SettingsStore {
|
|||
return SETTINGS[settingName].isFeature;
|
||||
}
|
||||
|
||||
public static getBetaInfo(settingName: string) {
|
||||
// consider a beta disabled if the config is explicitly set to false, in which case treat as normal Labs flag
|
||||
if (SettingsStore.isFeature(settingName)
|
||||
&& SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, null, true, true) !== false
|
||||
) {
|
||||
return SETTINGS[settingName]?.betaInfo;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a setting is enabled.
|
||||
* If a setting is disabled then it should be hidden from the user.
|
||||
|
@ -445,8 +454,8 @@ export default class SettingsStore {
|
|||
throw new Error("Setting '" + settingName + "' does not appear to be a setting.");
|
||||
}
|
||||
|
||||
// When features are specified in the config.json, we force them as enabled or disabled.
|
||||
if (SettingsStore.isFeature(settingName)) {
|
||||
// When non-beta features are specified in the config.json, we force them as enabled or disabled.
|
||||
if (SettingsStore.isFeature(settingName) && !SETTINGS[settingName]?.betaInfo) {
|
||||
const configVal = SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, roomId, true, true);
|
||||
if (configVal === true || configVal === false) return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue