diff --git a/cypress/e2e/user-onboarding/user-onboarding-new.ts b/cypress/e2e/user-onboarding/user-onboarding-new.ts index ca83fe8ed2..9c00370558 100644 --- a/cypress/e2e/user-onboarding/user-onboarding-new.ts +++ b/cypress/e2e/user-onboarding/user-onboarding-new.ts @@ -52,6 +52,15 @@ describe("User Onboarding (new user)", () => { cy.percySnapshot("User onboarding page"); }); + it("app download dialog", () => { + cy.get('.mx_UserOnboardingPage').should('exist'); + cy.contains(".mx_UserOnboardingTask_action", "Download apps").click(); + cy.get('[role=dialog]') + .contains("#mx_BaseDialog_title", "Download Element") + .should("exist"); + cy.percySnapshot("App download dialog"); + }); + it("using find friends action should increase progress", () => { cy.get(".mx_ProgressBar").invoke("val").then((oldProgress) => { const findPeopleAction = cy.contains(".mx_UserOnboardingTask_action", "Find friends"); diff --git a/res/css/_components.pcss b/res/css/_components.pcss index 552e71675f..bbfc03ca53 100644 --- a/res/css/_components.pcss +++ b/res/css/_components.pcss @@ -94,6 +94,7 @@ @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"; diff --git a/res/css/views/dialogs/_AppDownloadDialog.pcss b/res/css/views/dialogs/_AppDownloadDialog.pcss new file mode 100644 index 0000000000..7e15497617 --- /dev/null +++ b/res/css/views/dialogs/_AppDownloadDialog.pcss @@ -0,0 +1,70 @@ +.mx_AppDownloadDialog { + display: flex; + flex-direction: column; + gap: $spacing-32; + color: $primary-content; + + .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 { + 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; + } + } +} diff --git a/res/img/badges/f-droid.svg b/res/img/badges/f-droid.svg new file mode 100644 index 0000000000..d97143c42b --- /dev/null +++ b/res/img/badges/f-droid.svg @@ -0,0 +1 @@ + diff --git a/res/img/badges/google-play.svg b/res/img/badges/google-play.svg new file mode 100644 index 0000000000..973d9d3afc --- /dev/null +++ b/res/img/badges/google-play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/badges/ios.svg b/res/img/badges/ios.svg new file mode 100644 index 0000000000..e723d1cc04 --- /dev/null +++ b/res/img/badges/ios.svg @@ -0,0 +1 @@ + diff --git a/src/components/views/dialogs/AppDownloadDialog.tsx b/src/components/views/dialogs/AppDownloadDialog.tsx new file mode 100644 index 0000000000..ab5ebfd22c --- /dev/null +++ b/src/components/views/dialogs/AppDownloadDialog.tsx @@ -0,0 +1,112 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import 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"; +import { IDialogProps } from "./IDialogProps"; + +const fallbackAppStore = "https://apps.apple.com/app/vector/id1083446067"; +const fallbackGooglePlay = "https://play.google.com/store/apps/details?id=im.vector.app"; +const fallbackFDroid = "https://f-droid.org/repository/browse/?fdid=im.vector.app"; + +export const AppDownloadDialog: FC = ({ onFinished }: IDialogProps) => { + const brand = SdkConfig.get("brand"); + const desktopBuilds = SdkConfig.getObject("desktop_builds"); + const mobileBuilds = SdkConfig.getObject("mobile_builds"); + + const urlAppStore = mobileBuilds?.get("ios") ?? fallbackAppStore; + + const urlAndroid = mobileBuilds?.get("android") ?? mobileBuilds?.get("fdroid") ?? fallbackGooglePlay; + const urlGooglePlay = mobileBuilds?.get("android") ?? fallbackGooglePlay; + const urlFDroid = mobileBuilds?.get("fdroid") ?? fallbackFDroid; + + return ( + + { desktopBuilds?.get("available") && ( +
+ + { _t("Download %(brand)s Desktop", { brand }) } + + {}}> + { _t("Download %(brand)s Desktop", { brand }) } + +
+ ) } +
+
+ + { _t("iOS") } + + +
or
+
+ {}}> + + +
+
+
+ + { _t("Android") } + + +
or
+
+ {}}> + + + {}}> + + +
+
+
+
+

{ _t("App Store® and the Apple logo® are trademarks of Apple Inc.") }

+

{ _t("Google Play and the Google Play logo are trademarks of Google LLC.") }

+
+
+ ); +}; diff --git a/src/hooks/useUserOnboardingTasks.ts b/src/hooks/useUserOnboardingTasks.ts index 41bd043463..daef154de0 100644 --- a/src/hooks/useUserOnboardingTasks.ts +++ b/src/hooks/useUserOnboardingTasks.ts @@ -16,11 +16,13 @@ limitations under the License. import { useMemo } from "react"; +import { AppDownloadDialog } 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 { UseCase } from "../settings/enums/UseCase"; @@ -100,9 +102,9 @@ const tasks: InternalUserOnboardingTask[] = [ }, action: { label: _t("Download apps"), - href: "https://element.io/get-started#download", onClick: (ev: ButtonEvent) => { PosthogTrackers.trackInteraction("WebUserOnboardingTaskDownloadApps", ev); + Modal.createDialog(AppDownloadDialog, {}, "mx_AppDownloadDialog_wrapper", false, true); }, }, }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0c2ea59d5c..c14bf1dbf6 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2454,6 +2454,15 @@ "We don't record or profile any account data": "We don't record or profile any account data", "We don't share information with third parties": "We don't share information with third parties", "You can turn this off anytime in settings": "You can turn this off anytime in settings", + "Download %(brand)s": "Download %(brand)s", + "Download %(brand)s Desktop": "Download %(brand)s Desktop", + "iOS": "iOS", + "Download on the App Store": "Download on the App Store", + "Android": "Android", + "Get it on Google Play": "Get it on Google Play", + "Get it on F-Droid": "Get it on F-Droid", + "App Store® and the Apple logo® are trademarks of Apple Inc.": "App Store® and the Apple logo® are trademarks of Apple Inc.", + "Google Play and the Google Play logo are trademarks of Google LLC.": "Google Play and the Google Play logo are trademarks of Google LLC.", "The following users may not exist": "The following users may not exist", "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?": "Unable to find profiles for the Matrix IDs listed below - would you like to invite them anyway?", "Invite anyway and never warn me again": "Invite anyway and never warn me again",