diff --git a/cypress/e2e/user-onboarding/user-onboarding-new.ts b/cypress/e2e/user-onboarding/user-onboarding-new.ts index 59099297ee1..833d0a2c8e5 100644 --- a/cypress/e2e/user-onboarding/user-onboarding-new.ts +++ b/cypress/e2e/user-onboarding/user-onboarding-new.ts @@ -48,8 +48,11 @@ describe("User Onboarding (new user)", () => { cy.stopSynapse(synapse); }); - it("page is shown", () => { + it("page is shown and preference exists", () => { cy.get('.mx_UserOnboardingPage').should('exist'); + cy.get('.mx_UserOnboardingButton').should('exist'); + cy.openUserSettings("Preferences"); + cy.contains("Show shortcut to welcome page above the room list").should("exist"); }); it("using find friends action should increase progress", () => { diff --git a/cypress/e2e/user-onboarding/user-onboarding-old.ts b/cypress/e2e/user-onboarding/user-onboarding-old.ts index 2be066e0a1c..f079ed9a4c3 100644 --- a/cypress/e2e/user-onboarding/user-onboarding-old.ts +++ b/cypress/e2e/user-onboarding/user-onboarding-old.ts @@ -40,7 +40,10 @@ describe("User Onboarding (old user)", () => { cy.stopSynapse(synapse); }); - it("page is hidden", () => { + it("page and preference are hidden", () => { cy.get('.mx_UserOnboardingPage').should('not.exist'); + cy.get('.mx_UserOnboardingButton').should('not.exist'); + cy.openUserSettings("Preferences"); + cy.contains("Show shortcut to welcome page above the room list").should("not.exist"); }); }); diff --git a/res/css/_components.pcss b/res/css/_components.pcss index 3525aa2ba0f..8db58ea624c 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -328,6 +328,7 @@ @import "./views/user-onboarding/_UserOnboardingList.pcss"; @import "./views/user-onboarding/_UserOnboardingPage.pcss"; @import "./views/user-onboarding/_UserOnboardingTask.pcss"; +@import "./views/user-onboarding/_UserOnboardingButton.pcss"; @import "./views/verification/_VerificationShowSas.pcss"; @import "./views/voip/CallView/_CallViewButtons.pcss"; @import "./views/voip/_CallPreview.pcss"; diff --git a/res/css/views/user-onboarding/_UserOnboardingButton.pcss b/res/css/views/user-onboarding/_UserOnboardingButton.pcss new file mode 100644 index 00000000000..40516b0f2bb --- /dev/null +++ b/res/css/views/user-onboarding/_UserOnboardingButton.pcss @@ -0,0 +1,68 @@ +.mx_UserOnboardingButton { + display: flex; + flex-direction: column; + align-content: stretch; + align-items: stretch; + border-radius: 8px; + margin: $spacing-8 $spacing-8 0; + padding: $spacing-12; + + &.mx_UserOnboardingButton_selected, + &:hover, + &:focus-within { + background-color: $panel-actions; + } + + .mx_UserOnboardingButton_content { + display: flex; + flex-direction: row; + gap: 5px; + align-items: center; + + .mx_Heading_h4 { + margin-right: auto; + font-size: $font-14px; + color: $primary-content; + } + + .mx_UserOnboardingButton_percentage { + font-size: $font-12px; + color: $secondary-content; + } + + .mx_UserOnboardingButton_close { + position: relative; + box-sizing: border-box; + width: 14px; + height: 14px; + border-radius: 7px; + border: 1px solid $secondary-content; + flex-shrink: 0; + + &::before { + background-color: $secondary-content; + content: ""; + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + width: 7px; + height: 7px; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + mask-image: url("$(res)/img/element-icons/cancel-rounded.svg"); + } + } + } + + .mx_ProgressBar { + width: auto; + margin-top: $spacing-8; + background: $background; + } + + &.mx_UserOnboardingButton_completed .mx_ProgressBar { + display: none; + } +} diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index ab8620a26df..e5eee9cff74 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -45,9 +45,12 @@ import { shouldShowComponent } from "../../customisations/helpers/UIComponents"; import { UIComponent } from "../../settings/UIFeature"; import { ButtonEvent } from "../views/elements/AccessibleButton"; import PosthogTrackers from "../../PosthogTrackers"; +import PageType from "../../PageTypes"; +import { UserOnboardingButton } from "../views/user-onboarding/UserOnboardingButton"; interface IProps { isMinimized: boolean; + pageType: PageType; resizeNotifier: ResizeNotifier; } @@ -390,6 +393,10 @@ export default class LeftPanel extends React.Component { onVisibilityChange={this.refreshStickyHeaders} /> ) } +
{ data-collapsed={this.props.collapseLhs ? true : undefined} > diff --git a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx index fd9dd6f4388..8e735856efe 100644 --- a/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/PreferencesUserSettingsTab.tsx @@ -44,6 +44,7 @@ interface IState { export default class PreferencesUserSettingsTab extends React.Component { static ROOM_LIST_SETTINGS = [ 'breadcrumbs', + "FTUE.userOnboardingButton", ]; static SPACES_SETTINGS = [ diff --git a/src/components/views/user-onboarding/UserOnboardingButton.tsx b/src/components/views/user-onboarding/UserOnboardingButton.tsx new file mode 100644 index 00000000000..436e034ed2e --- /dev/null +++ b/src/components/views/user-onboarding/UserOnboardingButton.tsx @@ -0,0 +1,90 @@ +/* +Copyright 2022 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 classNames from "classnames"; +import React, { useCallback } from "react"; + +import { Action } from "../../../dispatcher/actions"; +import defaultDispatcher from "../../../dispatcher/dispatcher"; +import { useSettingValue } from "../../../hooks/useSettings"; +import { useUserOnboardingTasks } from "../../../hooks/useUserOnboardingTasks"; +import { _t } from "../../../languageHandler"; +import { UseCase } from "../../../settings/enums/UseCase"; +import { SettingLevel } from "../../../settings/SettingLevel"; +import SettingsStore from "../../../settings/SettingsStore"; +import AccessibleButton from "../../views/elements/AccessibleButton"; +import ProgressBar from "../../views/elements/ProgressBar"; +import Heading from "../../views/typography/Heading"; +import { showUserOnboardingPage } from "./UserOnboardingPage"; + +function toPercentage(progress: number): string { + return (progress * 100).toFixed(0); +} + +interface Props { + selected: boolean; + minimized: boolean; +} + +export function UserOnboardingButton({ selected, minimized }: Props) { + const [completedTasks, waitingTasks] = useUserOnboardingTasks(); + + const completed = completedTasks.length; + const waiting = waitingTasks.length; + const total = completed + waiting; + + const progress = waiting ? completed / total : 1; + + const onDismiss = useCallback(() => { + SettingsStore.setValue("FTUE.userOnboardingButton", null, SettingLevel.ACCOUNT, false); + }, []); + + const useCase = useSettingValue("FTUE.useCaseSelection"); + const visible = useSettingValue("FTUE.userOnboardingButton"); + if (!visible || minimized || !showUserOnboardingPage(useCase)) { + return null; + } + + return ( + defaultDispatcher.fire(Action.ViewHomePage)}> + { !minimized && ( + <> +
+ + { _t("Welcome") } + + { !completed && ( +
+ { toPercentage(progress) }% +
+ ) } + +
+ + + ) } +
+ ); +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f95e91ab4f2..39ce62a16a8 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -951,6 +951,7 @@ "Order rooms by name": "Order rooms by name", "Show rooms with unread notifications first": "Show rooms with unread notifications first", "Show shortcuts to recently viewed rooms above the room list": "Show shortcuts to recently viewed rooms above the room list", + "Show shortcut to welcome page above the room list": "Show shortcut to welcome page above the room list", "Show hidden events in timeline": "Show hidden events in timeline", "Low bandwidth mode (requires compatible homeserver)": "Low bandwidth mode (requires compatible homeserver)", "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)": "Allow fallback call assist server turn.matrix.org when your homeserver does not offer one (your IP address would be shared during a call)", @@ -1147,6 +1148,7 @@ "Anchor": "Anchor", "Headphones": "Headphones", "Folder": "Folder", + "Welcome": "Welcome", "Secure messaging for friends and family.": "Secure messaging for friends and family.", "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.": "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.", "Start your first chat": "Start your first chat", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 44d82da5cd4..773b4a79f06 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -804,6 +804,11 @@ export const SETTINGS: {[setting: string]: ISetting} = { default: true, controller: new IncompatibleController("feature_breadcrumbs_v2", true), }, + "FTUE.userOnboardingButton": { + supportedLevels: LEVELS_ACCOUNT_SETTINGS, + displayName: _td("Show shortcut to welcome page above the room list"), + default: true, + }, "showHiddenEventsInTimeline": { displayName: _td("Show hidden events in timeline"), supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,