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/_MemberStatusMessageAvatar.scss";
|
||||||
@import "./views/avatars/_PulsedAvatar.scss";
|
@import "./views/avatars/_PulsedAvatar.scss";
|
||||||
@import "./views/avatars/_WidgetAvatar.scss";
|
@import "./views/avatars/_WidgetAvatar.scss";
|
||||||
|
@import "./views/beta/_BetaCard.scss";
|
||||||
@import "./views/context_menus/_CallContextMenu.scss";
|
@import "./views/context_menus/_CallContextMenu.scss";
|
||||||
@import "./views/context_menus/_IconizedContextMenu.scss";
|
@import "./views/context_menus/_IconizedContextMenu.scss";
|
||||||
@import "./views/context_menus/_MessageContextMenu.scss";
|
@import "./views/context_menus/_MessageContextMenu.scss";
|
||||||
|
@ -237,6 +238,7 @@
|
||||||
@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/user/_HelpUserSettingsTab.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/_MjolnirUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
|
||||||
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
|
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";
|
||||||
|
|
|
@ -56,6 +56,12 @@ limitations under the License.
|
||||||
.mx_GroupFilterPanel .mx_TagTile {
|
.mx_GroupFilterPanel .mx_TagTile {
|
||||||
// opacity: 0.5;
|
// opacity: 0.5;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
.mx_BetaDot {
|
||||||
|
position: absolute;
|
||||||
|
right: -13px;
|
||||||
|
top: -11px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupFilterPanel .mx_TagTile.mx_TagTile_prototype {
|
.mx_GroupFilterPanel .mx_TagTile.mx_TagTile_prototype {
|
||||||
|
|
|
@ -17,6 +17,11 @@ limitations under the License.
|
||||||
.mx_MyGroups {
|
.mx_MyGroups {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
|
.mx_BetaCard {
|
||||||
|
margin: 0 72px;
|
||||||
|
max-width: 760px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MyGroups .mx_RoomHeader_simpleHeader {
|
.mx_MyGroups .mx_RoomHeader_simpleHeader {
|
||||||
|
@ -30,7 +35,7 @@ limitations under the License.
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MyGroups > :not(.mx_RoomHeader) {
|
.mx_MyGroups > :not(.mx_RoomHeader):not(.mx_BetaCard) {
|
||||||
max-width: 960px;
|
max-width: 960px;
|
||||||
margin: 40px;
|
margin: 40px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,6 +137,44 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
box-shadow: 2px 15px 30px $dialog-shadow-color;
|
box-shadow: 2px 15px 30px $dialog-shadow-color;
|
||||||
border-radius: 8px;
|
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 {
|
.mx_SpaceRoomView_preview_inviter {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -314,6 +352,23 @@ $SpaceRoomViewInnerWidth: 428px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SpaceRoomView_inviteTeammates {
|
.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 {
|
.mx_SpaceRoomView_inviteTeammates_buttons {
|
||||||
color: $secondary-fg-color;
|
color: $secondary-fg-color;
|
||||||
margin-top: 28px;
|
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;
|
width: 480px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background-color: $primary-bg-color;
|
background-color: $primary-bg-color;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
> div {
|
> div {
|
||||||
> h2 {
|
> 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 {
|
.mx_SpaceCreateMenuType {
|
||||||
@mixin SpacePillButton;
|
@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,
|
mx_GroupFilterPanel_items_selected: itemsSelected,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let betaDot;
|
||||||
|
if (SettingsStore.getBetaInfo("feature_spaces") && !localStorage.getItem("mx_seenSpacesBeta")) {
|
||||||
|
betaDot = <div className="mx_BetaDot" />;
|
||||||
|
}
|
||||||
|
|
||||||
let createButton = (
|
let createButton = (
|
||||||
<ActionButton
|
<ActionButton
|
||||||
tooltip
|
tooltip
|
||||||
label={_t("Communities")}
|
label={_t("Communities")}
|
||||||
action="toggle_my_groups"
|
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")) {
|
if (SettingsStore.getValue("feature_communities_v2_prototypes")) {
|
||||||
|
|
|
@ -740,6 +740,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.showScreenAfterLogin();
|
this.showScreenAfterLogin();
|
||||||
break;
|
break;
|
||||||
case 'toggle_my_groups':
|
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
|
// We just dispatch the page change rather than have to worry about
|
||||||
// what the logic is for each of these branches.
|
// what the logic is for each of these branches.
|
||||||
if (this.state.page_type === PageTypes.MyGroups) {
|
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";
|
const type = screen === "start_sso" ? "sso" : "cas";
|
||||||
PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin());
|
PlatformPeg.get().startSingleSignOn(cli, type, this.getFragmentAfterLogin());
|
||||||
} else if (screen === 'groups') {
|
} else if (screen === 'groups') {
|
||||||
|
if (SettingsStore.getValue("feature_spaces")) {
|
||||||
|
dis.dispatch({ action: "view_home_page" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_my_groups',
|
action: 'view_my_groups',
|
||||||
});
|
});
|
||||||
|
@ -1767,6 +1773,11 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
subAction: params.action,
|
subAction: params.action,
|
||||||
});
|
});
|
||||||
} else if (screen.indexOf('group/') === 0) {
|
} else if (screen.indexOf('group/') === 0) {
|
||||||
|
if (SettingsStore.getValue("feature_spaces")) {
|
||||||
|
dis.dispatch({ action: "view_home_page" });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const groupId = screen.substring(6);
|
const groupId = screen.substring(6);
|
||||||
|
|
||||||
// TODO: Check valid group ID
|
// TODO: Check valid group ID
|
||||||
|
|
|
@ -25,6 +25,7 @@ import AccessibleButton from '../views/elements/AccessibleButton';
|
||||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||||
import AutoHideScrollbar from "./AutoHideScrollbar";
|
import AutoHideScrollbar from "./AutoHideScrollbar";
|
||||||
import {replaceableComponent} from "../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../utils/replaceableComponent";
|
||||||
|
import BetaCard from "../views/beta/BetaCard";
|
||||||
|
|
||||||
@replaceableComponent("structures.MyGroups")
|
@replaceableComponent("structures.MyGroups")
|
||||||
export default class MyGroups extends React.Component {
|
export default class MyGroups extends React.Component {
|
||||||
|
@ -139,6 +140,7 @@ export default class MyGroups extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</div>*/}
|
</div>*/}
|
||||||
</div>
|
</div>
|
||||||
|
<BetaCard featureId="feature_spaces" title={_t("Communities are changing to Spaces")} />
|
||||||
<div className="mx_MyGroups_content">
|
<div className="mx_MyGroups_content">
|
||||||
{ contentHeader }
|
{ contentHeader }
|
||||||
{ content }
|
{ 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
|
return <SpaceRoomView
|
||||||
space={this.state.room}
|
space={this.state.room}
|
||||||
justCreatedOpts={this.props.justCreatedOpts}
|
justCreatedOpts={this.props.justCreatedOpts}
|
||||||
|
|
|
@ -60,6 +60,10 @@ import IconizedContextMenu, {
|
||||||
IconizedContextMenuOptionList,
|
IconizedContextMenuOptionList,
|
||||||
} from "../views/context_menus/IconizedContextMenu";
|
} from "../views/context_menus/IconizedContextMenu";
|
||||||
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
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 {
|
interface IProps {
|
||||||
space: Room;
|
space: Room;
|
||||||
|
@ -136,15 +140,39 @@ const SpaceInfo = ({ space }) => {
|
||||||
</div>
|
</div>
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onBetaClick = () => {
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: Action.ViewUserSettings,
|
||||||
|
initialTabId: USER_LABS_TAB,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => {
|
const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => {
|
||||||
const cli = useContext(MatrixClientContext);
|
const cli = useContext(MatrixClientContext);
|
||||||
const myMembership = useMyRoomMembership(space);
|
const myMembership = useMyRoomMembership(space);
|
||||||
|
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
|
|
||||||
|
const spacesEnabled = SettingsStore.getValue("feature_spaces");
|
||||||
|
|
||||||
let inviterSection;
|
let inviterSection;
|
||||||
let joinButtons;
|
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 inviteSender = space.getMember(cli.getUserId())?.events.member?.getSender();
|
||||||
const inviter = inviteSender && space.getMember(inviteSender);
|
const inviter = inviteSender && space.getMember(inviteSender);
|
||||||
|
|
||||||
|
@ -180,6 +208,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
onJoinButtonClicked();
|
onJoinButtonClicked();
|
||||||
}}
|
}}
|
||||||
|
disabled={!spacesEnabled}
|
||||||
>
|
>
|
||||||
{ _t("Accept") }
|
{ _t("Accept") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
@ -192,10 +221,11 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
onJoinButtonClicked();
|
onJoinButtonClicked();
|
||||||
}}
|
}}
|
||||||
|
disabled={!spacesEnabled}
|
||||||
>
|
>
|
||||||
{ _t("Join") }
|
{ _t("Join") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (busy) {
|
if (busy) {
|
||||||
|
@ -203,6 +233,7 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className="mx_SpaceRoomView_preview">
|
return <div className="mx_SpaceRoomView_preview">
|
||||||
|
<BetaPill onClick={onBetaClick} />
|
||||||
{ inviterSection }
|
{ inviterSection }
|
||||||
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
<RoomAvatar room={space} height={80} width={80} viewAvatarOnClick={true} />
|
||||||
<h1 className="mx_SpaceRoomView_preview_name">
|
<h1 className="mx_SpaceRoomView_preview_name">
|
||||||
|
@ -220,6 +251,20 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) =>
|
||||||
<div className="mx_SpaceRoomView_preview_joinButtons">
|
<div className="mx_SpaceRoomView_preview_joinButtons">
|
||||||
{ joinButtons }
|
{ joinButtons }
|
||||||
</div>
|
</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>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -513,9 +558,11 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished }) => {
|
||||||
return <div className="mx_SpaceRoomView_publicShare">
|
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">
|
<div className="mx_SpaceRoomView_description">
|
||||||
{ _t("It's just you at the moment, it will be even better with others.") }
|
{ _t("It's just you at the moment, it will be even better with others.") }
|
||||||
</div>
|
</div>
|
||||||
|
@ -530,11 +577,13 @@ const SpaceSetupPublicShare = ({ space, onFinished }) => {
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const SpaceSetupPrivateScope = ({ space, onFinished }) => {
|
const SpaceSetupPrivateScope = ({ space, justCreatedOpts, onFinished }) => {
|
||||||
return <div className="mx_SpaceRoomView_privateScope">
|
return <div className="mx_SpaceRoomView_privateScope">
|
||||||
<h1>{ _t("Who are you working with?") }</h1>
|
<h1>{ _t("Who are you working with?") }</h1>
|
||||||
<div className="mx_SpaceRoomView_description">
|
<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>
|
</div>
|
||||||
|
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
|
@ -636,6 +685,17 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
|
||||||
{ _t("Make sure the right people have access. You can invite more later.") }
|
{ _t("Make sure the right people have access. You can invite more later.") }
|
||||||
</div>
|
</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> }
|
{ error && <div className="mx_SpaceRoomView_errorText">{ error }</div> }
|
||||||
<form onSubmit={onClick} id="mx_SpaceSetupPrivateInvite">
|
<form onSubmit={onClick} id="mx_SpaceSetupPrivateInvite">
|
||||||
{ fields }
|
{ fields }
|
||||||
|
@ -781,7 +841,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
private renderBody() {
|
private renderBody() {
|
||||||
switch (this.state.phase) {
|
switch (this.state.phase) {
|
||||||
case Phase.Landing:
|
case Phase.Landing:
|
||||||
if (this.state.myMembership === "join") {
|
if (this.state.myMembership === "join" && SettingsStore.getValue("feature_spaces")) {
|
||||||
return <SpaceLanding space={this.props.space} />;
|
return <SpaceLanding space={this.props.space} />;
|
||||||
} else {
|
} else {
|
||||||
return <SpacePreview
|
return <SpacePreview
|
||||||
|
@ -794,7 +854,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
||||||
return <SpaceSetupFirstRooms
|
return <SpaceSetupFirstRooms
|
||||||
space={this.props.space}
|
space={this.props.space}
|
||||||
title={_t("What are some things you want to discuss in %(spaceName)s?", {
|
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={
|
description={
|
||||||
_t("Let's create a room for each of them.") + "\n" +
|
_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 })}
|
onFinished={() => this.setState({ phase: Phase.PublicShare })}
|
||||||
/>;
|
/>;
|
||||||
case 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:
|
case Phase.PrivateScope:
|
||||||
return <SpaceSetupPrivateScope
|
return <SpaceSetupPrivateScope
|
||||||
space={this.props.space}
|
space={this.props.space}
|
||||||
|
justCreatedOpts={this.props.justCreatedOpts}
|
||||||
onFinished={(invite: boolean) => {
|
onFinished={(invite: boolean) => {
|
||||||
this.setState({ phase: invite ? Phase.PrivateInvite : Phase.PrivateExistingRooms });
|
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",
|
"mx_UserSettingsDialog_securityIcon",
|
||||||
<SecurityUserSettingsTab closeSettingsFn={this.props.onFinished} />,
|
<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(
|
tabs.push(new Tab(
|
||||||
USER_LABS_TAB,
|
USER_LABS_TAB,
|
||||||
_td("Labs"),
|
_td("Labs"),
|
||||||
|
|
|
@ -32,6 +32,7 @@ export default class ActionButton extends React.Component {
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
iconPath: PropTypes.string,
|
iconPath: PropTypes.string,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
|
children: PropTypes.node,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
|
@ -79,7 +80,8 @@ export default class ActionButton extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AccessibleButton className={classNames.join(" ")}
|
<AccessibleButton
|
||||||
|
className={classNames.join(" ")}
|
||||||
onClick={this._onClick}
|
onClick={this._onClick}
|
||||||
onMouseEnter={this._onMouseEnter}
|
onMouseEnter={this._onMouseEnter}
|
||||||
onMouseLeave={this._onMouseLeave}
|
onMouseLeave={this._onMouseLeave}
|
||||||
|
@ -87,6 +89,7 @@ export default class ActionButton extends React.Component {
|
||||||
>
|
>
|
||||||
{ icon }
|
{ icon }
|
||||||
{ tooltip }
|
{ tooltip }
|
||||||
|
{ this.props.children }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||||
import * as sdk from "../../../../../index";
|
import * as sdk from "../../../../../index";
|
||||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
||||||
|
import SdkConfig from "../../../../../SdkConfig";
|
||||||
|
import BetaCard from "../../../beta/BetaCard";
|
||||||
|
|
||||||
export class LabsSettingToggle extends React.Component {
|
export class LabsSettingToggle extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -48,14 +50,40 @@ export default class LabsUserSettingsTab extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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 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 (
|
return (
|
||||||
<div className="mx_SettingsTab">
|
<div className="mx_SettingsTab mx_LabsUserSettingsTab">
|
||||||
<div className="mx_SettingsTab_heading">{_t("Labs")}</div>
|
<div className="mx_SettingsTab_heading">{_t("Labs")}</div>
|
||||||
<div className='mx_SettingsTab_subsectionText'>
|
<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>Learn more</a>.', {}, {
|
||||||
'a': (sub) => {
|
'a': (sub) => {
|
||||||
return <a href="https://github.com/vector-im/element-web/blob/develop/docs/labs.md"
|
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>
|
||||||
<div className="mx_SettingsTab_section">
|
{ betaSection }
|
||||||
{flags}
|
{ labsSection }
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
import React, {useContext, useRef, useState} from "react";
|
import React, {useContext, useRef, useState} from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types/event";
|
import {EventType, RoomType, RoomCreateTypeField} from "matrix-js-sdk/src/@types/event";
|
||||||
|
import FocusLock from "react-focus-lock";
|
||||||
|
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
|
@ -25,7 +26,10 @@ import createRoom, {IStateEvent, Preset} from "../../../createRoom";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import {SpaceAvatar} from "./SpaceBasicSettings";
|
import {SpaceAvatar} from "./SpaceBasicSettings";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
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 Field from "../elements/Field";
|
||||||
import withValidation from "../elements/Validation";
|
import withValidation from "../elements/Validation";
|
||||||
|
|
||||||
|
@ -131,7 +135,7 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
||||||
if (visibility === null) {
|
if (visibility === null) {
|
||||||
body = <React.Fragment>
|
body = <React.Fragment>
|
||||||
<h2>{ _t("Create a space") }</h2>
|
<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>
|
"To join an existing space you'll need an invite.") }</p>
|
||||||
|
|
||||||
<SpaceCreateMenuType
|
<SpaceCreateMenuType
|
||||||
|
@ -209,6 +213,13 @@ const SpaceCreateMenu = ({ onFinished }) => {
|
||||||
managed={false}
|
managed={false}
|
||||||
>
|
>
|
||||||
<FocusLock returnFocus={true}>
|
<FocusLock returnFocus={true}>
|
||||||
|
<BetaPill onClick={() => {
|
||||||
|
onFinished();
|
||||||
|
defaultDispatcher.dispatch({
|
||||||
|
action: Action.ViewUserSettings,
|
||||||
|
initialTabId: USER_LABS_TAB,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
{ body }
|
{ body }
|
||||||
</FocusLock>
|
</FocusLock>
|
||||||
</ContextMenu>;
|
</ContextMenu>;
|
||||||
|
|
|
@ -785,6 +785,13 @@
|
||||||
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
"%(senderName)s: %(stickerName)s": "%(senderName)s: %(stickerName)s",
|
||||||
"Change notification settings": "Change notification settings",
|
"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 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",
|
"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)",
|
"Send and receive voice messages (in development)": "Send and receive voice messages (in development)",
|
||||||
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
|
||||||
|
@ -998,7 +1005,7 @@
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
"Please enter a name for the space": "Please enter a name for the space",
|
"Please enter a name for the space": "Please enter a name for the space",
|
||||||
"Create a space": "Create a 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",
|
"Public": "Public",
|
||||||
"Open space for anyone, best for communities": "Open space for anyone, best for communities",
|
"Open space for anyone, best for communities": "Open space for anyone, best for communities",
|
||||||
"Private": "Private",
|
"Private": "Private",
|
||||||
|
@ -1259,7 +1266,7 @@
|
||||||
"Copy": "Copy",
|
"Copy": "Copy",
|
||||||
"Clear cache and reload": "Clear cache and reload",
|
"Clear cache and reload": "Clear cache and reload",
|
||||||
"Labs": "Labs",
|
"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",
|
"Ignored/Blocked": "Ignored/Blocked",
|
||||||
"Error adding ignored user/server": "Error adding ignored user/server",
|
"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.",
|
"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",
|
"%(networkName)s rooms": "%(networkName)s rooms",
|
||||||
"Matrix rooms": "Matrix rooms",
|
"Matrix rooms": "Matrix rooms",
|
||||||
"Filter your rooms and spaces": "Filter your rooms and spaces",
|
"Filter your rooms and spaces": "Filter your rooms and spaces",
|
||||||
"Spaces": "Spaces",
|
|
||||||
"Direct Messages": "Direct Messages",
|
"Direct Messages": "Direct Messages",
|
||||||
"Space selection": "Space selection",
|
"Space selection": "Space selection",
|
||||||
"Add existing rooms": "Add existing rooms",
|
"Add existing rooms": "Add existing rooms",
|
||||||
|
@ -2466,6 +2472,11 @@
|
||||||
"Revoke permissions": "Revoke permissions",
|
"Revoke permissions": "Revoke permissions",
|
||||||
"Move left": "Move left",
|
"Move left": "Move left",
|
||||||
"Move right": "Move right",
|
"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",
|
"Avatar": "Avatar",
|
||||||
"This room is public": "This room is public",
|
"This room is public": "This room is public",
|
||||||
"Away": "Away",
|
"Away": "Away",
|
||||||
|
@ -2599,6 +2610,7 @@
|
||||||
"Error whilst fetching joined communities": "Error whilst fetching joined communities",
|
"Error whilst fetching joined communities": "Error whilst fetching joined communities",
|
||||||
"Create a new community": "Create a new community",
|
"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.",
|
"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’re all caught up": "You’re all caught up",
|
||||||
"You have no visible notifications.": "You have no visible notifications.",
|
"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.",
|
"%(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",
|
"Public space": "Public space",
|
||||||
"Private space": "Private space",
|
"Private space": "Private space",
|
||||||
"<inviter/> invites you": "<inviter/> invites you",
|
"<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/>",
|
"Welcome to <name/>": "Welcome to <name/>",
|
||||||
"Random": "Random",
|
"Random": "Random",
|
||||||
"Support": "Support",
|
"Support": "Support",
|
||||||
|
@ -2696,6 +2710,7 @@
|
||||||
"Inviting...": "Inviting...",
|
"Inviting...": "Inviting...",
|
||||||
"Invite your teammates": "Invite your teammates",
|
"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.",
|
"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",
|
"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?",
|
"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.",
|
"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 { MatrixClient } from 'matrix-js-sdk/src/client';
|
||||||
|
import React, { ReactNode } from "react";
|
||||||
|
|
||||||
import { _td } from '../languageHandler';
|
import { _t, _td } from '../languageHandler';
|
||||||
import {
|
import {
|
||||||
NotificationBodyEnabledController,
|
NotificationBodyEnabledController,
|
||||||
NotificationsEnabledController,
|
NotificationsEnabledController,
|
||||||
|
@ -39,6 +40,7 @@ import { OrderedMultiController } from "./controllers/OrderedMultiController";
|
||||||
import { Layout } from "./Layout";
|
import { Layout } from "./Layout";
|
||||||
import ReducedMotionController from './controllers/ReducedMotionController';
|
import ReducedMotionController from './controllers/ReducedMotionController';
|
||||||
import IncompatibleController from "./controllers/IncompatibleController";
|
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
|
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
||||||
const LEVELS_ROOM_SETTINGS = [
|
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
|
// historical settings which we don't want existing user's values be wiped. Do
|
||||||
// not use this for new settings.
|
// not use this for new settings.
|
||||||
invertedSettingName?: string;
|
invertedSettingName?: string;
|
||||||
|
|
||||||
|
betaInfo?: {
|
||||||
|
title: string; // _td
|
||||||
|
caption: string; // _td
|
||||||
|
disclaimer?: (enabled: boolean) => ReactNode;
|
||||||
|
image: string; // require(...)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SETTINGS: {[setting: string]: ISetting} = {
|
export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
|
@ -127,6 +136,33 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
supportedLevels: LEVELS_FEATURE,
|
supportedLevels: LEVELS_FEATURE,
|
||||||
default: false,
|
default: false,
|
||||||
controller: new ReloadOnChangeController(),
|
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": {
|
"feature_dnd": {
|
||||||
isFeature: true,
|
isFeature: true,
|
|
@ -257,6 +257,15 @@ export default class SettingsStore {
|
||||||
return SETTINGS[settingName].isFeature;
|
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.
|
* Determines if a setting is enabled.
|
||||||
* If a setting is disabled then it should be hidden from the user.
|
* 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.");
|
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.
|
// When non-beta features are specified in the config.json, we force them as enabled or disabled.
|
||||||
if (SettingsStore.isFeature(settingName)) {
|
if (SettingsStore.isFeature(settingName) && !SETTINGS[settingName]?.betaInfo) {
|
||||||
const configVal = SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, roomId, true, true);
|
const configVal = SettingsStore.getValueAt(SettingLevel.CONFIG, settingName, roomId, true, true);
|
||||||
if (configVal === true || configVal === false) return false;
|
if (configVal === true || configVal === false) return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue