diff --git a/res/css/_components.scss b/res/css/_components.scss index 253f97bf42..da18936b47 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -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"; @@ -236,6 +237,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"; diff --git a/res/css/structures/_MyGroups.scss b/res/css/structures/_MyGroups.scss index 73f1332cd0..24b9213336 100644 --- a/res/css/structures/_MyGroups.scss +++ b/res/css/structures/_MyGroups.scss @@ -35,6 +35,12 @@ limitations under the License. margin: 40px; } +.mx_MyGroups { + .mx_BetaCard { + margin: 0 40px; + } +} + .mx_MyGroups_headerCard { flex: 1 0 50%; margin-bottom: 30px; diff --git a/res/css/views/beta/_BetaCard.scss b/res/css/views/beta/_BetaCard.scss new file mode 100644 index 0000000000..a860473732 --- /dev/null +++ b/res/css/views/beta/_BetaCard.scss @@ -0,0 +1,70 @@ +/* +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_caption { + font-size: $font-15px; + line-height: $font-20px; + color: $secondary-fg-color; + } + + .mx_AccessibleButton { + display: block; + margin: 20px 0; + padding: 12px 40px; + } + + .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; + margin-left: 12px; + text-transform: uppercase; + font-size: 12px; + line-height: 15px; + color: #FFFFFF; +} diff --git a/res/css/views/settings/tabs/user/_LabsUserSettingsTab.scss b/res/css/views/settings/tabs/user/_LabsUserSettingsTab.scss new file mode 100644 index 0000000000..540db48d65 --- /dev/null +++ b/res/css/views/settings/tabs/user/_LabsUserSettingsTab.scss @@ -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 + } + } +} diff --git a/res/img/betas/spaces.png b/res/img/betas/spaces.png new file mode 100644 index 0000000000..b487e477af Binary files /dev/null and b/res/img/betas/spaces.png differ diff --git a/src/components/structures/MyGroups.js b/src/components/structures/MyGroups.js index 2ab11dad25..14f22827a9 100644 --- a/src/components/structures/MyGroups.js +++ b/src/components/structures/MyGroups.js @@ -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 { */} +
{ contentHeader } { content } diff --git a/src/components/views/beta/BetaCard.tsx b/src/components/views/beta/BetaCard.tsx new file mode 100644 index 0000000000..9516ce975c --- /dev/null +++ b/src/components/views/beta/BetaCard.tsx @@ -0,0 +1,60 @@ +/* +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 {_t} from "../../../languageHandler"; +import AccessibleButton from "../elements/AccessibleButton"; +import SettingsStore from "../../../settings/SettingsStore"; +import {SettingLevel} from "../../../settings/SettingLevel"; + +interface IProps { + title?: string; + featureId: string; +} + +export const BetaPill = ({ onClick }) => { + return + { _t("Beta") } + ; +}; + +const BetaCard = ({ title: titleOverride, featureId }: IProps) => { + const { title, caption, disclaimer, image } = SettingsStore.getBetaInfo(featureId); + const value = SettingsStore.getValue(featureId); + + return
+
+

+ { titleOverride || _t(title) } + +

+ { _t(caption) } + SettingsStore.setValue(featureId, null, SettingLevel.DEVICE, !value)} + kind="primary" + > + { value ? _t("Leave the beta") : _t("Join the beta") } + + { disclaimer &&
+ { typeof disclaimer === "string" ? _t(disclaimer) : disclaimer() } +
} +
+ +
; +}; + +export default BetaCard; diff --git a/src/components/views/dialogs/UserSettingsDialog.js b/src/components/views/dialogs/UserSettingsDialog.js index eb9eaeb5dd..0cee622fdc 100644 --- a/src/components/views/dialogs/UserSettingsDialog.js +++ b/src/components/views/dialogs/UserSettingsDialog.js @@ -125,7 +125,10 @@ export default class UserSettingsDialog extends React.Component { "mx_UserSettingsDialog_securityIcon", , )); - 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"), diff --git a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js index f515f1862b..98148b19e0 100644 --- a/src/components/views/settings/tabs/user/LabsUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/LabsUserSettingsTab.js @@ -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 SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); - const flags = SettingsStore.getFeatureSettingNames().map(f => ); + 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 =
+ { betas.map(f => ) } +
; + } + + let labsSection; + if (SdkConfig.get()['showLabsSettings']) { + const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag"); + const flags = labs.map(f => ); + + labsSection =
+ {flags} + + + + +
; + } + return ( -
+
{_t("Labs")}
{ - _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. ' + 'Learn more.', {}, { 'a': (sub) => { return -
- {flags} - - - - -
+ { betaSection } + { labsSection }
); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c6f7a8d25e..857c1cd568 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -784,7 +784,9 @@ "%(senderName)s: %(reaction)s": "%(senderName)s: %(reaction)s", "%(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 people and rooms for fun, work, yourself or anything in between.": "Spaces are a new way to group people and rooms for fun, work, yourself or anything in between.", + "%(brand)s will reload with Spaces enabled, and communities and custom tags disabled. You can leave the beta at anytime. Certain features will require a compatible homeserver. Beta only available for Web, Desktop, and Android.": "%(brand)s will reload with Spaces enabled, and communities and custom tags disabled. You can leave the beta at anytime. Certain features will require a compatible homeserver. Beta only available for Web, Desktop, and Android.", "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", @@ -1254,7 +1256,7 @@ "click to reveal": "click to reveal", "Clear cache and reload": "Clear cache and reload", "Labs": "Labs", - "Customise your experience with experimental labs features. Learn more.": "Customise your experience with experimental labs features. Learn more.", + "Feeling experimental? Labs are the best way to get things early, test out new features and help shape them before they actually launch. Learn more.": "Feeling experimental? Labs are the best way to get things early, test out new features and help shape them before they actually launch. Learn more.", "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.", @@ -2020,7 +2022,6 @@ "Space selection": "Space selection", "Add existing rooms": "Add existing rooms", "Filter your rooms and spaces": "Filter your rooms and spaces", - "Spaces": "Spaces", "Direct Messages": "Direct Messages", "Don't want to add an existing room?": "Don't want to add an existing room?", "Create a new room": "Create a new room", @@ -2451,6 +2452,9 @@ "Revoke permissions": "Revoke permissions", "Move left": "Move left", "Move right": "Move right", + "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", @@ -2584,6 +2588,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.", diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 2a26eeac13..99f0ebecbb 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -16,8 +16,9 @@ limitations under the License. */ import { MatrixClient } from 'matrix-js-sdk/src/client'; +import type { 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,16 +119,32 @@ 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?: (() => ReactNode) | string; // _td + image: string; // require(...) + }; } export const SETTINGS: {[setting: string]: ISetting} = { "feature_spaces": { isFeature: true, - displayName: _td("Spaces prototype. Incompatible with Communities, Communities v2 and Custom Tags. " + - "Requires compatible homeserver for some features."), supportedLevels: LEVELS_FEATURE, default: false, controller: new ReloadOnChangeController(), + betaInfo: { + title: _td("Spaces"), + caption: _td("Spaces are a new way to group people and rooms for fun, " + + "work, yourself or anything in between."), + disclaimer: () => _t("%(brand)s will reload with Spaces enabled, " + + "and communities and custom tags disabled. " + + "You can leave the beta at anytime. " + + "Certain features will require a compatible homeserver. " + + "Beta only available for Web, Desktop, and Android.", { brand: SdkConfig.get().brand }), + image: require("../../res/img/betas/spaces.png"), + }, }, "feature_dnd": { isFeature: true, diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index c2675bd8f8..ff3a5371b0 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -257,6 +257,10 @@ export default class SettingsStore { return SETTINGS[settingName].isFeature; } + public static getBetaInfo(settingName: string) { + return SETTINGS[settingName]?.betaInfo; + } + /** * Determines if a setting is enabled. * If a setting is disabled then it should be hidden from the user.