diff --git a/docs/config.md b/docs/config.md index 4095559c02..f1ced14fd2 100644 --- a/docs/config.md +++ b/docs/config.md @@ -130,37 +130,32 @@ complete re-branding/private labeling, a more personalised experience can be ach 6. `mobile_builds`: Optional. Like `desktop_builds`, except for the mobile apps. Also described in more detail down below. 7. `mobile_guide_toast`: When `true` (default), users accessing the Element Web instance from a mobile device will be prompted to download the app instead. -8. `mobile_guide_app_variant`: Optional. The mobile app that the user is prompted to download from the `/mobile_guide` page. When omitted - the mobile guide will be configured with the Classic apps. Allowed values are as follows: - 1. `classic`: Element Android/iOS. - 2. `x`: Element X Android/iOS. - 3. `pro`: Element Pro Android/iOS. -9. `update_base_url`: For the desktop app only, the URL where to acquire update packages. If specified, must be a path to a directory +8. `update_base_url`: For the desktop app only, the URL where to acquire update packages. If specified, must be a path to a directory containing `macos` and `win32` directories, with the update packages within. Defaults to `https://packages.element.io/desktop/update/` in production. -10. `map_style_url`: Map tile server style URL for location sharing. e.g. `https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY_GOES_HERE` - This setting is ignored if your homeserver provides `/.well-known/matrix/client` in its well-known location, and the JSON file - at that location has a key `m.tile_server` (or the unstable version `org.matrix.msc3488.tile_server`). In this case, the - configuration found in the well-known location is used instead. -11. `welcome_user_id`: **DEPRECATED** An optional user ID to start a DM with after creating an account. Defaults to nothing (no DM created). -12. `custom_translations_url`: An optional URL to allow overriding of translatable strings. The JSON file must be in a format of +9. `map_style_url`: Map tile server style URL for location sharing. e.g. `https://api.maptiler.com/maps/streets/style.json?key=YOUR_KEY_GOES_HERE` + This setting is ignored if your homeserver provides `/.well-known/matrix/client` in its well-known location, and the JSON file + at that location has a key `m.tile_server` (or the unstable version `org.matrix.msc3488.tile_server`). In this case, the + configuration found in the well-known location is used instead. +10. `welcome_user_id`: **DEPRECATED** An optional user ID to start a DM with after creating an account. Defaults to nothing (no DM created). +11. `custom_translations_url`: An optional URL to allow overriding of translatable strings. The JSON file must be in a format of `{"affected|translation|key": {"languageCode": "new string"}}`. See https://github.com/matrix-org/matrix-react-sdk/pull/7886 for details. -13. `branding`: Options for configuring various assets used within the app. Described in more detail down below. -14. `embedded_pages`: Further optional URLs for various assets used within the app. Described in more detail down below. -15. `disable_3pid_login`: When `false` (default), **enables** the options to log in with email address or phone number. Set to +12. `branding`: Options for configuring various assets used within the app. Described in more detail down below. +13. `embedded_pages`: Further optional URLs for various assets used within the app. Described in more detail down below. +14. `disable_3pid_login`: When `false` (default), **enables** the options to log in with email address or phone number. Set to `true` to hide these options. -16. `disable_login_language_selector`: When `false` (default), **enables** the language selector on the login pages. Set to `true` +15. `disable_login_language_selector`: When `false` (default), **enables** the language selector on the login pages. Set to `true` to hide this dropdown. -17. `disable_guests`: When `false` (default), **enable** guest-related functionality (peeking/previewing rooms, etc) for unregistered +16. `disable_guests`: When `false` (default), **enable** guest-related functionality (peeking/previewing rooms, etc) for unregistered users. Set to `true` to disable this functionality. -18. `user_notice`: Optional notice to show to the user, e.g. for sunsetting a deployment and pushing users to move in their own time. +17. `user_notice`: Optional notice to show to the user, e.g. for sunsetting a deployment and pushing users to move in their own time. Takes a configuration object as below: 1. `title`: Required. Title to show at the top of the notice. 2. `description`: Required. The description to use for the notice. 3. `show_once`: Optional. If true then the notice will only be shown once per device. -19. `help_url`: The URL to point users to for help with the app, defaults to `https://element.io/help`. -20. `help_encryption_url`: The URL to point users to for help with encryption, defaults to `https://element.io/help#encryption`. -21. `force_verification`: If true, users must verify new logins (eg. with another device / their recovery key) +18. `help_url`: The URL to point users to for help with the app, defaults to `https://element.io/help`. +19. `help_encryption_url`: The URL to point users to for help with encryption, defaults to `https://element.io/help#encryption`. +20. `force_verification`: If true, users must verify new logins (eg. with another device / their recovery key) ### `desktop_builds` and `mobile_builds` diff --git a/playwright/e2e/mobile-guide/mobile-guide.spec.ts b/playwright/e2e/mobile-guide/mobile-guide.spec.ts deleted file mode 100644 index 4ecb309481..0000000000 --- a/playwright/e2e/mobile-guide/mobile-guide.spec.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -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"; -import { MobileAppVariant } from "../../../src/vector/mobile_guide/mobile-apps"; - -const variants = [MobileAppVariant.Classic, MobileAppVariant.X, MobileAppVariant.Pro]; - -test.describe("Mobile Guide Screenshots", { tag: "@screenshot" }, () => { - for (const variant of variants) { - test.use({ - config: { - default_server_config: { - "m.homeserver": { - base_url: "https://matrix.server.invalid", - server_name: "server.invalid", - }, - }, - mobile_guide_app_variant: variant, - }, - viewport: { width: 390, height: 844 }, // iPhone 16e - }); - test(`should match the homepage screenshot for variant: ${variant}`, async ({ page, axe }) => { - await page.goto("/mobile_guide/"); - await expect(page).toMatchScreenshot(`mobile-guide-${variant}.png`); - await expect(axe).toHaveNoViolations(); - }); - } -}); diff --git a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-classic-linux.png b/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-classic-linux.png deleted file mode 100644 index ff1bb69a4f..0000000000 Binary files a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-classic-linux.png and /dev/null differ diff --git a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-pro-linux.png b/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-pro-linux.png deleted file mode 100644 index ff1bb69a4f..0000000000 Binary files a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-pro-linux.png and /dev/null differ diff --git a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-x-linux.png b/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-x-linux.png deleted file mode 100644 index ff1bb69a4f..0000000000 Binary files a/playwright/snapshots/mobile-guide/mobile-guide.spec.ts/mobile-guide-x-linux.png and /dev/null differ diff --git a/src/IConfigOptions.ts b/src/IConfigOptions.ts index 2e129f1186..8b88a18075 100644 --- a/src/IConfigOptions.ts +++ b/src/IConfigOptions.ts @@ -81,7 +81,6 @@ export interface IConfigOptions { }; mobile_guide_toast?: boolean; - mobile_guide_app_variant?: "classic" | "x" | "pro"; default_theme?: "light" | "dark" | string; // custom themes are strings default_country_code?: string; // ISO 3166 alpha2 country code diff --git a/src/vector/mobile_guide/assets/app-store-badge.svg b/src/vector/mobile_guide/assets/app-store-badge.svg deleted file mode 100755 index 072b425a1a..0000000000 --- a/src/vector/mobile_guide/assets/app-store-badge.svg +++ /dev/null @@ -1,46 +0,0 @@ - - Download_on_the_App_Store_Badge_US-UK_RGB_blk_4SVG_092917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/vector/mobile_guide/assets/bottom-gradient.svg b/src/vector/mobile_guide/assets/bottom-gradient.svg deleted file mode 100644 index 0740440946..0000000000 --- a/src/vector/mobile_guide/assets/bottom-gradient.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/vector/mobile_guide/assets/element-logo.svg b/src/vector/mobile_guide/assets/element-logo.svg deleted file mode 100644 index d2cb52e498..0000000000 --- a/src/vector/mobile_guide/assets/element-logo.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/vector/mobile_guide/assets/google-play-badge.svg b/src/vector/mobile_guide/assets/google-play-badge.svg deleted file mode 100644 index fac62d70ed..0000000000 --- a/src/vector/mobile_guide/assets/google-play-badge.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/vector/mobile_guide/index.css b/src/vector/mobile_guide/index.css deleted file mode 100644 index 064b9b86a5..0000000000 --- a/src/vector/mobile_guide/index.css +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -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 url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css"); - -html { - min-height: 100%; - position: relative; -} - -body { - background: var(--cpd-color-bg-canvas-default); - max-width: 680px; - margin: var(--cpd-space-0x) auto; - padding-bottom: 178px; /* Match the height of mx_BottomGradient */ - font-family: var(--cpd-font-family-sans); - font-size: var(--cpd-font-size-body-lg); /* Design says 16px, this is 17px */ - color: var(--cpd-color-text-primary); -} - -hr { - border: none; - height: var(--cpd-border-width-1); - background-color: var( - --cpd-color-bg-subtle-primary /* Design uses Border token from "Compound Marketing" set, but this matches. */ - ); - color: var( - --cpd-color-bg-subtle-primary /* Design uses Border token from "Compound Marketing" set, but this matches. */ - ); - margin: 0; -} - -p { - margin: var(--cpd-space-1x) var(--cpd-space-0x); - padding: var(--cpd-space-0x); -} - -.mx_Button { - border: 0; - border-radius: 100px; - min-width: 80px; - background-color: var(--cpd-color-bg-action-primary-rest); - color: var(--cpd-color-text-on-solid-primary); - cursor: pointer; - padding: 12px 22px; - word-break: break-word; - text-decoration: none; -} - -#deep_link_button { - margin-top: 12px; - display: inline-block; - width: auto; - box-sizing: border-box; -} - -.mx_StoreLinks { - margin: 15px 0 12px 0; -} - -.mx_StoreBadge { - text-decoration: none !important; - margin: 16px 16px 16px 0px; -} - -#f_droid_link { - color: var(--cpd-color-text-action-accent); - font-weight: bold; - text-decoration: none; -} - -#f_droid_link:visited { - color: var(--cpd-color-text-action-accent); -} - -.mx_HomePage_header { - color: var(--cpd-color-text-secondary); - align-items: center; - justify-content: center; - text-align: center; - padding-top: 48px; - padding-bottom: 48px; -} - -.mx_HomePage_header #header_title { - margin-top: 8px; - margin-bottom: 0px; -} - -.mx_HomePage h3 { - margin-top: 30px; -} - -.mx_HomePage_col { - display: flex; - flex-direction: row; -} - -.mx_HomePage_row { - flex: 1 1 0; - display: flex; - flex-direction: row; - flex-wrap: wrap; - align-items: flex-start; -} - -.mx_HomePage_container { - margin: 10px 20px; -} - -.mx_HomePage_errorContainer { - display: none; /* shown in JS if needed */ - margin: 20px; - border: var(--cpd-border-width-1) solid var(--cpd-color-border-critical-primary); - background-color: var(--cpd-color-bg-critical-subtle); - padding: 5px; -} - -.mx_HomePage_container h1, -.mx_HomePage_container h2, -.mx_HomePage_container h3, -.mx_HomePage_container h4 { - font-weight: var(--cpd-font-weight-semibold); - font-size: var(--cpd-font-size-body-lg); /* Design says 16px, this is 17px */ - margin-bottom: 8px; - margin-top: 4px; -} - -.mx_Spacer { - margin-top: 48px; -} - -.mx_DesktopLink { - color: var(--cpd-color-text-action-accent); - font-weight: var(--cpd-font-weight-semibold); - text-decoration: none; -} - -/* - * The bottom gradient is a full-width background image that stretches horizontally across the page. - * It is positioned pinned to the bottom of the viewport unless the content is taller than the viewport, - * in which case it will be pinned to the bottom of the content. - */ -.mx_BottomGradient { - position: absolute; - bottom: 0; - left: 0; - right: 0; - width: 100vw; - height: 178px; /* Match the height of assets/bottom-gradient.svg so the gradient only stretches horizontally */ - background-image: url("assets/bottom-gradient.svg"); - background-size: 100% 100%; - background-repeat: no-repeat; - z-index: -1; - margin-left: calc(50% - 50vw); /* Center the gradient regardless of body width */ -} - -.mx_HomePage_step_number { - display: flex; - align-items: flex-start; - margin-right: 8px; -} - -.mx_HomePage_step_number span { - display: flex; - align-items: center; - justify-content: center; - width: var(--cpd-space-6x); - height: var(--cpd-space-6x); - border-radius: 50%; - border: var(--cpd-border-width-1) solid var(--cpd-color-bg-subtle-primary); /* Not a border token, but matches the Design (Border token from the "Compound Marketing" set). */ - background-color: transparent; - color: var(--cpd-color-text-secondary); - font-size: var(--cpd-font-size-body-md); /* Design says 14px, this is 15px */ -} - -#step2_description { - color: var(--cpd-color-text-secondary); -} diff --git a/src/vector/mobile_guide/index.html b/src/vector/mobile_guide/index.html index 34c3c4cc4a..d58842d6a6 100644 --- a/src/vector/mobile_guide/index.html +++ b/src/vector/mobile_guide/index.html @@ -1,13 +1,141 @@ - Element Mobile Guide + + @@ -16,90 +144,648 @@
-
-
+
+ +

Set up Element on iOS or Android

+
+ +
- -
-
-

- The desktop site does - not work on mobile, please download the app. -

-
-
-
-
- 1 -
-
-

Download Element

- -

- Also available on - F-Droid -

-
-
-
- -
+
+
+

+ Go to Desktop Site +
+ Please note the Desktop site does not work on mobile. +

+
- - diff --git a/src/vector/mobile_guide/index.ts b/src/vector/mobile_guide/index.ts index 4953536e1e..ae769039a8 100644 --- a/src/vector/mobile_guide/index.ts +++ b/src/vector/mobile_guide/index.ts @@ -5,14 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com Please see LICENSE files in the repository root for full details. */ -import "./index.css"; -import "@fontsource/inter/400.css"; -import "@fontsource/inter/600.css"; - import { logger } from "matrix-js-sdk/src/logger"; import { getVectorConfig } from "../getconfig"; -import { type MobileAppVariant, mobileApps, updateMobilePage } from "./mobile-apps.ts"; function onBackToElementClick(): void { // Cookie should expire in 4 hours @@ -43,19 +38,18 @@ function renderConfigError(message: string): void { } async function initPage(): Promise { + document.getElementById("back_to_element_button")!.onclick = onBackToElementClick; + const config = await getVectorConfig(".."); // We manually parse the config similar to how validateServerConfig works because // calling that function pulls in roughly 4mb of JS we don't use. const wkConfig = config?.["default_server_config"]; // overwritten later under some conditions - let serverName = config?.["default_server_name"]; + const serverName = config?.["default_server_name"]; const defaultHsUrl = config?.["default_hs_url"]; const defaultIsUrl = config?.["default_is_url"]; - const appVariant = (config?.["mobile_guide_app_variant"] ?? "classic") as MobileAppVariant; - const metadata = mobileApps[appVariant]; - const incompatibleOptions = [wkConfig, serverName, defaultHsUrl].filter((i) => !!i); if (defaultHsUrl && (wkConfig || serverName)) { return renderConfigError( @@ -72,7 +66,6 @@ async function initPage(): Promise { if (!serverName && typeof wkConfig?.["m.homeserver"]?.["base_url"] === "string") { hsUrl = wkConfig["m.homeserver"]["base_url"]; - serverName = wkConfig["m.homeserver"]["server_name"]; if (typeof wkConfig["m.identity_server"]?.["base_url"] === "string") { isUrl = wkConfig["m.identity_server"]["base_url"]; @@ -117,21 +110,21 @@ async function initPage(): Promise { if (hsUrl && !hsUrl.endsWith("/")) hsUrl += "/"; if (isUrl && !isUrl.endsWith("/")) isUrl += "/"; - let deepLinkUrl = `https://mobile.element.io${metadata.deepLinkPath}`; + if (hsUrl !== "https://matrix.org/") { + let url = "https://mobile.element.io?hs_url=" + encodeURIComponent(hsUrl); - if (metadata.usesLegacyDeepLink) { - deepLinkUrl += `?hs_url=${encodeURIComponent(hsUrl)}`; if (isUrl) { - deepLinkUrl += `&is_url=${encodeURIComponent(isUrl)}`; + document.getElementById("custom_is")!.style.display = "block"; + document.getElementById("is_url")!.style.display = "block"; + document.getElementById("is_url")!.innerText = isUrl; + url += "&is_url=" + encodeURIComponent(isUrl ?? ""); } - } else if (serverName) { - deepLinkUrl += `?account_provider=${serverName}`; + + (document.getElementById("configure_element_button") as HTMLAnchorElement).href = url; + document.getElementById("step1_heading")!.innerHTML = "1: Install the app"; + document.getElementById("step2_container")!.style.display = "block"; + document.getElementById("hs_url")!.innerText = hsUrl; } - - // Not part of updateMobilePage as the link is only shown on mobile_guide and not on mobile.element.io - document.getElementById("back_to_element_button")!.onclick = onBackToElementClick; - - updateMobilePage(metadata, deepLinkUrl, serverName ?? hsUrl); } void initPage(); diff --git a/src/vector/mobile_guide/mobile-apps.ts b/src/vector/mobile_guide/mobile-apps.ts deleted file mode 100644 index ab2ff2240a..0000000000 --- a/src/vector/mobile_guide/mobile-apps.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2025 New Vector Ltd. - -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. -*/ - -/* - * Shared code that is used by the mobile guide and the mobile.element.io site. - */ - -export enum MobileAppVariant { - Classic = "classic", - X = "x", - Pro = "pro", -} - -export interface MobileAppMetadata { - name: string; - appleAppId: string; - appStoreUrl: string; - playStoreUrl: string; - fDroidUrl?: string; - deepLinkPath: string; - usesLegacyDeepLink: boolean; - isProApp: boolean; -} - -export const mobileApps: Record = { - [MobileAppVariant.Classic]: { - name: "Element", - appleAppId: "id1083446067", - appStoreUrl: "https://apps.apple.com/app/element-messenger/id1083446067", - playStoreUrl: "https://play.google.com/store/apps/details?id=im.vector.app", - fDroidUrl: "https://f-droid.org/packages/im.vector.app", - deepLinkPath: "", - usesLegacyDeepLink: true, - isProApp: false, - }, - [MobileAppVariant.X]: { - name: "Element X", - appleAppId: "id1631335820", - appStoreUrl: "https://apps.apple.com/app/element-x-secure-chat-call/id1631335820", - playStoreUrl: "https://play.google.com/store/apps/details?id=io.element.android.x", - fDroidUrl: "https://f-droid.org/packages/io.element.android.x", - deepLinkPath: "/element", - usesLegacyDeepLink: false, - isProApp: false, - }, - [MobileAppVariant.Pro]: { - name: "Element Pro", - appleAppId: "id6502951615", - appStoreUrl: "https://apps.apple.com/app/element-pro-for-work/id6502951615", - playStoreUrl: "https://play.google.com/store/apps/details?id=io.element.enterprise", - deepLinkPath: "/element-pro", - usesLegacyDeepLink: false, - isProApp: true, - }, -}; - -export function updateMobilePage(metadata: MobileAppMetadata, deepLinkUrl: string, server: string | undefined): void { - const appleMeta = document.querySelector('meta[name="apple-itunes-app"]') as Element; - appleMeta.setAttribute("content", `app-id=${metadata.appleAppId}`); - - if (server) { - (document.getElementById("header_title") as HTMLHeadingElement).innerText = `Join ${server} on Element`; - } - (document.getElementById("app_store_link") as HTMLAnchorElement).href = metadata.appStoreUrl; - (document.getElementById("play_store_link") as HTMLAnchorElement).href = metadata.playStoreUrl; - - if (metadata.fDroidUrl) { - (document.getElementById("f_droid_link") as HTMLAnchorElement).href = metadata.fDroidUrl; - } else { - document.getElementById("f_droid_section")!.style.display = "none"; - } - - const step1Heading = document.getElementById("step1_heading")!; - step1Heading.innerHTML = step1Heading!.innerHTML.replace("Element", metadata.name); - - // Step 2 is only shown on the mobile guide, not on mobile.element.io - if (document.getElementById("step2_container")) { - document.getElementById("step2_container")!.style.display = "block"; - if (metadata.isProApp) { - document.getElementById("step2_description")!.innerHTML = "Use your work email to join"; - } - (document.getElementById("deep_link_button") as HTMLAnchorElement).href = deepLinkUrl; - } -} diff --git a/webpack.config.js b/webpack.config.js index 4d7b59d7ab..e0d7d4fe93 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -679,12 +679,6 @@ module.exports = (env, argv) => { context: path.resolve(__dirname, "node_modules/@element-hq/element-call-embedded/dist"), to: path.join(__dirname, "webapp", "widgets", "element-call"), }, - // Mobile guide assets - { - from: "assets/**", - context: path.resolve(__dirname, "src/vector/mobile_guide"), - to: "mobile_guide", - }, ], }),