diff --git a/res/css/_components.scss b/res/css/_components.scss index 74f5feedf394..c483dccc4580 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -133,6 +133,7 @@ @import "./views/dialogs/_TermsDialog.scss"; @import "./views/dialogs/_UntrustedDeviceDialog.scss"; @import "./views/dialogs/_UploadConfirmDialog.scss"; +@import "./views/dialogs/_UseCaseDialog.scss"; @import "./views/dialogs/_UserSettingsDialog.scss"; @import "./views/dialogs/_WidgetCapabilitiesPromptDialog.scss"; @import "./views/dialogs/security/_AccessSecretStorageDialog.scss"; diff --git a/res/css/views/dialogs/_UseCaseDialog.scss b/res/css/views/dialogs/_UseCaseDialog.scss new file mode 100644 index 000000000000..f01f01c93340 --- /dev/null +++ b/res/css/views/dialogs/_UseCaseDialog.scss @@ -0,0 +1,179 @@ +.mx_UseCaseDialog { + display: grid; + grid-template-rows: 1fr 1fr max-content 2fr; + height: 100%; + position: relative; + grid-gap: $spacing-40; + + &::before { + /* Once we drop support for Firefox 102, remove this entire pseudo-element, move the background to the + .mx_UseCaseDialog, and use backdrop-filter for the blur instead. */ + content: ""; + display: block; + position: absolute; + z-index: -1; + opacity: 0.6; + background-color: #ffffff; + /* Intermediate values generated compared to the original figma design to compensate for issues with + gamma-incorrect gradient blending in browsers */ + background-image: + radial-gradient(53.85% 66.75% at 87.55% 0%, + hsla(250, 76%, 71%, 0.261) 0%, + hsla(251, 83%, 79%, 0.1305) 50%, + hsla(250, 100%, 88%, 0) 100%), + radial-gradient(41.93% 41.93% at 0% 0%, + hsla(222, 29%, 20%, 0.28) 0%, + hsla(240, 17%, 53%, 0.14) 50%, + hsla(250, 100%, 88%, 0) 100%), + radial-gradient(100% 100% at 0% 0%, + hsla(250, 100%, 88%, 0.2436) 0%, + hsla(318, 50%, 82%, 0.1218) 50%, + hsla(0, 100%, 86%, 0) 100%), + radial-gradient(106.35% 96.26% at 100% 0%, + hsla(250, 100%, 88%, 0.4) 0%, + hsla(208, 67%, 85%, 0.2) 50%, + hsla(167, 76%, 82%, 0) 100%); + /* Ugly hack to allow us to get rid of the color banding of the gradients */ + /* Can be replaced with backdrop-filter, replacing the hack below, after we stop supporting Firefox 102 */ + filter: blur(8px); + /* Even uglier hack to prevent the blur filter from causing white fringing at the edges */ + left: -9px; + right: -9px; + top: -9px; + bottom: -9px; + } + + .mx_UseCaseDialog_title { + display: flex; + flex-direction: column; + justify-content: end; + + h1 { + /* Values from design, not yet replaced with variables */ + font-weight: 600; + font-size: 32px; + line-height: 39px; + text-align: center; + } + } + + .mx_UseCaseDialog_info { + display: flex; + flex-direction: column; + gap: $spacing-8; + align-self: end; + + h2 { + /* Values from design, not yet replaced with variables */ + margin: 0; + font-weight: 500; + font-size: 24px; + line-height: 32px; + text-align: center; + } + + h4 { + /* Values from design, not yet replaced with variables */ + margin: 0; + font-weight: 400; + font-size: 16px; + line-height: 19px; + color: $secondary-content; + text-align: center; + } + } + + .mx_UseCaseDialog_options { + display: grid; + /* Values from design, not yet replaced with variables */ + grid-template-columns: repeat(auto-fit, 230px); + gap: $spacing-32; + align-self: stretch; + justify-content: center; + + .mx_UseCaseDialog_option { + display: flex; + flex-direction: column; + align-items: center; + padding: $spacing-24 $spacing-16; + background: $background; + border: 1px solid $quinary-content; + border-radius: 8px; + + .mx_UseCaseDialog_icon { + /* Values from design, not yet replaced with variables */ + background: rgba(171, 59, 167, 0.15); + border-radius: 14px; + padding: $spacing-8; + margin-bottom: $spacing-16; + + &::before { + /* Values from design, not yet replaced with variables */ + content: ""; + display: block; + background: #1E1E1E; + mask-position: center; + mask-repeat: no-repeat; + mask-size: contain; + width: 22px; + height: 22px; + } + + &.mx_UseCaseDialog_icon_messaging::before { + mask-image: url('$(res)/img/element-icons/chat-bubble.svg'); + } + + &.mx_UseCaseDialog_icon_work::before { + mask-image: url('$(res)/img/element-icons/view-community.svg'); + } + + &.mx_UseCaseDialog_icon_community::before { + mask-image: url('$(res)/img/globe.svg'); + } + } + } + } + + .mx_UseCaseDialog_skip { + display: flex; + flex-direction: column; + align-self: start; + } +} + +/* Animation duration, delay, and positioning not yet final */ +.mx_UseCaseDialog_fadeIn { + animation-delay: 100ms; + animation-duration: 1s; + animation-name: mx_UseCaseDialog_fadeIn; + animation-fill-mode: backwards; + will-change: opacity; +} + +.mx_UseCaseDialog_slideIn { + animation-delay: 100ms; + animation-duration: 1s; + animation-name: mx_UseCaseDialog_slideIn; + animation-fill-mode: backwards; + will-change: transform, opacity; +} + +@keyframes mx_UseCaseDialog_fadeIn { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes mx_UseCaseDialog_slideIn { + 0% { + transform: translate(0, 16px); + opacity: 0; + } + 100% { + transform: translate(0, 0); + opacity: 1; + } +} diff --git a/res/img/element-icons/chat-bubble.svg b/res/img/element-icons/chat-bubble.svg new file mode 100644 index 000000000000..3b0e765843fa --- /dev/null +++ b/res/img/element-icons/chat-bubble.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/element-icons/group-members.svg b/res/img/element-icons/group-members.svg index 553ba3b1af15..7fa9c392320d 100644 --- a/res/img/element-icons/group-members.svg +++ b/res/img/element-icons/group-members.svg @@ -1,8 +1,3 @@ - - - - - - - + + diff --git a/res/img/element-icons/view-community.svg b/res/img/element-icons/view-community.svg index ee33aa525e4f..9bd7115a47f8 100644 --- a/res/img/element-icons/view-community.svg +++ b/res/img/element-icons/view-community.svg @@ -1,8 +1,3 @@ - - - - - - - + + diff --git a/res/img/globe.svg b/res/img/globe.svg index 635fa91cceb7..954a16d478dc 100644 --- a/res/img/globe.svg +++ b/res/img/globe.svg @@ -1,3 +1,3 @@ - - + + diff --git a/src/Views.ts b/src/Views.ts index 4c734f7bba1f..51b93834db28 100644 --- a/src/Views.ts +++ b/src/Views.ts @@ -38,6 +38,8 @@ enum Views { // flow to setup SSSS / cross-signing on this account E2E_SETUP, + USE_CASE_CHOICE, + // we are logged in with an active matrix client. The logged_in state also // includes guests users as they too are logged in at the client level. LOGGED_IN, diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 3f6ffd115464..476d6fbcf77b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -129,6 +129,7 @@ import { SnakedObject } from "../../utils/SnakedObject"; import { leaveRoomBehaviour } from "../../utils/leave-behaviour"; import VideoChannelStore from "../../stores/VideoChannelStore"; import { IRoomStateEventsActionPayload } from "../../actions/MatrixActionCreators"; +import UseCaseChooser, { UseCase } from '../views/dialogs/UseCaseChooser'; // legacy export export { default as Views } from "../../Views"; @@ -755,7 +756,8 @@ export default class MatrixChat extends React.PureComponent { this.state.view !== Views.LOGIN && this.state.view !== Views.REGISTER && this.state.view !== Views.COMPLETE_SECURITY && - this.state.view !== Views.E2E_SETUP + this.state.view !== Views.E2E_SETUP && + this.state.view !== Views.USE_CASE_CHOICE ) { this.onLoggedIn(); } @@ -1206,6 +1208,30 @@ export default class MatrixChat extends React.PureComponent { private async onLoggedIn() { ThemeController.isLogin = false; this.themeWatcher.recheck(); + StorageManager.tryPersistStorage(); + + if (!SettingsStore.isLevelSupported(SettingLevel.ACCOUNT)) { + return this.onUseCaseSelected(); + } + + // Show the analytics toast if necessary + if (SettingsStore.getValue("useCaseSelection") === null) { + this.setStateForNewView({ view: Views.USE_CASE_CHOICE }); + } + + SettingsStore.watchSetting("useCaseSelection", null, + (originalSettingName, changedInRoomId, atLevel, newValueAtLevel, newValue) => { + if (newValue !== null && this.state.view === Views.USE_CASE_CHOICE) { + this.onUseCaseSelected(); + } + }); + } + + private async onUseCaseSelected(useCase?: UseCase) { + if (useCase) { + SettingsStore.setValue("useCaseSelection", null, SettingLevel.ACCOUNT, UseCase[useCase]); + } + this.setStateForNewView({ view: Views.LOGGED_IN }); // If a specific screen is set to be shown after login, show that above // all else, as it probably means the user clicked on something already. @@ -1243,8 +1269,7 @@ export default class MatrixChat extends React.PureComponent { this.showScreenAfterLogin(); } - StorageManager.tryPersistStorage(); - + // Will be moved to a pre-login flow as well if (PosthogAnalytics.instance.isEnabled() && SettingsStore.isLevelSupported(SettingLevel.ACCOUNT)) { this.initPosthogAnalyticsToast(); } @@ -2005,6 +2030,10 @@ export default class MatrixChat extends React.PureComponent { fragmentAfterLogin={fragmentAfterLogin} /> ); + } else if (this.state.view === Views.USE_CASE_CHOICE) { + view = ( + this.onUseCaseSelected()} /> + ); } else { logger.error(`Unknown view ${this.state.view}`); } diff --git a/src/components/views/dialogs/UseCaseChooser.tsx b/src/components/views/dialogs/UseCaseChooser.tsx new file mode 100644 index 000000000000..97f4987c1add --- /dev/null +++ b/src/components/views/dialogs/UseCaseChooser.tsx @@ -0,0 +1,65 @@ +/* +Copyright 2017 Vector Creations Ltd +Copyright 2018, 2019 New Vector Ltd +Copyright 2019 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"; + +export enum UseCase { + MESSAGING, + WORK, + COMMUNITIY +} + +interface Props { + onFinished: (useCase?: UseCase) => void; +} + +export default function UseCaseChooser({ onFinished }: Props) { + return ( +
+
+

{ _t("You’re in") }

+
+
+

{ _t("Who will you chat to the most?") }

+

{ _t("We’ll help you get connected.") }

+
+
+ onFinished(UseCase.MESSAGING)}> +
+ { _t("Friends and Family") } + + onFinished(UseCase.WORK)}> +
+ { _t("Coworkers and teams") } + + onFinished(UseCase.COMMUNITIY)}> +
+ { _t("Online community members") } + +
+
+ onFinished()}> + { _t("Skip") } + +
+
+ ); +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8e40a870cb59..578293f3d862 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2781,6 +2781,12 @@ "Upload %(count)s other files|one": "Upload %(count)s other file", "Cancel All": "Cancel All", "Upload Error": "Upload Error", + "You’re in": "You’re in", + "Who will you chat to the most?": "Who will you chat to the most?", + "We’ll help you get connected.": "We’ll help you get connected.", + "Friends and Family": "Friends and Family", + "Coworkers and teams": "Coworkers and teams", + "Online community members": "Online community members", "Verify other device": "Verify other device", "Verification Request": "Verification Request", "Approve widget permissions": "Approve widget permissions", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 596398cab0da..fa1192d005dc 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -700,6 +700,10 @@ export const SETTINGS: {[setting: string]: ISetting} = { displayName: _td('Send analytics data'), default: null, }, + "useCaseSelection": { + supportedLevels: [SettingLevel.ACCOUNT], + default: null, + }, "autocompleteDelay": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, default: 200,