Remove FTUE onboarding as it is incompatible with SSO/OIDC
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>t3chguy/remove-ftue-onboarding
parent
7685e547de
commit
964c27557b
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
test.describe("User Onboarding (new user)", () => {
|
||||
test.use({
|
||||
displayName: "Jane Doe",
|
||||
});
|
||||
|
||||
// This first beforeEach happens before the `user` fixture runs
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("mx_registration_time", "1656633601");
|
||||
});
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page, user }) => {
|
||||
await expect(page.locator(".mx_UserOnboardingPage")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Welcome" })).toBeVisible();
|
||||
await expect(page.locator(".mx_UserOnboardingList")).toBeVisible();
|
||||
});
|
||||
|
||||
test("page is shown and preference exists", { tag: "@screenshot" }, async ({ page, app }) => {
|
||||
await expect(page.locator(".mx_UserOnboardingPage")).toMatchScreenshot(
|
||||
"User-Onboarding-new-user-page-is-shown-and-preference-exists-1.png",
|
||||
);
|
||||
await app.settings.openUserSettings("Preferences");
|
||||
await expect(page.getByText("Show shortcut to welcome checklist above the room list")).toBeVisible();
|
||||
});
|
||||
|
||||
test("app download dialog", { tag: "@screenshot" }, async ({ page }) => {
|
||||
await page.getByRole("button", { name: "Download apps" }).click();
|
||||
await expect(
|
||||
page.getByRole("dialog").getByRole("heading", { level: 1, name: "Download Element" }),
|
||||
).toBeVisible();
|
||||
await expect(page.locator(".mx_Dialog")).toMatchScreenshot(
|
||||
"User-Onboarding-new-user-app-download-dialog-1.png",
|
||||
{
|
||||
// Set a constant bg behind the modal to ensure screenshot stability
|
||||
css: `
|
||||
.mx_AppDownloadDialog_wrapper {
|
||||
background: black;
|
||||
}
|
||||
`,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("using find friends action should increase progress", async ({ page, homeserver }) => {
|
||||
const bot = await homeserver.registerUser("botbob", "password", "BotBob");
|
||||
|
||||
const oldProgress = parseFloat(await page.getByRole("progressbar").getAttribute("value"));
|
||||
await page.getByRole("button", { name: "Find friends" }).click();
|
||||
await page.locator(".mx_InviteDialog_editor").getByRole("textbox").fill(bot.userId);
|
||||
await page.getByRole("button", { name: "Go" }).click();
|
||||
await expect(page.locator(".mx_InviteDialog_buttonAndSpinner")).not.toBeVisible();
|
||||
|
||||
const message = "Hi!";
|
||||
const composer = page.getByRole("textbox", { name: "Send a message…" });
|
||||
await composer.fill(`${message}`);
|
||||
await composer.press("Enter");
|
||||
await expect(page.locator(".mx_MTextBody.mx_EventTile_content", { hasText: message })).toBeVisible();
|
||||
|
||||
await page.goto("/#/home");
|
||||
await expect(page.locator(".mx_UserOnboardingPage")).toBeVisible();
|
||||
await expect(page.getByRole("button", { name: "Welcome" })).toBeVisible();
|
||||
await expect(page.locator(".mx_UserOnboardingList")).toBeVisible();
|
||||
|
||||
await page.waitForTimeout(500); // await progress bar animation
|
||||
const progress = parseFloat(await page.getByRole("progressbar").getAttribute("value"));
|
||||
expect(progress).toBeGreaterThan(oldProgress);
|
||||
});
|
||||
});
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
test.describe("User Onboarding (old user)", () => {
|
||||
test.use({
|
||||
displayName: "Jane Doe",
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.addInitScript(() => {
|
||||
window.localStorage.setItem("mx_registration_time", "2");
|
||||
});
|
||||
});
|
||||
|
||||
test("page and preference are hidden", async ({ page, user, app }) => {
|
||||
await expect(page.locator(".mx_UserOnboardingPage")).not.toBeVisible();
|
||||
await expect(page.locator(".mx_UserOnboardingButton")).not.toBeVisible();
|
||||
await app.settings.openUserSettings("Preferences");
|
||||
await expect(page.getByText("Show shortcut to welcome checklist above the room list")).not.toBeVisible();
|
||||
});
|
||||
});
|
|
@ -126,7 +126,6 @@
|
|||
@import "./views/context_menus/_RoomNotificationContextMenu.pcss";
|
||||
@import "./views/dialogs/_AddExistingToSpaceDialog.pcss";
|
||||
@import "./views/dialogs/_AnalyticsLearnMoreDialog.pcss";
|
||||
@import "./views/dialogs/_AppDownloadDialog.pcss";
|
||||
@import "./views/dialogs/_BugReportDialog.pcss";
|
||||
@import "./views/dialogs/_BulkRedactDialog.pcss";
|
||||
@import "./views/dialogs/_ChangelogDialog.pcss";
|
||||
|
@ -217,8 +216,6 @@
|
|||
@import "./views/elements/_TagComposer.pcss";
|
||||
@import "./views/elements/_TextWithTooltip.pcss";
|
||||
@import "./views/elements/_ToggleSwitch.pcss";
|
||||
@import "./views/elements/_UseCaseSelection.pcss";
|
||||
@import "./views/elements/_UseCaseSelectionButton.pcss";
|
||||
@import "./views/elements/_Validation.pcss";
|
||||
@import "./views/emojipicker/_EmojiPicker.pcss";
|
||||
@import "./views/location/_LocationPicker.pcss";
|
||||
|
@ -375,11 +372,6 @@
|
|||
@import "./views/toasts/_IncomingLegacyCallToast.pcss";
|
||||
@import "./views/toasts/_NonUrgentEchoFailureToast.pcss";
|
||||
@import "./views/typography/_Heading.pcss";
|
||||
@import "./views/user-onboarding/_UserOnboardingButton.pcss";
|
||||
@import "./views/user-onboarding/_UserOnboardingHeader.pcss";
|
||||
@import "./views/user-onboarding/_UserOnboardingList.pcss";
|
||||
@import "./views/user-onboarding/_UserOnboardingPage.pcss";
|
||||
@import "./views/user-onboarding/_UserOnboardingTask.pcss";
|
||||
@import "./views/verification/_VerificationShowSas.pcss";
|
||||
@import "./views/voip/LegacyCallView/_LegacyCallViewButtons.pcss";
|
||||
@import "./views/voip/_CallDuration.pcss";
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
.mx_AppDownloadDialog {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-32;
|
||||
color: $primary-content;
|
||||
|
||||
&.mx_Dialog_fixedWidth {
|
||||
width: 640px;
|
||||
}
|
||||
|
||||
.mx_AppDownloadDialog_desktop {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: $spacing-16;
|
||||
}
|
||||
|
||||
.mx_AppDownloadDialog_mobile {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $spacing-24;
|
||||
|
||||
.mx_AppDownloadDialog_app {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
flex-basis: 50%;
|
||||
align-items: center;
|
||||
gap: $spacing-16;
|
||||
|
||||
.mx_QRCode {
|
||||
/* intentionally hardcoded color to ensure the QR code is readable in any situation */
|
||||
background: #ffffff;
|
||||
|
||||
padding: $spacing-24;
|
||||
border: 1px solid $quinary-content;
|
||||
border-radius: 4px;
|
||||
align-self: stretch;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
.mx_VerificationQRCode {
|
||||
height: 144px;
|
||||
width: 144px;
|
||||
image-rendering: pixelated;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_AppDownloadDialog_info {
|
||||
font-size: $font-12px;
|
||||
color: $tertiary-content;
|
||||
}
|
||||
|
||||
.mx_AppDownloadDialog_links {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $spacing-8;
|
||||
|
||||
.mx_AccessibleButton {
|
||||
svg {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_AppDownloadDialog_legal {
|
||||
p {
|
||||
margin: 0;
|
||||
font-size: $font-12px;
|
||||
color: $tertiary-content;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UseCaseSelection {
|
||||
display: grid;
|
||||
grid-template-rows: 1fr 1fr max-content 2fr;
|
||||
height: 100%;
|
||||
grid-gap: $spacing-40;
|
||||
|
||||
.mx_UseCaseSelection_title {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
|
||||
h1 {
|
||||
font-weight: var(--cpd-font-weight-semibold);
|
||||
font-size: $font-32px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: $spacing-8;
|
||||
align-self: flex-end;
|
||||
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: $font-24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-weight: 400;
|
||||
font-size: $font-16px;
|
||||
color: $secondary-content;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_options {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, 232px);
|
||||
gap: $spacing-32;
|
||||
align-self: stretch;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_skip {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_slideIn {
|
||||
animation-delay: 800ms;
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
|
||||
animation-name: mx_UseCaseSelection_slideInLong;
|
||||
animation-fill-mode: backwards;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_slideInDelayed {
|
||||
animation-delay: 1500ms;
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
|
||||
animation-name: mx_UseCaseSelection_slideInShort;
|
||||
animation-fill-mode: backwards;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
|
||||
.mx_UseCaseSelection_selected {
|
||||
.mx_UseCaseSelection_slideIn,
|
||||
.mx_UseCaseSelection_slideInDelayed {
|
||||
animation-delay: 800ms;
|
||||
animation-duration: 300ms;
|
||||
animation-fill-mode: forwards;
|
||||
animation-name: mx_UseCaseSelection_fadeOut;
|
||||
will-change: opacity;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UseCaseSelection_slideInLong {
|
||||
0% {
|
||||
transform: translate(0, 20px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UseCaseSelection_slideInShort {
|
||||
0% {
|
||||
transform: translate(0, 8px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UseCaseSelection_fadeOut {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UseCaseSelectionButton {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: $spacing-24 $spacing-16;
|
||||
background: $background;
|
||||
border: 1px solid $quinary-content;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
transition-property: box-shadow, transform;
|
||||
transition-duration: 300ms;
|
||||
|
||||
.mx_UseCaseSelectionButton_icon {
|
||||
/* workaround: design expects a layering of two colors */
|
||||
background: linear-gradient(0deg, rgba(172, 59, 168, 0.15), rgba(172, 59, 168, 0.15)), #ffffff;
|
||||
border-radius: 14px;
|
||||
padding: $spacing-8;
|
||||
margin-bottom: $spacing-16;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
/* this has to remain the same color across all themes,
|
||||
as its background has a fixed color as well */
|
||||
background: #1e1e1e;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
&.mx_UseCaseSelectionButton_messaging::before {
|
||||
mask-image: url("$(res)/img/element-icons/chat-bubble.svg");
|
||||
}
|
||||
|
||||
&.mx_UseCaseSelectionButton_work::before {
|
||||
mask-image: url("$(res)/img/element-icons/view-community.svg");
|
||||
}
|
||||
|
||||
&.mx_UseCaseSelectionButton_community::before {
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/public.svg");
|
||||
mask-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
box-shadow: 0 $spacing-4 $spacing-8 rgba(0, 0, 0, 0.08);
|
||||
transform: translate(0, -$spacing-8);
|
||||
}
|
||||
|
||||
.mx_UseCaseSelectionButton_selectedIcon {
|
||||
right: -12px;
|
||||
top: -12px;
|
||||
position: absolute;
|
||||
border-radius: 24px;
|
||||
background: $accent;
|
||||
padding: 6px;
|
||||
transition-property: opacity, transform;
|
||||
transition-duration: 150ms;
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
background: $background;
|
||||
mask-position: center;
|
||||
mask-repeat: no-repeat;
|
||||
mask-size: contain;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
|
||||
}
|
||||
}
|
||||
|
||||
&.mx_UseCaseSelectionButton_selected {
|
||||
border: 2px solid $accent;
|
||||
padding: calc($spacing-24 - 1px) calc($spacing-16 - 1px);
|
||||
box-shadow: 0 $spacing-4 $spacing-8 rgba(0, 0, 0, 0.08);
|
||||
|
||||
.mx_UseCaseSelectionButton_selectedIcon {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.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: var(--cpd-font-body-md-regular);
|
||||
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: 12px;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/close.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mx_ProgressBar {
|
||||
width: auto;
|
||||
margin-top: $spacing-8;
|
||||
background: $background;
|
||||
}
|
||||
|
||||
&.mx_UserOnboardingButton_completed .mx_ProgressBar {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UserOnboardingHeader {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: $spacing-32;
|
||||
border-radius: 16px;
|
||||
background: $system;
|
||||
gap: $spacing-64;
|
||||
|
||||
animation-delay: 1500ms;
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
|
||||
animation-name: mx_UserOnboardingHeader_slideIn;
|
||||
animation-fill-mode: backwards;
|
||||
will-change: opacity, transform;
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
margin: $spacing-32;
|
||||
}
|
||||
|
||||
.mx_UserOnboardingHeader_dot {
|
||||
color: $accent;
|
||||
}
|
||||
|
||||
.mx_UserOnboardingHeader_content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-basis: 50%;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
min-width: 0;
|
||||
gap: $spacing-24;
|
||||
margin-right: auto;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.mx_AccessibleButton {
|
||||
margin-top: auto;
|
||||
align-self: flex-start;
|
||||
padding: $spacing-12 $spacing-24;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingHeader_image {
|
||||
flex-basis: 30%;
|
||||
flex-shrink: 1;
|
||||
flex-grow: 1;
|
||||
align-self: center;
|
||||
height: calc(100% + $spacing-64 + $spacing-64);
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: contain;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin-top: -$spacing-64;
|
||||
margin-bottom: -$spacing-64;
|
||||
|
||||
animation-delay: 1500ms;
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
|
||||
animation-name: mx_UserOnboardingHeader_slideInLong;
|
||||
animation-fill-mode: backwards;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UserOnboardingHeader_slideIn {
|
||||
0% {
|
||||
transform: translate(0, 8px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UserOnboardingHeader_slideInLong {
|
||||
0% {
|
||||
transform: translate(0, 32px);
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UserOnboardingList {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 0 $spacing-32;
|
||||
|
||||
animation-duration: 300ms;
|
||||
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
|
||||
animation-name: mx_UserOnboardingList_slideIn;
|
||||
animation-fill-mode: backwards;
|
||||
will-change: opacity;
|
||||
|
||||
.mx_UserOnboardingList_header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
|
||||
.mx_UserOnboardingList_hint {
|
||||
color: $secondary-content;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingList_progress {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
counter-reset: user-onboarding;
|
||||
|
||||
.mx_ProgressBar {
|
||||
width: auto;
|
||||
margin-top: $spacing-16;
|
||||
height: 16px;
|
||||
|
||||
@mixin ProgressBarBorderRadius 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingList_list {
|
||||
display: grid;
|
||||
grid-template-columns: max-content 1fr max-content;
|
||||
|
||||
appearance: none;
|
||||
list-style: none;
|
||||
margin: $spacing-32 0 0;
|
||||
padding: 0;
|
||||
|
||||
grid-gap: $spacing-24;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UserOnboardingList_slideIn {
|
||||
0% {
|
||||
transform: translate(0, 8px);
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
transform: translate(0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UserOnboardingPage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
align-self: stretch;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto auto;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
|
||||
gap: $spacing-64;
|
||||
padding: $spacing-64 100px;
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
padding: $spacing-48 $spacing-32;
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_UserOnboardingTask {
|
||||
display: contents;
|
||||
|
||||
.mx_UserOnboardingTask_number {
|
||||
counter-increment: user-onboarding;
|
||||
grid-column: 1;
|
||||
color: $secondary-content;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
border: 2px solid $quinary-content;
|
||||
border-radius: 32px;
|
||||
line-height: 32px;
|
||||
align-self: center;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: counter(user-onboarding);
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingTask_content {
|
||||
grid-column: 2;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 1;
|
||||
|
||||
transition: all 500ms;
|
||||
|
||||
.mx_UserOnboardingTask_title {
|
||||
font: var(--cpd-font-body-md-medium);
|
||||
}
|
||||
|
||||
.mx_UserOnboardingTask_description {
|
||||
font-size: $font-12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingTask_action.mx_AccessibleButton {
|
||||
grid-column: 3;
|
||||
min-width: 180px;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
grid-column: 2;
|
||||
margin-top: -16px;
|
||||
}
|
||||
}
|
||||
|
||||
&.mx_UserOnboardingTask_completed {
|
||||
.mx_UserOnboardingTask_number {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -2px;
|
||||
background: var(--cpd-color-icon-accent-tertiary);
|
||||
border-radius: 32px;
|
||||
|
||||
animation-duration: 300ms;
|
||||
animation-fill-mode: both;
|
||||
animation-name: mx_UserOnboardingTask_spring;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
&::after {
|
||||
background-color: var(--cpd-color-icon-on-solid-primary);
|
||||
content: "";
|
||||
mask-repeat: no-repeat;
|
||||
mask-position: center;
|
||||
mask-size: 24px;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
|
||||
|
||||
animation-duration: 300ms;
|
||||
animation-fill-mode: both;
|
||||
animation-name: mx_UserOnboardingTask_spring;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_UserOnboardingTask_content {
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mx_UserOnboardingTask_spring {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 8.3 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 11 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 7.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 545 KiB |
Binary file not shown.
Before Width: | Height: | Size: 188 KiB |
Binary file not shown.
Before Width: | Height: | Size: 688 KiB |
|
@ -24,7 +24,6 @@ const notLoggedInMap: Record<Exclude<Views, Views.LOGGED_IN>, ScreenName> = {
|
|||
[Views.WELCOME]: "Welcome",
|
||||
[Views.LOGIN]: "Login",
|
||||
[Views.REGISTER]: "Register",
|
||||
[Views.USE_CASE_SELECTION]: "UseCaseSelection",
|
||||
[Views.FORGOT_PASSWORD]: "ForgotPassword",
|
||||
[Views.COMPLETE_SECURITY]: "CompleteSecurity",
|
||||
[Views.E2E_SETUP]: "E2ESetup",
|
||||
|
|
|
@ -33,9 +33,6 @@ enum Views {
|
|||
// flow to setup SSSS / cross-signing on this account
|
||||
E2E_SETUP,
|
||||
|
||||
// screen that allows users to select which use case they’ll use matrix for
|
||||
USE_CASE_SELECTION,
|
||||
|
||||
// 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,
|
||||
|
|
|
@ -35,7 +35,6 @@ import { UIComponent } from "../../settings/UIFeature";
|
|||
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
|
||||
import PosthogTrackers from "../../PosthogTrackers";
|
||||
import PageType from "../../PageTypes";
|
||||
import { UserOnboardingButton } from "../views/user-onboarding/UserOnboardingButton";
|
||||
import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation";
|
||||
|
||||
interface IProps {
|
||||
|
@ -398,10 +397,6 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
|||
{shouldShowComponent(UIComponent.FilterContainer) && this.renderSearchDialExplore()}
|
||||
{this.renderBreadcrumbs()}
|
||||
{!this.props.isMinimized && <RoomListHeader onVisibilityChange={this.refreshStickyHeaders} />}
|
||||
<UserOnboardingButton
|
||||
selected={this.props.pageType === PageType.HomePage}
|
||||
minimized={this.props.isMinimized}
|
||||
/>
|
||||
<nav className="mx_LeftPanel_roomListWrapper" aria-label={_t("common|rooms")}>
|
||||
<div
|
||||
className={roomListClasses}
|
||||
|
|
|
@ -61,7 +61,7 @@ import { TimelineRenderingType } from "../../contexts/RoomContext";
|
|||
import { KeyBindingAction } from "../../accessibility/KeyboardShortcuts";
|
||||
import { SwitchSpacePayload } from "../../dispatcher/payloads/SwitchSpacePayload";
|
||||
import LeftPanelLiveShareWarning from "../views/beacon/LeftPanelLiveShareWarning";
|
||||
import { UserOnboardingPage } from "../views/user-onboarding/UserOnboardingPage";
|
||||
import HomePage from "./HomePage";
|
||||
import { PipContainer } from "./PipContainer";
|
||||
import { monitorSyncedPushRules } from "../../utils/pushRules/monitorSyncedPushRules";
|
||||
import { ConfigOptions } from "../../SdkConfig";
|
||||
|
@ -678,7 +678,7 @@ class LoggedInView extends React.Component<IProps, IState> {
|
|||
break;
|
||||
|
||||
case PageTypes.HomePage:
|
||||
pageElement = <UserOnboardingPage justRegistered={this.props.justRegistered} />;
|
||||
pageElement = <HomePage justRegistered={this.props.justRegistered} />;
|
||||
break;
|
||||
|
||||
case PageTypes.UserView:
|
||||
|
|
|
@ -55,7 +55,6 @@ import { FontWatcher } from "../../settings/watchers/FontWatcher";
|
|||
import { storeRoomAliasInCache } from "../../RoomAliasCache";
|
||||
import ToastStore from "../../stores/ToastStore";
|
||||
import * as StorageManager from "../../utils/StorageManager";
|
||||
import { UseCase } from "../../settings/enums/UseCase";
|
||||
import type LoggedInViewType from "./LoggedInView";
|
||||
import LoggedInView from "./LoggedInView";
|
||||
import { Action } from "../../dispatcher/actions";
|
||||
|
@ -114,7 +113,6 @@ import { ShowThreadPayload } from "../../dispatcher/payloads/ShowThreadPayload";
|
|||
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
||||
import RightPanelStore from "../../stores/right-panel/RightPanelStore";
|
||||
import { TimelineRenderingType } from "../../contexts/RoomContext";
|
||||
import { UseCaseSelection } from "../views/elements/UseCaseSelection";
|
||||
import { ValidatedServerConfig } from "../../utils/ValidatedServerConfig";
|
||||
import { isLocalRoom } from "../../utils/localRoom/isLocalRoom";
|
||||
import { SDKContext, SdkContextClass } from "../../contexts/SDKContext";
|
||||
|
@ -866,8 +864,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
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.USE_CASE_SELECTION
|
||||
this.state.view !== Views.E2E_SETUP
|
||||
) {
|
||||
this.onLoggedIn();
|
||||
}
|
||||
|
@ -1359,12 +1356,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
await this.onShowPostLoginScreen();
|
||||
}
|
||||
|
||||
private async onShowPostLoginScreen(useCase?: UseCase): Promise<void> {
|
||||
if (useCase) {
|
||||
PosthogAnalytics.instance.setProperty("ftueUseCaseSelection", useCase);
|
||||
SettingsStore.setValue("FTUE.useCaseSelection", null, SettingLevel.ACCOUNT, useCase);
|
||||
}
|
||||
|
||||
private async onShowPostLoginScreen(): Promise<void> {
|
||||
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.
|
||||
|
@ -2010,33 +2002,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
// complete security / e2e setup has finished
|
||||
private onCompleteSecurityE2eSetupFinished = (): void => {
|
||||
if (MatrixClientPeg.currentUserIsJustRegistered() && SettingsStore.getValue("FTUE.useCaseSelection") === null) {
|
||||
this.setStateForNewView({ view: Views.USE_CASE_SELECTION });
|
||||
|
||||
// Listen to changes in settings and hide the use case screen if appropriate - this is necessary because
|
||||
// account settings can still be changing at this point in app init (due to the initial sync being cached,
|
||||
// then subsequent syncs being received from the server)
|
||||
//
|
||||
// This seems unlikely for something that should happen directly after registration, but if a user does
|
||||
// their initial login on another device/browser than they registered on, we want to avoid asking this
|
||||
// question twice
|
||||
//
|
||||
// initPosthogAnalyticsToast pioneered this technique, we’re just reusing it here.
|
||||
SettingsStore.watchSetting(
|
||||
"FTUE.useCaseSelection",
|
||||
null,
|
||||
(originalSettingName, changedInRoomId, atLevel, newValueAtLevel, newValue) => {
|
||||
if (newValue !== null && this.state.view === Views.USE_CASE_SELECTION) {
|
||||
this.onShowPostLoginScreen();
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
// This is async but we makign this function async to wait for it isn't useful
|
||||
this.onShowPostLoginScreen().catch((e) => {
|
||||
logger.error("Exception showing post-login screen", e);
|
||||
});
|
||||
}
|
||||
// This is async but we making this function async to wait for it isn't useful
|
||||
this.onShowPostLoginScreen().catch((e) => {
|
||||
logger.error("Exception showing post-login screen", e);
|
||||
});
|
||||
};
|
||||
|
||||
private getFragmentAfterLogin(): string {
|
||||
|
@ -2156,8 +2125,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
fragmentAfterLogin={fragmentAfterLogin}
|
||||
/>
|
||||
);
|
||||
} else if (this.state.view === Views.USE_CASE_SELECTION) {
|
||||
view = <UseCaseSelection onFinished={(useCase): Promise<void> => this.onShowPostLoginScreen(useCase)} />;
|
||||
} else if (this.state.view === Views.LOCK_STOLEN) {
|
||||
view = <SessionLockStolenView />;
|
||||
} else {
|
||||
|
|
|
@ -17,7 +17,7 @@ import RightPanel from "./RightPanel";
|
|||
import Spinner from "../views/elements/Spinner";
|
||||
import ResizeNotifier from "../../utils/ResizeNotifier";
|
||||
import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases";
|
||||
import { UserOnboardingPage } from "../views/user-onboarding/UserOnboardingPage";
|
||||
import HomePage from "./HomePage.tsx";
|
||||
import MatrixClientContext from "../../contexts/MatrixClientContext";
|
||||
|
||||
interface IProps {
|
||||
|
@ -93,7 +93,7 @@ export default class UserView extends React.Component<IProps, IState> {
|
|||
defaultSize={420}
|
||||
analyticsRoomType="user_profile"
|
||||
>
|
||||
<UserOnboardingPage />
|
||||
<HomePage />
|
||||
</MainSplit>
|
||||
);
|
||||
} else {
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { FC } from "react";
|
||||
|
||||
import { Icon as FDroidBadge } from "../../../../res/img/badges/f-droid.svg";
|
||||
import { Icon as GooglePlayBadge } from "../../../../res/img/badges/google-play.svg";
|
||||
import { Icon as IOSBadge } from "../../../../res/img/badges/ios.svg";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import QRCode from "../elements/QRCode";
|
||||
import Heading from "../typography/Heading";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
|
||||
interface Props {
|
||||
onFinished(): void;
|
||||
}
|
||||
|
||||
export const showAppDownloadDialogPrompt = (): boolean => {
|
||||
const desktopBuilds = SdkConfig.getObject("desktop_builds");
|
||||
const mobileBuilds = SdkConfig.getObject("mobile_builds");
|
||||
|
||||
return (
|
||||
!!desktopBuilds?.get("available") ||
|
||||
!!mobileBuilds?.get("ios") ||
|
||||
!!mobileBuilds?.get("android") ||
|
||||
!!mobileBuilds?.get("fdroid")
|
||||
);
|
||||
};
|
||||
|
||||
export const AppDownloadDialog: FC<Props> = ({ onFinished }) => {
|
||||
const brand = SdkConfig.get("brand");
|
||||
const desktopBuilds = SdkConfig.getObject("desktop_builds");
|
||||
const mobileBuilds = SdkConfig.getObject("mobile_builds");
|
||||
|
||||
const urlAppStore = mobileBuilds?.get("ios");
|
||||
|
||||
const urlGooglePlay = mobileBuilds?.get("android");
|
||||
const urlFDroid = mobileBuilds?.get("fdroid");
|
||||
const urlAndroid = urlGooglePlay ?? urlFDroid;
|
||||
|
||||
return (
|
||||
<BaseDialog
|
||||
title={_t("onboarding|download_brand", { brand })}
|
||||
className="mx_AppDownloadDialog"
|
||||
fixedWidth
|
||||
onFinished={onFinished}
|
||||
>
|
||||
{desktopBuilds?.get("available") && (
|
||||
<div className="mx_AppDownloadDialog_desktop">
|
||||
<Heading size="3">{_t("onboarding|download_brand_desktop", { brand })}</Heading>
|
||||
<AccessibleButton
|
||||
kind="primary"
|
||||
element="a"
|
||||
href={desktopBuilds?.get("url")}
|
||||
target="_blank"
|
||||
onClick={() => {}}
|
||||
>
|
||||
{_t("onboarding|download_brand_desktop", { brand })}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
)}
|
||||
<div className="mx_AppDownloadDialog_mobile">
|
||||
{urlAppStore && (
|
||||
<div className="mx_AppDownloadDialog_app">
|
||||
<Heading size="3">{_t("common|ios")}</Heading>
|
||||
<QRCode data={urlAppStore} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("onboarding|qr_or_app_links", {
|
||||
appLinks: "",
|
||||
qrCode: "",
|
||||
})}
|
||||
</div>
|
||||
<div className="mx_AppDownloadDialog_links">
|
||||
<AccessibleButton
|
||||
element="a"
|
||||
href={urlAppStore}
|
||||
target="_blank"
|
||||
aria-label={_t("onboarding|download_app_store")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<IOSBadge />
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{urlAndroid && (
|
||||
<div className="mx_AppDownloadDialog_app">
|
||||
<Heading size="3">{_t("common|android")}</Heading>
|
||||
<QRCode data={urlAndroid} margin={0} width={172} />
|
||||
<div className="mx_AppDownloadDialog_info">
|
||||
{_t("onboarding|qr_or_app_links", {
|
||||
appLinks: "",
|
||||
qrCode: "",
|
||||
})}
|
||||
</div>
|
||||
<div className="mx_AppDownloadDialog_links">
|
||||
{urlGooglePlay && (
|
||||
<AccessibleButton
|
||||
element="a"
|
||||
href={urlGooglePlay}
|
||||
target="_blank"
|
||||
aria-label={_t("onboarding|download_google_play")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<GooglePlayBadge />
|
||||
</AccessibleButton>
|
||||
)}
|
||||
{urlFDroid && (
|
||||
<AccessibleButton
|
||||
element="a"
|
||||
href={urlFDroid}
|
||||
target="_blank"
|
||||
aria-label={_t("onboarding|download_f_droid")}
|
||||
onClick={() => {}}
|
||||
>
|
||||
<FDroidBadge />
|
||||
</AccessibleButton>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mx_AppDownloadDialog_legal">
|
||||
<p>{_t("onboarding|apple_trademarks")}</p>
|
||||
<p>{_t("onboarding|google_trademarks")}</p>
|
||||
</div>
|
||||
</BaseDialog>
|
||||
);
|
||||
};
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { UseCase } from "../../../settings/enums/UseCase";
|
||||
import SplashPage from "../../structures/SplashPage";
|
||||
import AccessibleButton from "../elements/AccessibleButton";
|
||||
import { UseCaseSelectionButton } from "./UseCaseSelectionButton";
|
||||
|
||||
interface Props {
|
||||
onFinished: (useCase: UseCase) => void;
|
||||
}
|
||||
|
||||
const TIMEOUT = 1500;
|
||||
|
||||
export function UseCaseSelection({ onFinished }: Props): JSX.Element {
|
||||
const [selection, setSelected] = useState<UseCase | null>(null);
|
||||
|
||||
// Call onFinished 1.5s after `selection` becomes truthy, to give time for the animation to run
|
||||
useEffect(() => {
|
||||
if (selection) {
|
||||
let handler: number | null = window.setTimeout(() => {
|
||||
handler = null;
|
||||
onFinished(selection);
|
||||
}, TIMEOUT);
|
||||
return () => {
|
||||
if (handler !== null) clearTimeout(handler);
|
||||
handler = null;
|
||||
};
|
||||
}
|
||||
}, [selection, onFinished]);
|
||||
|
||||
return (
|
||||
<SplashPage
|
||||
className={classNames("mx_UseCaseSelection", {
|
||||
mx_UseCaseSelection_selected: selection !== null,
|
||||
})}
|
||||
>
|
||||
<div className="mx_UseCaseSelection_title mx_UseCaseSelection_slideIn">
|
||||
<h1>{_t("onboarding|use_case_heading1")}</h1>
|
||||
</div>
|
||||
<div className="mx_UseCaseSelection_info mx_UseCaseSelection_slideInDelayed">
|
||||
<h2>{_t("onboarding|use_case_heading2")}</h2>
|
||||
<h3>{_t("onboarding|use_case_heading3")}</h3>
|
||||
</div>
|
||||
<div className="mx_UseCaseSelection_options mx_UseCaseSelection_slideInDelayed">
|
||||
<UseCaseSelectionButton
|
||||
useCase={UseCase.PersonalMessaging}
|
||||
selected={selection === UseCase.PersonalMessaging}
|
||||
onClick={setSelected}
|
||||
/>
|
||||
<UseCaseSelectionButton
|
||||
useCase={UseCase.WorkMessaging}
|
||||
selected={selection === UseCase.WorkMessaging}
|
||||
onClick={setSelected}
|
||||
/>
|
||||
<UseCaseSelectionButton
|
||||
useCase={UseCase.CommunityMessaging}
|
||||
selected={selection === UseCase.CommunityMessaging}
|
||||
onClick={setSelected}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx_UseCaseSelection_skip mx_UseCaseSelection_slideInDelayed">
|
||||
<AccessibleButton kind="link" onClick={async () => setSelected(UseCase.Skip)}>
|
||||
{_t("action|skip")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</SplashPage>
|
||||
);
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { UseCase } from "../../../settings/enums/UseCase";
|
||||
import AccessibleButton from "./AccessibleButton";
|
||||
|
||||
interface Props {
|
||||
useCase: UseCase;
|
||||
selected: boolean;
|
||||
onClick: (useCase: UseCase) => void;
|
||||
}
|
||||
|
||||
export function UseCaseSelectionButton({ useCase, onClick, selected }: Props): JSX.Element {
|
||||
let label: string | undefined;
|
||||
switch (useCase) {
|
||||
case UseCase.PersonalMessaging:
|
||||
label = _t("onboarding|use_case_personal_messaging");
|
||||
break;
|
||||
case UseCase.WorkMessaging:
|
||||
label = _t("onboarding|use_case_work_messaging");
|
||||
break;
|
||||
case UseCase.CommunityMessaging:
|
||||
label = _t("onboarding|use_case_community_messaging");
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<AccessibleButton
|
||||
className={classNames("mx_UseCaseSelectionButton", {
|
||||
mx_UseCaseSelectionButton_selected: selected,
|
||||
})}
|
||||
onClick={async () => onClick(useCase)}
|
||||
>
|
||||
<div
|
||||
className={classNames("mx_UseCaseSelectionButton_icon", {
|
||||
mx_UseCaseSelectionButton_messaging: useCase === UseCase.PersonalMessaging,
|
||||
mx_UseCaseSelectionButton_work: useCase === UseCase.WorkMessaging,
|
||||
mx_UseCaseSelectionButton_community: useCase === UseCase.CommunityMessaging,
|
||||
})}
|
||||
/>
|
||||
<span>{label}</span>
|
||||
<div className="mx_UseCaseSelectionButton_selectedIcon" />
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
|
@ -22,7 +22,6 @@ import { UserTab } from "../../../dialogs/UserTab";
|
|||
import { OpenToTabPayload } from "../../../../../dispatcher/payloads/OpenToTabPayload";
|
||||
import { Action } from "../../../../../dispatcher/actions";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import { showUserOnboardingPage } from "../../../user-onboarding/UserOnboardingPage";
|
||||
import { SettingsSubsection } from "../../shared/SettingsSubsection";
|
||||
import SettingsTab from "../SettingsTab";
|
||||
import { SettingsSection } from "../../shared/SettingsSection";
|
||||
|
@ -117,7 +116,7 @@ const SpellCheckSection: React.FC = () => {
|
|||
};
|
||||
|
||||
export default class PreferencesUserSettingsTab extends React.Component<IProps, IState> {
|
||||
private static ROOM_LIST_SETTINGS: BooleanSettingKey[] = ["breadcrumbs", "FTUE.userOnboardingButton"];
|
||||
private static ROOM_LIST_SETTINGS: BooleanSettingKey[] = ["breadcrumbs"];
|
||||
|
||||
private static SPACES_SETTINGS: BooleanSettingKey[] = ["Spaces.allRoomsInHome"];
|
||||
|
||||
|
@ -237,10 +236,7 @@ export default class PreferencesUserSettingsTab extends React.Component<IProps,
|
|||
};
|
||||
|
||||
public render(): React.ReactNode {
|
||||
const useCase = SettingsStore.getValue("FTUE.useCaseSelection");
|
||||
const roomListSettings = PreferencesUserSettingsTab.ROOM_LIST_SETTINGS
|
||||
// Only show the user onboarding setting if the user should see the user onboarding page
|
||||
.filter((it) => it !== "FTUE.userOnboardingButton" || showUserOnboardingPage(useCase));
|
||||
const roomListSettings = PreferencesUserSettingsTab.ROOM_LIST_SETTINGS;
|
||||
|
||||
const browserTimezoneLabel: string = _t("settings|preferences|default_timezone", {
|
||||
timezone: TimezoneHandler.shortBrowserTimezone(),
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
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 { _t } from "../../../languageHandler";
|
||||
import PosthogTrackers from "../../../PosthogTrackers";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
||||
import Heading from "../../views/typography/Heading";
|
||||
import { showUserOnboardingPage } from "./UserOnboardingPage";
|
||||
|
||||
interface Props {
|
||||
selected: boolean;
|
||||
minimized: boolean;
|
||||
}
|
||||
|
||||
export function UserOnboardingButton({ selected, minimized }: Props): JSX.Element {
|
||||
const useCase = useSettingValue("FTUE.useCaseSelection");
|
||||
const visible = useSettingValue("FTUE.userOnboardingButton");
|
||||
|
||||
if (!visible || minimized || !showUserOnboardingPage(useCase)) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return <UserOnboardingButtonInternal selected={selected} minimized={minimized} />;
|
||||
}
|
||||
|
||||
function UserOnboardingButtonInternal({ selected, minimized }: Props): JSX.Element {
|
||||
const onDismiss = useCallback((ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
PosthogTrackers.trackInteraction("WebRoomListUserOnboardingIgnoreButton", ev);
|
||||
SettingsStore.setValue("FTUE.userOnboardingButton", null, SettingLevel.ACCOUNT, false);
|
||||
}, []);
|
||||
|
||||
const onClick = useCallback((ev: ButtonEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
PosthogTrackers.trackInteraction("WebRoomListUserOnboardingButton", ev);
|
||||
defaultDispatcher.fire(Action.ViewHomePage);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AccessibleButton
|
||||
className={classNames("mx_UserOnboardingButton", {
|
||||
mx_UserOnboardingButton_selected: selected,
|
||||
mx_UserOnboardingButton_minimized: minimized,
|
||||
})}
|
||||
onClick={onClick}
|
||||
>
|
||||
{!minimized && (
|
||||
<>
|
||||
<div className="mx_UserOnboardingButton_content">
|
||||
<Heading size="4" className="mx_Heading_h4">
|
||||
{_t("common|welcome")}
|
||||
</Heading>
|
||||
<AccessibleButton
|
||||
className="mx_UserOnboardingButton_close"
|
||||
onClick={onDismiss}
|
||||
aria-label={_t("action|dismiss")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</AccessibleButton>
|
||||
);
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import defaultDispatcher from "../../../dispatcher/dispatcher";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import PosthogTrackers from "../../../PosthogTrackers";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import { UseCase } from "../../../settings/enums/UseCase";
|
||||
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
|
||||
import Heading from "../../views/typography/Heading";
|
||||
|
||||
const onClickSendDm = (ev: ButtonEvent): void => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingHeaderSendDm", ev);
|
||||
defaultDispatcher.dispatch({ action: "view_create_chat" });
|
||||
};
|
||||
|
||||
interface Props {
|
||||
useCase: UseCase | null;
|
||||
}
|
||||
|
||||
export function UserOnboardingHeader({ useCase }: Props): JSX.Element {
|
||||
let title: string;
|
||||
let description = _t("onboarding|free_e2ee_messaging_unlimited_voip", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
});
|
||||
let image: string;
|
||||
let actionLabel: string;
|
||||
|
||||
switch (useCase) {
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
case UseCase.PersonalMessaging:
|
||||
title = _t("onboarding|personal_messaging_title");
|
||||
image = require("../../../../res/img/user-onboarding/PersonalMessaging.png");
|
||||
actionLabel = _t("onboarding|personal_messaging_action");
|
||||
break;
|
||||
case UseCase.WorkMessaging:
|
||||
title = _t("onboarding|work_messaging_title");
|
||||
description = _t("onboarding|free_e2ee_messaging_unlimited_voip", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
});
|
||||
image = require("../../../../res/img/user-onboarding/WorkMessaging.png");
|
||||
actionLabel = _t("onboarding|work_messaging_action");
|
||||
break;
|
||||
case UseCase.CommunityMessaging:
|
||||
title = _t("onboarding|community_messaging_title");
|
||||
description = _t("onboarding|community_messaging_description");
|
||||
image = require("../../../../res/img/user-onboarding/CommunityMessaging.png");
|
||||
actionLabel = _t("onboarding|community_messaging_action");
|
||||
break;
|
||||
default:
|
||||
title = _t("onboarding|welcome_to_brand", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
});
|
||||
image = require("../../../../res/img/user-onboarding/PersonalMessaging.png");
|
||||
actionLabel = _t("onboarding|personal_messaging_action");
|
||||
break;
|
||||
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_UserOnboardingHeader">
|
||||
<div className="mx_UserOnboardingHeader_content">
|
||||
<Heading size="1">
|
||||
{title}
|
||||
<span className="mx_UserOnboardingHeader_dot">.</span>
|
||||
</Heading>
|
||||
<p>{description}</p>
|
||||
<AccessibleButton onClick={onClickSendDm} kind="primary">
|
||||
{actionLabel}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
<img className="mx_UserOnboardingHeader_image" src={image} alt="" />
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import * as React from "react";
|
||||
|
||||
import { UserOnboardingTaskWithResolvedCompletion } from "../../../hooks/useUserOnboardingTasks";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import ProgressBar from "../../views/elements/ProgressBar";
|
||||
import Heading from "../../views/typography/Heading";
|
||||
import { UserOnboardingTask } from "./UserOnboardingTask";
|
||||
|
||||
export const getUserOnboardingCounters = (
|
||||
tasks: UserOnboardingTaskWithResolvedCompletion[],
|
||||
): {
|
||||
completed: number;
|
||||
waiting: number;
|
||||
total: number;
|
||||
} => {
|
||||
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 {
|
||||
tasks: UserOnboardingTaskWithResolvedCompletion[];
|
||||
}
|
||||
|
||||
export function UserOnboardingList({ tasks }: Props): JSX.Element {
|
||||
const { completed, waiting, total } = getUserOnboardingCounters(tasks);
|
||||
|
||||
return (
|
||||
<div className="mx_UserOnboardingList" data-testid="user-onboarding-list">
|
||||
<div className="mx_UserOnboardingList_header">
|
||||
<Heading size="3" className="mx_UserOnboardingList_title">
|
||||
{waiting > 0
|
||||
? _t("onboarding|only_n_steps_to_go", {
|
||||
count: waiting,
|
||||
})
|
||||
: _t("onboarding|you_did_it")}
|
||||
</Heading>
|
||||
<div className="mx_UserOnboardingList_hint">
|
||||
{_t("onboarding|complete_these", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx_UserOnboardingList_progress">
|
||||
<ProgressBar value={completed} max={total} animated />
|
||||
</div>
|
||||
<ol className="mx_UserOnboardingList_list">
|
||||
{tasks.map((task) => (
|
||||
<UserOnboardingTask key={task.id} completed={task.completed} task={task} />
|
||||
))}
|
||||
</ol>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2020-2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import { useInitialSyncComplete } from "../../../hooks/useIsInitialSyncComplete";
|
||||
import { useSettingValue } from "../../../hooks/useSettings";
|
||||
import { useUserOnboardingContext } from "../../../hooks/useUserOnboardingContext";
|
||||
import { useUserOnboardingTasks } from "../../../hooks/useUserOnboardingTasks";
|
||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
import { UseCase } from "../../../settings/enums/UseCase";
|
||||
import { getHomePageUrl } from "../../../utils/pages";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
import EmbeddedPage from "../../structures/EmbeddedPage";
|
||||
import HomePage from "../../structures/HomePage";
|
||||
import { UserOnboardingHeader } from "./UserOnboardingHeader";
|
||||
import { UserOnboardingList } from "./UserOnboardingList";
|
||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||
|
||||
interface Props {
|
||||
justRegistered?: boolean;
|
||||
}
|
||||
|
||||
// We decided to only show the new user onboarding page to new users
|
||||
// For now, that means we set the cutoff at 2022-07-01 00:00 UTC
|
||||
const USER_ONBOARDING_CUTOFF_DATE = new Date(1_656_633_600);
|
||||
export function showUserOnboardingPage(useCase: UseCase | null): boolean {
|
||||
return useCase !== null || MatrixClientPeg.userRegisteredAfter(USER_ONBOARDING_CUTOFF_DATE);
|
||||
}
|
||||
|
||||
const ANIMATION_DURATION = 2800;
|
||||
export function UserOnboardingPage({ justRegistered = false }: Props): JSX.Element {
|
||||
const cli = useMatrixClientContext();
|
||||
const config = SdkConfig.get();
|
||||
const pageUrl = getHomePageUrl(config, cli);
|
||||
|
||||
const useCase = useSettingValue("FTUE.useCaseSelection");
|
||||
const context = useUserOnboardingContext();
|
||||
const tasks = useUserOnboardingTasks(context);
|
||||
|
||||
const initialSyncComplete = useInitialSyncComplete();
|
||||
const [showList, setShowList] = useState<boolean>(false);
|
||||
useEffect(() => {
|
||||
if (initialSyncComplete) {
|
||||
const handler = window.setTimeout(() => {
|
||||
setShowList(true);
|
||||
}, ANIMATION_DURATION);
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
} else {
|
||||
setShowList(false);
|
||||
}
|
||||
}, [initialSyncComplete, setShowList]);
|
||||
|
||||
// Only show new onboarding list to users who registered after a given date or have chosen a use case
|
||||
if (!showUserOnboardingPage(useCase)) {
|
||||
return <HomePage justRegistered={justRegistered} />;
|
||||
}
|
||||
|
||||
if (pageUrl) {
|
||||
return <EmbeddedPage className="mx_HomePage" url={pageUrl} scrollbar={true} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoHideScrollbar className="mx_UserOnboardingPage">
|
||||
<UserOnboardingHeader useCase={useCase} />
|
||||
{showList && <UserOnboardingList tasks={tasks} />}
|
||||
</AutoHideScrollbar>
|
||||
);
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import classNames from "classnames";
|
||||
import * as React from "react";
|
||||
|
||||
import { UserOnboardingTaskWithResolvedCompletion } from "../../../hooks/useUserOnboardingTasks";
|
||||
import AccessibleButton from "../../views/elements/AccessibleButton";
|
||||
import Heading from "../../views/typography/Heading";
|
||||
|
||||
interface Props {
|
||||
task: UserOnboardingTaskWithResolvedCompletion;
|
||||
completed?: boolean;
|
||||
}
|
||||
|
||||
export function UserOnboardingTask({ task, completed = false }: Props): JSX.Element {
|
||||
const title = typeof task.title === "function" ? task.title() : task.title;
|
||||
const description = typeof task.description === "function" ? task.description() : task.description;
|
||||
|
||||
return (
|
||||
<li
|
||||
data-testid="user-onboarding-task"
|
||||
className={classNames("mx_UserOnboardingTask", {
|
||||
mx_UserOnboardingTask_completed: completed,
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className="mx_UserOnboardingTask_number"
|
||||
role="checkbox"
|
||||
aria-disabled="true"
|
||||
aria-checked={completed}
|
||||
aria-labelledby={`mx_UserOnboardingTask_${task.id}`}
|
||||
/>
|
||||
<div id={`mx_UserOnboardingTask_${task.id}`} className="mx_UserOnboardingTask_content">
|
||||
<Heading size="4" className="mx_UserOnboardingTask_title">
|
||||
{title}
|
||||
</Heading>
|
||||
<div className="mx_UserOnboardingTask_description">{description}</div>
|
||||
</div>
|
||||
{task.action && (!task.action.hideOnComplete || !completed) && (
|
||||
<AccessibleButton
|
||||
element="a"
|
||||
className="mx_UserOnboardingTask_action"
|
||||
kind="primary_outline"
|
||||
href={task.action.href}
|
||||
target="_blank"
|
||||
onClick={task.action.onClick ?? null}
|
||||
>
|
||||
{task.action.label}
|
||||
</AccessibleButton>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { ClientEvent } from "matrix-js-sdk/src/matrix";
|
||||
|
||||
import { useEventEmitterState } from "./useEventEmitter";
|
||||
import { useMatrixClientContext } from "../contexts/MatrixClientContext";
|
||||
|
||||
export function useInitialSyncComplete(): boolean {
|
||||
const cli = useMatrixClientContext();
|
||||
return useEventEmitterState(cli, ClientEvent.Sync, () => cli.isInitialSyncComplete());
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
import { ClientEvent, MatrixClient } from "matrix-js-sdk/src/matrix";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
import { Notifier, NotifierEvent } from "../Notifier";
|
||||
import DMRoomMap from "../utils/DMRoomMap";
|
||||
import { useMatrixClientContext } from "../contexts/MatrixClientContext";
|
||||
import { useSettingValue } from "./useSettings";
|
||||
import { useEventEmitter, useTypedEventEmitter } from "./useEventEmitter";
|
||||
|
||||
export interface UserOnboardingContext {
|
||||
hasAvatar: boolean;
|
||||
hasDevices: boolean;
|
||||
hasDmRooms: boolean;
|
||||
showNotificationsPrompt: boolean;
|
||||
}
|
||||
|
||||
const USER_ONBOARDING_CONTEXT_INTERVAL = 5000;
|
||||
|
||||
/**
|
||||
* Returns a persistent, non-changing reference to a function
|
||||
* This function proxies all its calls to the current value of the given input callback
|
||||
*
|
||||
* This allows you to use the current value of e.g., a state in a callback that’s used by e.g., a useEventEmitter or
|
||||
* similar hook without re-registering the hook when the state changes
|
||||
* @param value changing callback
|
||||
*/
|
||||
function useRefOf<T extends any[], R>(value: (...values: T) => R): (...values: T) => R {
|
||||
const ref = useRef(value);
|
||||
ref.current = value;
|
||||
return useCallback((...values: T) => ref.current(...values), []);
|
||||
}
|
||||
|
||||
function useUserOnboardingContextValue<T>(defaultValue: T, callback: (cli: MatrixClient) => Promise<T>): T {
|
||||
const [value, setValue] = useState<T>(defaultValue);
|
||||
const cli = useMatrixClientContext();
|
||||
|
||||
const handler = useRefOf(callback);
|
||||
|
||||
useEffect(() => {
|
||||
if (value) {
|
||||
return;
|
||||
}
|
||||
|
||||
let handle: number | null = null;
|
||||
let enabled = true;
|
||||
const repeater = async (): Promise<void> => {
|
||||
if (handle !== null) {
|
||||
clearTimeout(handle);
|
||||
handle = null;
|
||||
}
|
||||
setValue(await handler(cli));
|
||||
if (enabled) {
|
||||
handle = window.setTimeout(repeater, USER_ONBOARDING_CONTEXT_INTERVAL);
|
||||
}
|
||||
};
|
||||
repeater().catch((err) => logger.warn("could not update user onboarding context", err));
|
||||
cli.on(ClientEvent.AccountData, repeater);
|
||||
return () => {
|
||||
enabled = false;
|
||||
cli.off(ClientEvent.AccountData, repeater);
|
||||
if (handle !== null) {
|
||||
clearTimeout(handle);
|
||||
handle = null;
|
||||
}
|
||||
};
|
||||
}, [cli, handler, value]);
|
||||
return value;
|
||||
}
|
||||
|
||||
function useShowNotificationsPrompt(): boolean {
|
||||
const client = useMatrixClientContext();
|
||||
|
||||
const [value, setValue] = useState<boolean>(client.pushRules ? Notifier.shouldShowPrompt() : true);
|
||||
|
||||
const updateValue = useCallback(() => {
|
||||
setValue(client.pushRules ? Notifier.shouldShowPrompt() : true);
|
||||
}, [client]);
|
||||
|
||||
useEventEmitter(Notifier, NotifierEvent.NotificationHiddenChange, () => {
|
||||
updateValue();
|
||||
});
|
||||
|
||||
const setting = useSettingValue("notificationsEnabled");
|
||||
useEffect(() => {
|
||||
updateValue();
|
||||
}, [setting, updateValue]);
|
||||
|
||||
// shouldShowPrompt is dependent on the client having push rules. There isn't an event for the client
|
||||
// fetching its push rules, but we'll know it has them by the time it sync, so we update this on sync.
|
||||
useTypedEventEmitter(client, ClientEvent.Sync, updateValue);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useUserOnboardingContext(): UserOnboardingContext {
|
||||
const hasAvatar = useUserOnboardingContextValue(false, async (cli) => {
|
||||
const profile = await cli.getProfileInfo(cli.getUserId()!);
|
||||
return Boolean(profile?.avatar_url);
|
||||
});
|
||||
const hasDevices = useUserOnboardingContextValue(false, async (cli) => {
|
||||
const myDevice = cli.getDeviceId();
|
||||
const devices = await cli.getDevices();
|
||||
return Boolean(devices.devices.find((device) => device.device_id !== myDevice));
|
||||
});
|
||||
const hasDmRooms = useUserOnboardingContextValue(false, async () => {
|
||||
const dmRooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals() ?? {};
|
||||
return Boolean(Object.keys(dmRooms).length);
|
||||
});
|
||||
const showNotificationsPrompt = useShowNotificationsPrompt();
|
||||
|
||||
return useMemo(
|
||||
() => ({ hasAvatar, hasDevices, hasDmRooms, showNotificationsPrompt }),
|
||||
[hasAvatar, hasDevices, hasDmRooms, showNotificationsPrompt],
|
||||
);
|
||||
}
|
|
@ -1,161 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { AppDownloadDialog, showAppDownloadDialogPrompt } from "../components/views/dialogs/AppDownloadDialog";
|
||||
import { UserTab } from "../components/views/dialogs/UserTab";
|
||||
import { ButtonEvent } from "../components/views/elements/AccessibleButton";
|
||||
import { Action } from "../dispatcher/actions";
|
||||
import defaultDispatcher from "../dispatcher/dispatcher";
|
||||
import { _t } from "../languageHandler";
|
||||
import Modal from "../Modal";
|
||||
import { Notifier } from "../Notifier";
|
||||
import PosthogTrackers from "../PosthogTrackers";
|
||||
import SdkConfig from "../SdkConfig";
|
||||
import { UseCase } from "../settings/enums/UseCase";
|
||||
import { useSettingValue } from "./useSettings";
|
||||
import { UserOnboardingContext } from "./useUserOnboardingContext";
|
||||
|
||||
interface UserOnboardingTask {
|
||||
id: string;
|
||||
title: string | (() => string);
|
||||
description: string | (() => string);
|
||||
relevant?: UseCase[];
|
||||
action?: {
|
||||
label: string;
|
||||
onClick?: (ev: ButtonEvent) => void;
|
||||
href?: string;
|
||||
hideOnComplete?: boolean;
|
||||
};
|
||||
completed: (ctx: UserOnboardingContext) => boolean;
|
||||
disabled?(): boolean;
|
||||
}
|
||||
|
||||
export interface UserOnboardingTaskWithResolvedCompletion extends Omit<UserOnboardingTask, "completed"> {
|
||||
completed: boolean;
|
||||
}
|
||||
|
||||
const onClickStartDm = (ev: ButtonEvent): void => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingTaskSendDm", ev);
|
||||
defaultDispatcher.dispatch({ action: "view_create_chat" });
|
||||
};
|
||||
|
||||
const tasks: UserOnboardingTask[] = [
|
||||
{
|
||||
id: "create-account",
|
||||
title: _t("auth|create_account_title"),
|
||||
description: _t("onboarding|you_made_it"),
|
||||
completed: () => true,
|
||||
},
|
||||
{
|
||||
id: "find-friends",
|
||||
title: _t("onboarding|find_friends"),
|
||||
description: _t("onboarding|find_friends_description"),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasDmRooms,
|
||||
relevant: [UseCase.PersonalMessaging, UseCase.Skip],
|
||||
action: {
|
||||
label: _t("onboarding|find_friends_action"),
|
||||
onClick: onClickStartDm,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "find-coworkers",
|
||||
title: _t("onboarding|find_coworkers"),
|
||||
description: _t("onboarding|get_stuff_done"),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasDmRooms,
|
||||
relevant: [UseCase.WorkMessaging],
|
||||
action: {
|
||||
label: _t("onboarding|find_people"),
|
||||
onClick: onClickStartDm,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "find-community-members",
|
||||
title: _t("onboarding|find_community_members"),
|
||||
description: _t("onboarding|get_stuff_done"),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasDmRooms,
|
||||
relevant: [UseCase.CommunityMessaging],
|
||||
action: {
|
||||
label: _t("onboarding|find_people"),
|
||||
onClick: onClickStartDm,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "download-apps",
|
||||
title: () =>
|
||||
_t("onboarding|download_app", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
description: () =>
|
||||
_t("onboarding|download_app_description", {
|
||||
brand: SdkConfig.get("brand"),
|
||||
}),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasDevices,
|
||||
action: {
|
||||
label: _t("onboarding|download_app_action"),
|
||||
onClick: (ev: ButtonEvent) => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingTaskDownloadApps", ev);
|
||||
Modal.createDialog(AppDownloadDialog, {}, "mx_AppDownloadDialog_wrapper", false, true);
|
||||
},
|
||||
},
|
||||
disabled(): boolean {
|
||||
return !showAppDownloadDialogPrompt();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "setup-profile",
|
||||
title: _t("onboarding|set_up_profile"),
|
||||
description: _t("onboarding|set_up_profile_description"),
|
||||
completed: (ctx: UserOnboardingContext) => ctx.hasAvatar,
|
||||
action: {
|
||||
label: _t("onboarding|set_up_profile_action"),
|
||||
onClick: (ev: ButtonEvent) => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingTaskSetupProfile", ev);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Account,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "permission-notifications",
|
||||
title: _t("onboarding|enable_notifications"),
|
||||
description: _t("onboarding|enable_notifications_description"),
|
||||
completed: (ctx: UserOnboardingContext) => !ctx.showNotificationsPrompt,
|
||||
action: {
|
||||
label: _t("onboarding|enable_notifications_action"),
|
||||
onClick: (ev: ButtonEvent) => {
|
||||
PosthogTrackers.trackInteraction("WebUserOnboardingTaskEnableNotifications", ev);
|
||||
defaultDispatcher.dispatch({
|
||||
action: Action.ViewUserSettings,
|
||||
initialTabId: UserTab.Notifications,
|
||||
});
|
||||
Notifier.setPromptHidden(true);
|
||||
},
|
||||
hideOnComplete: !Notifier.isPossible(),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function useUserOnboardingTasks(context: UserOnboardingContext): UserOnboardingTaskWithResolvedCompletion[] {
|
||||
const useCase = useSettingValue("FTUE.useCaseSelection") ?? UseCase.Skip;
|
||||
|
||||
return useMemo<UserOnboardingTaskWithResolvedCompletion[]>(() => {
|
||||
return tasks
|
||||
.filter((task) => {
|
||||
if (task.disabled?.()) return false;
|
||||
return !task.relevant || task.relevant.includes(useCase);
|
||||
})
|
||||
.map((task) => ({
|
||||
...task,
|
||||
completed: task.completed(context),
|
||||
}));
|
||||
}, [context, useCase]);
|
||||
}
|
|
@ -451,7 +451,6 @@
|
|||
"one": "and one other...",
|
||||
"other": "and %(count)s others..."
|
||||
},
|
||||
"android": "Android",
|
||||
"appearance": "Appearance",
|
||||
"application": "Application",
|
||||
"are_you_sure": "Are you sure?",
|
||||
|
@ -491,7 +490,6 @@
|
|||
"identity_server": "Identity server",
|
||||
"image": "Image",
|
||||
"integration_manager": "Integration manager",
|
||||
"ios": "iOS",
|
||||
"joined": "Joined",
|
||||
"labs": "Labs",
|
||||
"legal": "Legal",
|
||||
|
@ -585,8 +583,7 @@
|
|||
"video": "Video",
|
||||
"video_room": "Video room",
|
||||
"view_message": "View message",
|
||||
"warning": "Warning",
|
||||
"welcome": "Welcome"
|
||||
"warning": "Warning"
|
||||
},
|
||||
"composer": {
|
||||
"autocomplete": {
|
||||
|
@ -1628,61 +1625,15 @@
|
|||
"m.key.verification.request": "%(name)s is requesting verification"
|
||||
},
|
||||
"onboarding": {
|
||||
"apple_trademarks": "App Store® and the Apple logo® are trademarks of Apple Inc.",
|
||||
"community_messaging_action": "Find your people",
|
||||
"community_messaging_description": "Keep ownership and control of community discussion.\nScale to support millions, with powerful moderation and interoperability.",
|
||||
"community_messaging_title": "Community ownership",
|
||||
"complete_these": "Complete these to get the most out of %(brand)s",
|
||||
"create_room": "Create a Group Chat",
|
||||
"download_app": "Download %(brand)s",
|
||||
"download_app_action": "Download apps",
|
||||
"download_app_description": "Don’t miss a thing by taking %(brand)s with you",
|
||||
"download_app_store": "Download on the App Store",
|
||||
"download_brand": "Download %(brand)s",
|
||||
"download_brand_desktop": "Download %(brand)s Desktop",
|
||||
"download_f_droid": "Get it on F-Droid",
|
||||
"download_google_play": "Get it on Google Play",
|
||||
"enable_notifications": "Turn on desktop notifications",
|
||||
"enable_notifications_action": "Open settings",
|
||||
"enable_notifications_description": "Don’t miss a reply or important message",
|
||||
"explore_rooms": "Explore Public Rooms",
|
||||
"find_community_members": "Find and invite your community members",
|
||||
"find_coworkers": "Find and invite your co-workers",
|
||||
"find_friends": "Find and invite your friends",
|
||||
"find_friends_action": "Find friends",
|
||||
"find_friends_description": "It’s what you’re here for, so lets get to it",
|
||||
"find_people": "Find people",
|
||||
"free_e2ee_messaging_unlimited_voip": "With free end-to-end encrypted messaging, and unlimited voice and video calls, %(brand)s is a great way to stay in touch.",
|
||||
"get_stuff_done": "Get stuff done by finding your teammates",
|
||||
"google_trademarks": "Google Play and the Google Play logo are trademarks of Google LLC.",
|
||||
"has_avatar_label": "Great, that'll help people know it's you",
|
||||
"intro_byline": "Own your conversations.",
|
||||
"intro_welcome": "Welcome to %(appName)s",
|
||||
"no_avatar_label": "Add a photo so people know it's you.",
|
||||
"only_n_steps_to_go": {
|
||||
"one": "Only %(count)s step to go",
|
||||
"other": "Only %(count)s steps to go"
|
||||
},
|
||||
"personal_messaging_action": "Start your first chat",
|
||||
"personal_messaging_title": "Secure messaging for friends and family",
|
||||
"qr_or_app_links": "%(qrCode)s or %(appLinks)s",
|
||||
"send_dm": "Send a Direct Message",
|
||||
"set_up_profile": "Set up your profile",
|
||||
"set_up_profile_action": "Your profile",
|
||||
"set_up_profile_description": "Make sure people know it’s really you",
|
||||
"use_case_community_messaging": "Online community members",
|
||||
"use_case_heading1": "You're in",
|
||||
"use_case_heading2": "Who will you chat to the most?",
|
||||
"use_case_heading3": "We'll help you get connected.",
|
||||
"use_case_personal_messaging": "Friends and family",
|
||||
"use_case_work_messaging": "Coworkers and teams",
|
||||
"welcome_detail": "Now, let's help you get started",
|
||||
"welcome_to_brand": "Welcome to %(brand)s",
|
||||
"welcome_user": "Welcome %(name)s",
|
||||
"work_messaging_action": "Find your co-workers",
|
||||
"work_messaging_title": "Secure messaging for work",
|
||||
"you_did_it": "You did it!",
|
||||
"you_made_it": "You made it!"
|
||||
"welcome_user": "Welcome %(name)s"
|
||||
},
|
||||
"pill": {
|
||||
"permalink_other_room": "Message in %(room)s",
|
||||
|
@ -2686,7 +2637,6 @@
|
|||
"room_directory_heading": "Room directory",
|
||||
"room_list_heading": "Room list",
|
||||
"show_avatars_pills": "Show avatars in user, room and event mentions",
|
||||
"show_checklist_shortcuts": "Show shortcut to welcome checklist above the room list",
|
||||
"show_polls_button": "Show polls button",
|
||||
"surround_text": "Surround selected text when typing special characters",
|
||||
"time_heading": "Displaying time",
|
||||
|
|
|
@ -38,7 +38,6 @@ import { WatchManager } from "./WatchManager";
|
|||
import { CustomTheme } from "../theme";
|
||||
import AnalyticsController from "./controllers/AnalyticsController";
|
||||
import FallbackIceServerController from "./controllers/FallbackIceServerController";
|
||||
import { UseCase } from "./enums/UseCase.tsx";
|
||||
import { IRightPanelForRoomStored } from "../stores/right-panel/RightPanelStoreIPanelState.ts";
|
||||
import { ILayoutSettings } from "../stores/widgets/WidgetLayoutStore.ts";
|
||||
import { ReleaseAnnouncementData } from "../stores/ReleaseAnnouncementStore.ts";
|
||||
|
@ -280,7 +279,6 @@ export interface Settings {
|
|||
"analyticsOptIn": IBaseSetting<boolean>;
|
||||
"pseudonymousAnalyticsOptIn": IBaseSetting<boolean | null>;
|
||||
"deviceClientInformationOptIn": IBaseSetting<boolean>;
|
||||
"FTUE.useCaseSelection": IBaseSetting<UseCase | null>;
|
||||
"Registration.mobileRegistrationHelper": IBaseSetting<boolean>;
|
||||
"autocompleteDelay": IBaseSetting<number>;
|
||||
"readMarkerInViewThresholdMs": IBaseSetting<number>;
|
||||
|
@ -308,7 +306,6 @@ export interface Settings {
|
|||
deny?: string[];
|
||||
}>;
|
||||
"breadcrumbs": IBaseSetting<boolean>;
|
||||
"FTUE.userOnboardingButton": IBaseSetting<boolean>;
|
||||
"showHiddenEventsInTimeline": IBaseSetting<boolean>;
|
||||
"lowBandwidth": IBaseSetting<boolean>;
|
||||
"fallbackICEServerAllowed": IBaseSetting<boolean | null>;
|
||||
|
@ -992,10 +989,6 @@ export const SETTINGS: Settings = {
|
|||
displayName: _td("settings|security|record_session_details"),
|
||||
default: false,
|
||||
},
|
||||
"FTUE.useCaseSelection": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
default: null,
|
||||
},
|
||||
"Registration.mobileRegistrationHelper": {
|
||||
supportedLevels: [SettingLevel.CONFIG],
|
||||
default: false,
|
||||
|
@ -1086,11 +1079,6 @@ export const SETTINGS: Settings = {
|
|||
displayName: _td("settings|show_breadcrumbs"),
|
||||
default: true,
|
||||
},
|
||||
"FTUE.userOnboardingButton": {
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
displayName: _td("settings|preferences|show_checklist_shortcuts"),
|
||||
default: true,
|
||||
},
|
||||
"showHiddenEventsInTimeline": {
|
||||
displayName: _td("devtools|show_hidden_events"),
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export enum UseCase {
|
||||
PersonalMessaging = "PersonalMessaging",
|
||||
WorkMessaging = "WorkMessaging",
|
||||
CommunityMessaging = "CommunityMessaging",
|
||||
Skip = "Skip",
|
||||
}
|
|
@ -1114,19 +1114,6 @@ describe("<MatrixChat />", () => {
|
|||
// set up keys screen is rendered
|
||||
expect(screen.getByText("Setting up keys")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should go to use case selection if user just registered", async () => {
|
||||
loginClient.doesServerSupportUnstableFeature.mockResolvedValue(true);
|
||||
MatrixClientPeg.setJustRegisteredUserId(userId);
|
||||
|
||||
await getComponentAndLogin();
|
||||
|
||||
bootstrapDeferred.resolve();
|
||||
|
||||
await expect(
|
||||
screen.findByRole("heading", { name: "You're in", level: 1 }),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2023 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { render, screen } from "jest-matrix-react";
|
||||
|
||||
import { AppDownloadDialog } from "../../../../../src/components/views/dialogs/AppDownloadDialog";
|
||||
import SdkConfig, { ConfigOptions } from "../../../../../src/SdkConfig";
|
||||
|
||||
describe("AppDownloadDialog", () => {
|
||||
afterEach(() => {
|
||||
SdkConfig.reset();
|
||||
});
|
||||
|
||||
it("should render with desktop, ios, android, fdroid buttons by default", () => {
|
||||
const { asFragment } = render(<AppDownloadDialog onFinished={jest.fn()} />);
|
||||
expect(screen.queryByRole("button", { name: "Download Element Desktop" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Download on the App Store" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on Google Play" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on F-Droid" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should allow disabling fdroid build", () => {
|
||||
SdkConfig.add({
|
||||
mobile_builds: {
|
||||
fdroid: null,
|
||||
},
|
||||
} as ConfigOptions);
|
||||
const { asFragment } = render(<AppDownloadDialog onFinished={jest.fn()} />);
|
||||
expect(screen.queryByRole("button", { name: "Download Element Desktop" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Download on the App Store" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on Google Play" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on F-Droid" })).not.toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should allow disabling desktop build", () => {
|
||||
SdkConfig.add({
|
||||
desktop_builds: {
|
||||
available: false,
|
||||
},
|
||||
} as ConfigOptions);
|
||||
const { asFragment } = render(<AppDownloadDialog onFinished={jest.fn()} />);
|
||||
expect(screen.queryByRole("button", { name: "Download Element Desktop" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Download on the App Store" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on Google Play" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on F-Droid" })).toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should allow disabling mobile builds", () => {
|
||||
SdkConfig.add({
|
||||
mobile_builds: {
|
||||
ios: null,
|
||||
android: null,
|
||||
fdroid: null,
|
||||
},
|
||||
} as ConfigOptions);
|
||||
const { asFragment } = render(<AppDownloadDialog onFinished={jest.fn()} />);
|
||||
expect(screen.queryByRole("button", { name: "Download Element Desktop" })).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Download on the App Store" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on Google Play" })).not.toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: "Get it on F-Droid" })).not.toBeInTheDocument();
|
||||
expect(asFragment()).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -1,540 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`AppDownloadDialog should allow disabling desktop build 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_AppDownloadDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Download Element
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_mobile"
|
||||
>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
iOS
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Download on the App Store"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://apps.apple.com/app/vector/id1083446067"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Android
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Get it on Google Play"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://play.google.com/store/apps/details?id=im.vector.app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
<a
|
||||
aria-label="Get it on F-Droid"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://f-droid.org/repository/browse/?fdid=im.vector.app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_legal"
|
||||
>
|
||||
<p>
|
||||
App Store® and the Apple logo® are trademarks of Apple Inc.
|
||||
</p>
|
||||
<p>
|
||||
Google Play and the Google Play logo are trademarks of Google LLC.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`AppDownloadDialog should allow disabling fdroid build 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_AppDownloadDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Download Element
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_desktop"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Download Element Desktop
|
||||
</h3>
|
||||
<a
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
href="https://element.io/download"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
Download Element Desktop
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_mobile"
|
||||
>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
iOS
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Download on the App Store"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://apps.apple.com/app/vector/id1083446067"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Android
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Get it on Google Play"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://play.google.com/store/apps/details?id=im.vector.app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_legal"
|
||||
>
|
||||
<p>
|
||||
App Store® and the Apple logo® are trademarks of Apple Inc.
|
||||
</p>
|
||||
<p>
|
||||
Google Play and the Google Play logo are trademarks of Google LLC.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`AppDownloadDialog should allow disabling mobile builds 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_AppDownloadDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Download Element
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_desktop"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Download Element Desktop
|
||||
</h3>
|
||||
<a
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
href="https://element.io/download"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
Download Element Desktop
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_mobile"
|
||||
/>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_legal"
|
||||
>
|
||||
<p>
|
||||
App Store® and the Apple logo® are trademarks of Apple Inc.
|
||||
</p>
|
||||
<p>
|
||||
Google Play and the Google Play logo are trademarks of Google LLC.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
||||
|
||||
exports[`AppDownloadDialog should render with desktop, ios, android, fdroid buttons by default 1`] = `
|
||||
<DocumentFragment>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
<div
|
||||
aria-labelledby="mx_BaseDialog_title"
|
||||
class="mx_AppDownloadDialog mx_Dialog_fixedWidth"
|
||||
data-focus-lock-disabled="false"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
class="mx_Dialog_header"
|
||||
>
|
||||
<h1
|
||||
class="mx_Heading_h3 mx_Dialog_title"
|
||||
id="mx_BaseDialog_title"
|
||||
>
|
||||
Download Element
|
||||
</h1>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_desktop"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Download Element Desktop
|
||||
</h3>
|
||||
<a
|
||||
class="mx_AccessibleButton mx_AccessibleButton_hasKind mx_AccessibleButton_kind_primary"
|
||||
href="https://element.io/download"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
Download Element Desktop
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_mobile"
|
||||
>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
iOS
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Download on the App Store"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://apps.apple.com/app/vector/id1083446067"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_app"
|
||||
>
|
||||
<h3
|
||||
class="mx_Heading_h3"
|
||||
>
|
||||
Android
|
||||
</h3>
|
||||
<div
|
||||
class="mx_QRCode"
|
||||
>
|
||||
<div
|
||||
class="mx_Spinner"
|
||||
>
|
||||
<div
|
||||
aria-label="Loading…"
|
||||
class="mx_Spinner_icon"
|
||||
data-testid="spinner"
|
||||
role="progressbar"
|
||||
style="width: 32px; height: 32px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_info"
|
||||
>
|
||||
or
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_links"
|
||||
>
|
||||
<a
|
||||
aria-label="Get it on Google Play"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://play.google.com/store/apps/details?id=im.vector.app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
<a
|
||||
aria-label="Get it on F-Droid"
|
||||
class="mx_AccessibleButton"
|
||||
href="https://f-droid.org/repository/browse/?fdid=im.vector.app"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
target="_blank"
|
||||
>
|
||||
<div />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="mx_AppDownloadDialog_legal"
|
||||
>
|
||||
<p>
|
||||
App Store® and the Apple logo® are trademarks of Apple Inc.
|
||||
</p>
|
||||
<p>
|
||||
Google Play and the Google Play logo are trademarks of Google LLC.
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
aria-label="Close dialog"
|
||||
class="mx_AccessibleButton mx_Dialog_cancelButton"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
data-focus-guard="true"
|
||||
style="width: 1px; height: 0px; padding: 0px; overflow: hidden; position: fixed; top: 1px; left: 1px;"
|
||||
tabindex="0"
|
||||
/>
|
||||
</DocumentFragment>
|
||||
`;
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { getUserOnboardingCounters } from "../../../../../src/components/views/user-onboarding/UserOnboardingList";
|
||||
|
||||
const tasks = [
|
||||
{
|
||||
id: "1",
|
||||
title: "Lorem ipsum",
|
||||
description: "Lorem ipsum dolor amet.",
|
||||
completed: true,
|
||||
},
|
||||
{
|
||||
id: "2",
|
||||
title: "Lorem ipsum",
|
||||
description: "Lorem ipsum dolor amet.",
|
||||
completed: false,
|
||||
},
|
||||
];
|
||||
|
||||
describe("getUserOnboardingCounters()", () => {
|
||||
it.each([
|
||||
{
|
||||
tasks: [],
|
||||
expectation: {
|
||||
completed: 0,
|
||||
waiting: 0,
|
||||
total: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
tasks: tasks,
|
||||
expectation: {
|
||||
completed: 1,
|
||||
waiting: 1,
|
||||
total: 2,
|
||||
},
|
||||
},
|
||||
])("should calculate counters correctly", ({ tasks, expectation }) => {
|
||||
const result = getUserOnboardingCounters(tasks);
|
||||
expect(result).toStrictEqual(expectation);
|
||||
});
|
||||
});
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { act, render, RenderResult } from "jest-matrix-react";
|
||||
|
||||
import { filterConsole, withClientContextRenderOptions, stubClient } from "../../../../test-utils";
|
||||
import { UserOnboardingPage } from "../../../../../src/components/views/user-onboarding/UserOnboardingPage";
|
||||
import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg";
|
||||
import SdkConfig from "../../../../../src/SdkConfig";
|
||||
|
||||
jest.mock("../../../../../src/components/structures/EmbeddedPage", () => ({
|
||||
__esModule: true,
|
||||
default: ({ url }: { url: string }) => <div>{url}</div>,
|
||||
}));
|
||||
|
||||
jest.mock("../../../../../src/components/structures/HomePage", () => ({
|
||||
__esModule: true,
|
||||
default: () => <div>home page</div>,
|
||||
}));
|
||||
|
||||
describe("UserOnboardingPage", () => {
|
||||
const renderComponent = async (): Promise<RenderResult> => {
|
||||
const renderResult = render(<UserOnboardingPage />, withClientContextRenderOptions(MatrixClientPeg.safeGet()));
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
return renderResult;
|
||||
};
|
||||
|
||||
filterConsole(
|
||||
// unrelated for this test
|
||||
"could not update user onboarding context",
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
stubClient();
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe("when the user registered before the cutoff date", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(MatrixClientPeg, "userRegisteredAfter").mockReturnValue(false);
|
||||
});
|
||||
|
||||
it("should render the home page", async () => {
|
||||
expect((await renderComponent()).queryByText("home page")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("when the user registered after the cutoff date", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(MatrixClientPeg, "userRegisteredAfter").mockReturnValue(true);
|
||||
});
|
||||
|
||||
describe("and there is an explicit home page configured", () => {
|
||||
beforeEach(() => {
|
||||
jest.spyOn(SdkConfig, "get").mockReturnValue({
|
||||
embedded_pages: {
|
||||
home_url: "https://example.com/home",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the configured page", async () => {
|
||||
expect((await renderComponent()).queryByText("https://example.com/home")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe("and there is no home page configured", () => {
|
||||
it("should render the onboarding", async () => {
|
||||
expect((await renderComponent()).queryByTestId("user-onboarding-list")).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,82 +0,0 @@
|
|||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import { renderHook, waitFor } from "jest-matrix-react";
|
||||
|
||||
import { useUserOnboardingTasks } from "../../../src/hooks/useUserOnboardingTasks";
|
||||
import { useUserOnboardingContext } from "../../../src/hooks/useUserOnboardingContext";
|
||||
import { stubClient } from "../../test-utils";
|
||||
import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
|
||||
import DMRoomMap from "../../../src/utils/DMRoomMap";
|
||||
import PlatformPeg from "../../../src/PlatformPeg";
|
||||
|
||||
describe("useUserOnboardingTasks", () => {
|
||||
it.each([
|
||||
{
|
||||
context: {
|
||||
hasAvatar: false,
|
||||
hasDevices: false,
|
||||
hasDmRooms: false,
|
||||
showNotificationsPrompt: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
context: {
|
||||
hasAvatar: true,
|
||||
hasDevices: false,
|
||||
hasDmRooms: false,
|
||||
showNotificationsPrompt: true,
|
||||
},
|
||||
},
|
||||
])("sequence should stay static", async ({ context }) => {
|
||||
const { result } = renderHook(() => useUserOnboardingTasks(context));
|
||||
|
||||
expect(result.current).toHaveLength(5);
|
||||
expect(result.current[0].id).toBe("create-account");
|
||||
expect(result.current[1].id).toBe("find-friends");
|
||||
expect(result.current[2].id).toBe("download-apps");
|
||||
expect(result.current[3].id).toBe("setup-profile");
|
||||
expect(result.current[4].id).toBe("permission-notifications");
|
||||
});
|
||||
|
||||
it("should mark desktop notifications task completed on click", async () => {
|
||||
jest.spyOn(PlatformPeg, "get").mockReturnValue({
|
||||
supportsNotifications: jest.fn().mockReturnValue(true),
|
||||
maySendNotifications: jest.fn().mockReturnValue(false),
|
||||
} as any);
|
||||
|
||||
const cli = stubClient();
|
||||
cli.pushRules = {
|
||||
global: {
|
||||
override: [
|
||||
{
|
||||
rule_id: ".m.rule.master",
|
||||
enabled: false,
|
||||
actions: [],
|
||||
default: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
DMRoomMap.makeShared(cli);
|
||||
const context = renderHook(() => useUserOnboardingContext(), {
|
||||
wrapper: (props) => {
|
||||
return <MatrixClientContext.Provider value={cli}>{props.children}</MatrixClientContext.Provider>;
|
||||
},
|
||||
});
|
||||
const { result, rerender } = renderHook(() => useUserOnboardingTasks(context.result.current));
|
||||
expect(result.current[4].id).toBe("permission-notifications");
|
||||
expect(result.current[4].completed).toBe(false);
|
||||
result.current[4].action!.onClick!({ type: "click" } as any);
|
||||
await waitFor(() => {
|
||||
rerender();
|
||||
expect(result.current[4].completed).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue