static-user-onboarding-steps (#9799)

This commit is contained in:
Marco Bartelt
2023-01-02 15:34:34 +01:00
committed by GitHub
parent ecb3e7a197
commit 1b06b72b67
9 changed files with 279 additions and 33 deletions

View File

@@ -30,7 +30,7 @@ export function UserOnboardingFeedback() {
}
return (
<div className="mx_UserOnboardingFeedback">
<div className="mx_UserOnboardingFeedback" data-testid="user-onboarding-feedback">
<div className="mx_UserOnboardingFeedback_content">
<Heading size="h4" className="mx_UserOnboardingFeedback_title">
{_t("How are you finding %(brand)s so far?", {

View File

@@ -15,9 +15,8 @@ limitations under the License.
*/
import * as React from "react";
import { useMemo } from "react";
import { UserOnboardingTask as Task } from "../../../hooks/useUserOnboardingTasks";
import { UserOnboardingTaskWithResolvedCompletion } from "../../../hooks/useUserOnboardingTasks";
import { _t } from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig";
import ProgressBar from "../../views/elements/ProgressBar";
@@ -25,26 +24,26 @@ import Heading from "../../views/typography/Heading";
import { UserOnboardingFeedback } from "./UserOnboardingFeedback";
import { UserOnboardingTask } from "./UserOnboardingTask";
export const getUserOnboardingCounters = (tasks: UserOnboardingTaskWithResolvedCompletion[]) => {
const completed = tasks.filter((task) => task.completed === true).length;
const waiting = tasks.filter((task) => task.completed === false).length;
return {
completed: completed,
waiting: waiting,
total: completed + waiting,
};
};
interface Props {
completedTasks: Task[];
waitingTasks: Task[];
tasks: UserOnboardingTaskWithResolvedCompletion[];
}
export function UserOnboardingList({ completedTasks, waitingTasks }: Props) {
const completed = completedTasks.length;
const waiting = waitingTasks.length;
const total = completed + waiting;
const tasks = useMemo(
() => [
...completedTasks.map((it): [Task, boolean] => [it, true]),
...waitingTasks.map((it): [Task, boolean] => [it, false]),
],
[completedTasks, waitingTasks],
);
export function UserOnboardingList({ tasks }: Props) {
const { completed, waiting, total } = getUserOnboardingCounters(tasks);
return (
<div className="mx_UserOnboardingList">
<div className="mx_UserOnboardingList" data-testid="user-onboarding-list">
<div className="mx_UserOnboardingList_header">
<Heading size="h3" className="mx_UserOnboardingList_title">
{waiting > 0
@@ -64,8 +63,8 @@ export function UserOnboardingList({ completedTasks, waitingTasks }: Props) {
{waiting === 0 && <UserOnboardingFeedback />}
</div>
<ol className="mx_UserOnboardingList_list">
{tasks.map(([task, completed]) => (
<UserOnboardingTask key={task.id} completed={completed} task={task} />
{tasks.map((task) => (
<UserOnboardingTask key={task.id} completed={task.completed} task={task} />
))}
</ol>
</div>

View File

@@ -49,7 +49,7 @@ export function UserOnboardingPage({ justRegistered = false }: Props) {
const useCase = useSettingValue<UseCase | null>("FTUE.useCaseSelection");
const context = useUserOnboardingContext();
const [completedTasks, waitingTasks] = useUserOnboardingTasks(context);
const tasks = useUserOnboardingTasks(context);
const initialSyncComplete = useInitialSyncComplete();
const [showList, setShowList] = useState<boolean>(false);
@@ -80,7 +80,7 @@ export function UserOnboardingPage({ justRegistered = false }: Props) {
return (
<AutoHideScrollbar className="mx_UserOnboardingPage">
<UserOnboardingHeader useCase={useCase} />
{showList && <UserOnboardingList completedTasks={completedTasks} waitingTasks={waitingTasks} />}
{showList && <UserOnboardingList tasks={tasks} />}
</AutoHideScrollbar>
);
}

View File

@@ -17,12 +17,12 @@ limitations under the License.
import classNames from "classnames";
import * as React from "react";
import { UserOnboardingTask as Task } from "../../../hooks/useUserOnboardingTasks";
import { UserOnboardingTaskWithResolvedCompletion } from "../../../hooks/useUserOnboardingTasks";
import AccessibleButton from "../../views/elements/AccessibleButton";
import Heading from "../../views/typography/Heading";
interface Props {
task: Task;
task: UserOnboardingTaskWithResolvedCompletion;
completed?: boolean;
}
@@ -32,6 +32,7 @@ export function UserOnboardingTask({ task, completed = false }: Props) {
return (
<li
data-testid="user-onboarding-task"
className={classNames("mx_UserOnboardingTask", {
mx_UserOnboardingTask_completed: completed,
})}

View File

@@ -82,7 +82,7 @@ function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: Matri
return value;
}
export function useUserOnboardingContext(): UserOnboardingContext | null {
export function useUserOnboardingContext(): UserOnboardingContext {
const hasAvatar = useUserOnboardingContextValue(false, async (cli) => {
const profile = await cli.getProfileInfo(cli.getUserId());
return Boolean(profile?.avatar_url);

View File

@@ -30,7 +30,7 @@ import { UseCase } from "../settings/enums/UseCase";
import { useSettingValue } from "./useSettings";
import { UserOnboardingContext } from "./useUserOnboardingContext";
export interface UserOnboardingTask {
interface UserOnboardingTask {
id: string;
title: string | (() => string);
description: string | (() => string);
@@ -41,10 +41,11 @@ export interface UserOnboardingTask {
href?: string;
hideOnComplete?: boolean;
};
completed: (ctx: UserOnboardingContext) => boolean;
}
interface InternalUserOnboardingTask extends UserOnboardingTask {
completed: (ctx: UserOnboardingContext) => boolean;
export interface UserOnboardingTaskWithResolvedCompletion extends Omit<UserOnboardingTask, "completed"> {
completed: boolean;
}
const onClickStartDm = (ev: ButtonEvent) => {
@@ -52,7 +53,7 @@ const onClickStartDm = (ev: ButtonEvent) => {
defaultDispatcher.dispatch({ action: "view_create_chat" });
};
const tasks: InternalUserOnboardingTask[] = [
const tasks: UserOnboardingTask[] = [
{
id: "create-account",
title: _t("Create account"),
@@ -143,9 +144,15 @@ const tasks: InternalUserOnboardingTask[] = [
},
];
export function useUserOnboardingTasks(context: UserOnboardingContext): [UserOnboardingTask[], UserOnboardingTask[]] {
export function useUserOnboardingTasks(context: UserOnboardingContext) {
const useCase = useSettingValue<UseCase | null>("FTUE.useCaseSelection") ?? UseCase.Skip;
const relevantTasks = useMemo(() => tasks.filter((it) => !it.relevant || it.relevant.includes(useCase)), [useCase]);
const completedTasks = relevantTasks.filter((it) => context && it.completed(context));
return [completedTasks, relevantTasks.filter((it) => !completedTasks.includes(it))];
return useMemo<UserOnboardingTaskWithResolvedCompletion[]>(() => {
return tasks
.filter((task) => !task.relevant || task.relevant.includes(useCase))
.map((task) => ({
...task,
completed: task.completed(context),
}));
}, [context, useCase]);
}