diff --git a/buildSrc/DevBuild.js b/buildSrc/DevBuild.js index 298142e7f79e..d95fef3143ab 100644 --- a/buildSrc/DevBuild.js +++ b/buildSrc/DevBuild.js @@ -69,7 +69,7 @@ export async function runDevBuild({ stage, host, desktop, clean, ignoreMigration u2fAppId: `${protocol}//${hostname}:${port}/u2f-appid.json`, giftCardBaseUrl: `${protocol}//${hostname}:${port}/giftcard`, referralBaseUrl: `${protocol}//${hostname}:${port}/signup`, - websiteBaseUrl: "https://tuta.com", + websiteBaseUrl: domainConfigs[hostname].websiteBaseUrl ?? "https://tuta.com", }, } } @@ -223,7 +223,7 @@ globalThis.buildOptions.sqliteNativePath = "./better-sqlite3.node";`, const __dirname = path.dirname(fileURLToPath(import.meta.url)) const root = __dirname.split(path.sep).slice(0, -1).join(path.sep) -async function createBootstrap(env, buildDir) { +async function createBootstrap(env, buildDir, stage) { let jsFileName let htmlFileName switch (env.mode) { @@ -249,7 +249,7 @@ if (env.staticUrl == null && window.tutaoDefaultApiUrl) { } import('./app.js')` await writeFile(`./${buildDir}/${jsFileName}`, template) - const html = await LaunchHtml.renderHtml(imports, env) + const html = await LaunchHtml.renderHtml(imports, env, stage) await writeFile(`./${buildDir}/${htmlFileName}`, html) } @@ -297,6 +297,6 @@ export async function prepareAssets(stage, host, version, domainConfigs, buildDi /** @type {EnvMode[]} */ const modes = ["Browser", "App", "Desktop"] for (const mode of modes) { - await createBootstrap(env.create({ staticUrl: getStaticUrl(stage, mode, host), version, mode, dist: false, domainConfigs }), buildDir) + await createBootstrap(env.create({ staticUrl: getStaticUrl(stage, mode, host), version, mode, dist: false, domainConfigs }), buildDir, stage) } } diff --git a/buildSrc/LaunchHtml.js b/buildSrc/LaunchHtml.js index 4fcf65444974..a70b16c1b71f 100644 --- a/buildSrc/LaunchHtml.js +++ b/buildSrc/LaunchHtml.js @@ -21,10 +21,23 @@ function getCspUrls(env) { } } +function getWebsiteUrl(stage) { + switch (stage) { + case "test": + return "https://test.tuta.com" + case "local": + return "https://local.tuta.com:9000" + default: // prod and host + return "https://tuta.com" + } +} + /** * Renders the initial HTML page to bootstrap Tutanota for different environments */ -export async function renderHtml(scripts, env) { +export async function renderHtml(scripts, env, stage) { + // ideally check if it is possible to use the website url from the env.domainConfigs here instead of getWebsiteURL() + const websiteUrl = getWebsiteUrl(stage) return ` @@ -45,7 +58,7 @@ export async function renderHtml(scripts, env) { - + @@ -53,11 +66,11 @@ export async function renderHtml(scripts, env) { content="Get a free email account with quantum-safe encryption and best privacy on all your devices. Green, secure & no ads!"> - + - + diff --git a/buildSrc/buildWebapp.js b/buildSrc/buildWebapp.js index e7bb0856222b..8bfca0ba54ae 100644 --- a/buildSrc/buildWebapp.js +++ b/buildSrc/buildWebapp.js @@ -177,10 +177,11 @@ self.onmessage = function (msg) { dist: true, domainConfigs, }), + stage, app, ) if (stage !== "release") { - await createHtml(env.create({ staticUrl: restUrl, version, mode: "App", dist: true, domainConfigs }), app) + await createHtml(env.create({ staticUrl: restUrl, version, mode: "App", dist: true, domainConfigs }), stage, app) } await bundleServiceWorker(chunks, version, minify, buildDir) diff --git a/buildSrc/createHtml.js b/buildSrc/createHtml.js index 51ec231f08c7..1dd04a486ec6 100644 --- a/buildSrc/createHtml.js +++ b/buildSrc/createHtml.js @@ -14,9 +14,10 @@ import path from "node:path" * staticUrl: String defining app base url for non-production environments and the native clients. * versionNumber: String containing the app's version number * @param app App to be built, defaults to mail app {String} + * @param stage Deployment for which to build: 'prod' will build for the production system, 'test' for the test system, 'local' will use local. * @returns {Promise[]>} */ -export async function createHtml(env, app = "mail") { +export async function createHtml(env, stage, app = "mail") { let jsFileName let htmlFileName const buildDir = app === "calendar" ? "build-calendar-app" : "build" @@ -44,7 +45,7 @@ window.env = ${JSON.stringify(env, null, 2)} ${indexTemplate}` return Promise.all([ _writeFile(`./${buildDir}/${jsFileName}`, index), - renderHtml(imports, env).then((content) => _writeFile(`./${buildDir}/${htmlFileName}`, content)), + renderHtml(imports, env, stage).then((content) => _writeFile(`./${buildDir}/${htmlFileName}`, content)), ]) } diff --git a/desktop.js b/desktop.js index c496cfbc4f72..8369bf1914d1 100644 --- a/desktop.js +++ b/desktop.js @@ -112,7 +112,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture } if (stage === "release") { - await createHtml(env.create({ staticUrl: tutaAppUrl, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: tutaAppUrl, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopBaseOpts) if (!customDesktopRelease) { const updateUrl = new URL(tutaTestUrl) @@ -124,7 +124,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture // Do not notarize test build notarize: false, }) - await createHtml(env.create({ staticUrl: tutaTestUrl, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: tutaTestUrl, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopTestOpts) } } else if (stage === "local") { @@ -140,7 +140,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture nameSuffix: "-snapshot", notarize: false, }) - await createHtml(env.create({ staticUrl: `http://${addr}:9000`, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: `http://${addr}:9000`, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopLocalOpts) } else if (stage === "test") { const updateUrl = new URL(tutaTestUrl) @@ -150,7 +150,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture nameSuffix: "-test", notarize: false, }) - await createHtml(env.create({ staticUrl: tutaTestUrl, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: tutaTestUrl, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopTestOpts) } else if (stage === "prod") { const desktopProdOpts = Object.assign({}, desktopBaseOpts, { @@ -158,7 +158,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture updateUrl: "http://localhost:9000/desktop", notarize: false, }) - await createHtml(env.create({ staticUrl: tutaAppUrl, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: tutaAppUrl, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopProdOpts) } else { // stage = host @@ -168,7 +168,7 @@ async function buildDesktopClient(version, { stage, host, platform, architecture nameSuffix: "-snapshot", notarize: false, }) - await createHtml(env.create({ staticUrl: host, version, mode: "Desktop", dist: true, domainConfigs })) + await createHtml(env.create({ staticUrl: host, version, mode: "Desktop", dist: true, domainConfigs }), stage) await buildDesktop(desktopHostOpts) } } diff --git a/src/calendar-app/calendar-app.ts b/src/calendar-app/calendar-app.ts index 6aef1cebf391..80b72dc36a42 100644 --- a/src/calendar-app/calendar-app.ts +++ b/src/calendar-app/calendar-app.ts @@ -1,7 +1,7 @@ import { client } from "../common/misc/ClientDetector.js" import m from "mithril" import Mithril, { Children, ClassComponent, Component, RouteDefs, RouteResolver, Vnode, VnodeDOM } from "mithril" -import { lang, languageCodeToTag, languages } from "../common/misc/LanguageViewModel.js" +import { lang, languageCodeToTag, languages, setInfoLinks } from "../common/misc/LanguageViewModel.js" import { root } from "../RootView.js" import { disableErrorHandlingDuringLogout, handleUncaughtError } from "../common/misc/ErrorHandler.js" import { assertMainOrNodeBoot, bootFinished, isApp, isDesktop, isOfflineStorageAvailable } from "../common/api/common/Env.js" @@ -397,6 +397,8 @@ import("../mail-app/translations/en.js") const serviceworker = await import("../common/serviceworker/ServiceWorkerClient.js") serviceworker.init(domainConfig) + setInfoLinks(domainConfig.websiteBaseUrl) + printJobsMessage(domainConfig) }) diff --git a/src/common/gui/RenderLoginInfoLinks.ts b/src/common/gui/RenderLoginInfoLinks.ts index 1074f4b9a3f0..e278d327b074 100644 --- a/src/common/gui/RenderLoginInfoLinks.ts +++ b/src/common/gui/RenderLoginInfoLinks.ts @@ -1,7 +1,7 @@ import m, { Children } from "mithril" import { isApp, isDesktop } from "../api/common/Env.js" import { ExternalLink } from "./base/ExternalLink.js" -import { InfoLink, lang } from "../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../misc/LanguageViewModel.js" import { createDropdown } from "./base/Dropdown.js" import { mapNullable } from "@tutao/tutanota-utils" import { getWhitelabelCustomizations } from "../misc/WhitelabelCustomizations.js" @@ -48,11 +48,11 @@ export function renderInfoLinks(): Children { } function getImprintLink(): string | null { - return mapNullable(getWhitelabelCustomizations(window), (c) => c.imprintUrl) || InfoLink.About + return mapNullable(getWhitelabelCustomizations(window), (c) => c.imprintUrl) || InfoLinks.About } function getPrivacyStatementLink(): string | null { - return mapNullable(getWhitelabelCustomizations(window), (c) => c.privacyStatementUrl) || InfoLink.Privacy + return mapNullable(getWhitelabelCustomizations(window), (c) => c.privacyStatementUrl) || InfoLinks.Privacy } /** diff --git a/src/common/login/recover/TakeOverDeletedAddressDialog.ts b/src/common/login/recover/TakeOverDeletedAddressDialog.ts index 602b2e2c9b74..d8bed7316fab 100644 --- a/src/common/login/recover/TakeOverDeletedAddressDialog.ts +++ b/src/common/login/recover/TakeOverDeletedAddressDialog.ts @@ -3,7 +3,7 @@ import stream from "mithril/stream" import { AccessBlockedError, AccessDeactivatedError, InvalidDataError, NotAuthenticatedError, TooManyRequestsError } from "../../api/common/error/RestError" import { showProgressDialog } from "../../gui/dialogs/ProgressDialog" import { isMailAddress } from "../../misc/FormatValidator.js" -import { InfoLink, lang } from "../../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../misc/LanguageViewModel.js" import { Autocomplete, TextField, TextFieldType } from "../../gui/base/TextField.js" import { Dialog, DialogType } from "../../gui/base/Dialog" import { HtmlEditor, HtmlEditorMode } from "../../gui/editor/HtmlEditor" @@ -27,7 +27,7 @@ export function showTakeOverDialog(mailAddress: string, password: string): Dialo view: () => { return [ m(".mt", lang.get("takeOverUnusedAddress_msg")), - m(MoreInfoLink, { link: InfoLink.InactiveAccounts }), + m(MoreInfoLink, { link: InfoLinks.InactiveAccounts }), m(TextField, { label: "targetAddress_label", value: targetAccountAddress(), diff --git a/src/common/misc/LanguageViewModel.ts b/src/common/misc/LanguageViewModel.ts index 154008665992..955551b04f88 100644 --- a/src/common/misc/LanguageViewModel.ts +++ b/src/common/misc/LanguageViewModel.ts @@ -1,5 +1,4 @@ -import type { lazy } from "@tutao/tutanota-utils" -import { downcast, typedEntries } from "@tutao/tutanota-utils" +import { downcast, lazy, typedEntries } from "@tutao/tutanota-utils" import type { TranslationKeyType } from "./TranslationKey" import { getWhitelabelCustomizations, WhitelabelCustomizations } from "./WhitelabelCustomizations" import { assertMainOrNodeBoot } from "../api/common/Env" @@ -194,34 +193,69 @@ export const languageNative: ReadonlyArray<{ } }) -export const enum InfoLink { - HomePage = "https://tuta.com", - About = "https://tuta.com/imprint", +// Modifiable so `app.js` can initialize it to the website urls in the domain config +export let InfoLinks = { + HomePage: "https://tuta.com", + About: `https://tuta.com/imprint`, //terms - Terms = "https://tuta.com/terms", - Privacy = "https://tuta.com/privacy-policy", - GiftCardsTerms = "https://tuta.com/giftCardsTerms", + Terms: `https://tuta.com/terms`, + Privacy: `https://tuta.com/privacy-policy`, + GiftCardsTerms: `https://tuta.com/giftCardsTerms`, //faq - RecoverCode = "https://tuta.com/faq#reset", - SecondFactor = "https://tuta.com/faq#2fa", - SpamRules = "https://tuta.com/faq#spam", - DomainInfo = "https://tuta.com/faq#custom-domain", - Whitelabel = "https://tuta.com/faq#whitelabel", - ReferralLink = "https://tuta.com/faq#refer-a-friend", - Webview = "https://tuta.com/faq#webview", - Phishing = "https://tuta.com/faq#phishing", - MailAuth = "https://tuta.com/faq#mail-auth", - RunInBackground = "https://tuta.com/faq#tray-desktop", - LoadImages = "https://tuta.com/faq#load-images", - Usage = "https://tuta.com/faq#usage", - Download = "https://tuta.com/#download", - SharedMailboxes = "https://tuta.com/support/#shared-mailboxes", - InactiveAccounts = "https://tuta.com/faq/#inactive-accounts", - AppStorePaymentChange = "https://tuta.com/support/#appstore-payment-change", - AppStorePayment = "https://tuta.com/support/#appstore-payments", - AppStoreDowngrade = "https://tuta.com/support/#appstore-subscription-downgrade", - PasswordGenerator = "https://tuta.com/faq#passphrase-generator", - HomePageFreeSignup = "https://tuta.com/free-email", + RecoverCode: `https://tuta.com/faq#reset`, + SecondFactor: `https://tuta.com/faq#2fa`, + SpamRules: `https://tuta.com/faq#spam`, + DomainInfo: `https://tuta.com/faq#custom-domain`, + Whitelabel: `https://tuta.com/faq#whitelabel`, + ReferralLink: `https://tuta.com/faq#refer-a-friend`, + Webview: `https://tuta.com/faq#webview`, + Phishing: `https://tuta.com/faq#phishing`, + MailAuth: `https://tuta.com/faq#mail-auth`, + RunInBackground: `https://tuta.com/faq#tray-desktop`, + LoadImages: `https://tuta.com/faq#load-images`, + Usage: `https://tuta.com/faq#usage`, + Download: `https://tuta.com/#download`, + SharedMailboxes: `https://tuta.com/support/#shared-mailboxes`, + InactiveAccounts: `https://tuta.com/faq/#inactive-accounts`, + AppStorePaymentChange: `https://tuta.com/support/#appstore-payment-change`, + AppStorePayment: `https://tuta.com/support/#appstore-payments`, + AppStoreDowngrade: `https://tuta.com/support/#appstore-subscription-downgrade`, + PasswordGenerator: `https://tuta.com/faq#passphrase-generator`, + HomePageFreeSignup: `https://tuta.com/free-email`, +} +export type InfoLink = (typeof InfoLinks)[keyof typeof InfoLinks] + +// Updates the website links in `InfoLinks` to use `websiteUrl` as the root +export function setInfoLinks(websiteUrl: string) { + InfoLinks = { + HomePage: websiteUrl, + About: `${websiteUrl}/imprint`, + //terms + Terms: `${websiteUrl}/terms`, + Privacy: `${websiteUrl}/privacy-policy`, + GiftCardsTerms: `${websiteUrl}/giftCardsTerms`, + //faq + RecoverCode: `${websiteUrl}/faq#reset`, + SecondFactor: `${websiteUrl}/faq#2fa`, + SpamRules: `${websiteUrl}/faq#spam`, + DomainInfo: `${websiteUrl}/faq#custom-domain`, + Whitelabel: `${websiteUrl}/faq#whitelabel`, + ReferralLink: `${websiteUrl}/faq#refer-a-friend`, + Webview: `${websiteUrl}/faq#webview`, + Phishing: `${websiteUrl}/faq#phishing`, + MailAuth: `${websiteUrl}/faq#mail-auth`, + RunInBackground: `${websiteUrl}/faq#tray-desktop`, + LoadImages: `${websiteUrl}/faq#load-images`, + Usage: `${websiteUrl}/faq#usage`, + Download: `${websiteUrl}/#download`, + SharedMailboxes: `${websiteUrl}/support/#shared-mailboxes`, + InactiveAccounts: `${websiteUrl}/faq/#inactive-accounts`, + AppStorePaymentChange: `${websiteUrl}/support/#appstore-payment-change`, + AppStorePayment: `${websiteUrl}/support/#appstore-payments`, + AppStoreDowngrade: `${websiteUrl}/support/#appstore-subscription-downgrade`, + PasswordGenerator: `${websiteUrl}/faq#passphrase-generator`, + HomePageFreeSignup: `${websiteUrl}/free-email`, + } } /** diff --git a/src/common/misc/news/MoreInfoLink.ts b/src/common/misc/news/MoreInfoLink.ts index fc36bb4ae9af..516f39a20273 100644 --- a/src/common/misc/news/MoreInfoLink.ts +++ b/src/common/misc/news/MoreInfoLink.ts @@ -1,4 +1,4 @@ -import { InfoLink, lang } from "../LanguageViewModel.js" +import { InfoLink, InfoLinks, lang } from "../LanguageViewModel.js" import m, { Children, Component, Vnode } from "mithril" import { ExternalLink, relDocument } from "../../gui/base/ExternalLink.js" @@ -15,17 +15,17 @@ export class MoreInfoLink implements Component { view(vnode: Vnode): Children { let specialType: relDocument | undefined switch (vnode.attrs.link) { - case InfoLink.HomePage: + case InfoLinks.HomePage: specialType = "me" break - case InfoLink.About: + case InfoLinks.About: specialType = "license" break - case InfoLink.Privacy: + case InfoLinks.Privacy: specialType = "privacy-policy" break - case InfoLink.Terms: - case InfoLink.GiftCardsTerms: + case InfoLinks.Terms: + case InfoLinks.GiftCardsTerms: specialType = "terms-of-service" break default: diff --git a/src/common/misc/news/items/NewPlansNews.ts b/src/common/misc/news/items/NewPlansNews.ts index d15601d2bea0..bf12edeff8e7 100644 --- a/src/common/misc/news/items/NewPlansNews.ts +++ b/src/common/misc/news/items/NewPlansNews.ts @@ -1,7 +1,7 @@ import { NewsListItem } from "../NewsListItem.js" import m, { Children } from "mithril" import { NewsId } from "../../../api/entities/tutanota/TypeRefs.js" -import { InfoLink, lang } from "../../LanguageViewModel.js" +import { InfoLinks, lang } from "../../LanguageViewModel.js" import { Button, ButtonAttrs, ButtonType } from "../../../gui/base/Button.js" import { NewsModel } from "../NewsModel.js" import { UserController } from "../../../api/main/UserController.js" @@ -23,7 +23,7 @@ export class NewPlansNews implements NewsListItem { } render(newsId: NewsId): Children { - const lnk = InfoLink.Privacy + const lnk = InfoLinks.Privacy const acknowledgeAction = () => { this.newsModel.acknowledgeNews(newsId.newsItemId).then(m.redraw) diff --git a/src/common/misc/news/items/NewPlansOfferEndingNews.ts b/src/common/misc/news/items/NewPlansOfferEndingNews.ts index 2cb2e8667b71..5e906fd79cd3 100644 --- a/src/common/misc/news/items/NewPlansOfferEndingNews.ts +++ b/src/common/misc/news/items/NewPlansOfferEndingNews.ts @@ -1,7 +1,7 @@ import { NewsListItem } from "../NewsListItem.js" import m, { Children } from "mithril" import { NewsId } from "../../../api/entities/tutanota/TypeRefs.js" -import { InfoLink, lang } from "../../LanguageViewModel.js" +import { lang } from "../../LanguageViewModel.js" import { Button, ButtonAttrs, ButtonType } from "../../../gui/base/Button.js" import { NewsModel } from "../NewsModel.js" import { UserController } from "../../../api/main/UserController.js" diff --git a/src/common/misc/news/items/ReferralLinkViewer.ts b/src/common/misc/news/items/ReferralLinkViewer.ts index 1e56ece26fe2..afb21ff78037 100644 --- a/src/common/misc/news/items/ReferralLinkViewer.ts +++ b/src/common/misc/news/items/ReferralLinkViewer.ts @@ -1,4 +1,4 @@ -import { InfoLink, lang } from "../../LanguageViewModel.js" +import { InfoLinks, lang } from "../../LanguageViewModel.js" import { isApp } from "../../../api/common/Env.js" import { locator } from "../../../api/main/CommonLocator.js" import { copyToClipboard } from "../../ClipboardUtils.js" @@ -37,7 +37,7 @@ export class ReferralLinkViewer implements Component { label: "referralLink_label", value: referralLink, injectionsRight: () => this.renderButtons(referralLink), - helpLabel: () => ifAllowedTutaLinks(locator.logins, InfoLink.ReferralLink, (link) => [m(MoreInfoLink, { link: link })]), + helpLabel: () => ifAllowedTutaLinks(locator.logins, InfoLinks.ReferralLink, (link) => [m(MoreInfoLink, { link: link })]), } } diff --git a/src/common/misc/news/items/UsageOptInNews.ts b/src/common/misc/news/items/UsageOptInNews.ts index a4544ac97076..f4cd0302043d 100644 --- a/src/common/misc/news/items/UsageOptInNews.ts +++ b/src/common/misc/news/items/UsageOptInNews.ts @@ -2,7 +2,7 @@ import { NewsListItem } from "../NewsListItem.js" import m, { Children } from "mithril" import { NewsId } from "../../../api/entities/tutanota/TypeRefs.js" import { locator } from "../../../api/main/CommonLocator.js" -import { InfoLink, lang } from "../../LanguageViewModel.js" +import { InfoLinks, lang } from "../../LanguageViewModel.js" import { Dialog } from "../../../gui/base/Dialog.js" import { Button, ButtonAttrs, ButtonType } from "../../../gui/base/Button.js" import { NewsModel } from "../NewsModel.js" @@ -66,7 +66,7 @@ export class UsageOptInNews implements NewsListItem { m("li", lang.get("userUsageDataOptInStatement3_msg")), m("li", lang.get("userUsageDataOptInStatement4_msg")), ]), - m(MoreInfoLink, { link: InfoLink.Privacy }), + m(MoreInfoLink, { link: InfoLinks.Privacy }), m( ".flex-end.flex-no-grow-no-shrink-auto.flex-wrap", buttonAttrs.map((a) => m(Button, a)), diff --git a/src/common/misc/passwords/PasswordGeneratorDialog.ts b/src/common/misc/passwords/PasswordGeneratorDialog.ts index 6a2e878c5df0..7b9b6679bc66 100644 --- a/src/common/misc/passwords/PasswordGeneratorDialog.ts +++ b/src/common/misc/passwords/PasswordGeneratorDialog.ts @@ -5,7 +5,7 @@ import { Button, ButtonType } from "../../gui/base/Button.js" import { locator } from "../../api/main/CommonLocator" import { px } from "../../gui/size" import { copyToClipboard } from "../ClipboardUtils" -import { InfoLink, lang } from "../LanguageViewModel.js" +import { InfoLinks, lang } from "../LanguageViewModel.js" import { LoginButton } from "../../gui/base/buttons/LoginButton.js" import { ExternalLink } from "../../gui/base/ExternalLink.js" @@ -76,7 +76,7 @@ class PasswordGeneratorDialog implements Component lang.get("passphraseGeneratorHelp_msg"), " ", m(ExternalLink, { - href: InfoLink.PasswordGenerator, + href: InfoLinks.PasswordGenerator, text: lang.get("faqEntry_label"), isCompanySite: true, }), diff --git a/src/common/settings/AboutDialog.ts b/src/common/settings/AboutDialog.ts index ddd3a253763f..d0f834ae2303 100644 --- a/src/common/settings/AboutDialog.ts +++ b/src/common/settings/AboutDialog.ts @@ -3,7 +3,7 @@ import { Button, ButtonType } from "../gui/base/Button.js" import { getLightOrDarkTutaLogo } from "../gui/theme.js" import { showUserError } from "../misc/ErrorHandlerImpl.js" import { locator } from "../api/main/CommonLocator.js" -import { InfoLink, lang } from "../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../misc/LanguageViewModel.js" import { newMailEditorFromTemplate } from "../../mail-app/mail/editor/MailEditor.js" import { UserError } from "../api/main/UserError.js" import { clientInfoString, getLogAttachments } from "../misc/ErrorReporter.js" @@ -32,7 +32,7 @@ export class AboutDialog implements Component { m.trust(getLightOrDarkTutaLogo(client.isCalendarApp())), ), m(".flex.justify-center.flex-wrap", [ - m(ExternalLink, { href: InfoLink.HomePage, text: "Website", isCompanySite: true, specialType: "me", class: "mlr mt" }), + m(ExternalLink, { href: InfoLinks.HomePage, text: "Website", isCompanySite: true, specialType: "me", class: "mlr mt" }), m(ExternalLink, { href: "https://github.com/tutao/tutanota/releases", text: "Releases", diff --git a/src/common/settings/EditNotificationEmailDialog.ts b/src/common/settings/EditNotificationEmailDialog.ts index 42b7c03f3aa2..ff0fef10d13b 100644 --- a/src/common/settings/EditNotificationEmailDialog.ts +++ b/src/common/settings/EditNotificationEmailDialog.ts @@ -1,7 +1,7 @@ import type { Booking, CustomerInfo, CustomerProperties, NotificationMailTemplate } from "../api/entities/sys/TypeRefs.js" import { BookingTypeRef, createNotificationMailTemplate, CustomerInfoTypeRef, CustomerPropertiesTypeRef } from "../api/entities/sys/TypeRefs.js" import { HtmlEditor } from "../gui/editor/HtmlEditor.js" -import { InfoLink, lang, languages } from "../misc/LanguageViewModel.js" +import { InfoLinks, lang, languages } from "../misc/LanguageViewModel.js" import stream from "mithril/stream" import Stream from "mithril/stream" import { Dialog, DialogType } from "../gui/base/Dialog.js" @@ -261,7 +261,7 @@ function getDefaultNotificationMail(): string { HTML_PTAG_END + HTML_PTAG_START + lang.get("externalNotificationMailBody2_msg", { - "{1}": InfoLink.HomePage, + "{1}": InfoLinks.HomePage, }) + HTML_PTAG_END + HTML_PTAG_START + diff --git a/src/common/settings/login/LoginSettingsViewer.ts b/src/common/settings/login/LoginSettingsViewer.ts index dfc6f30e4cc9..715f410d089e 100644 --- a/src/common/settings/login/LoginSettingsViewer.ts +++ b/src/common/settings/login/LoginSettingsViewer.ts @@ -2,7 +2,7 @@ import m, { Children } from "mithril" import stream from "mithril/stream" import type { TextFieldAttrs } from "../../gui/base/TextField.js" import { TextField } from "../../gui/base/TextField.js" -import { InfoLink, lang } from "../../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../misc/LanguageViewModel.js" import { Icons } from "../../gui/base/icons/Icons.js" import { CustomerPropertiesTypeRef, Session, SessionTypeRef } from "../../api/entities/sys/TypeRefs.js" import { assertNotNull, LazyLoaded, neverNull, ofClass } from "@tutao/tutanota-utils" @@ -108,7 +108,7 @@ export class LoginSettingsViewer implements UpdatableSettingsViewer { const recoveryCodeFieldAttrs: TextFieldAttrs = { label: "recoveryCode_label", helpLabel: () => { - return ifAllowedTutaLinks(locator.logins, InfoLink.RecoverCode, (link) => [m(MoreInfoLink, { link: link })]) + return ifAllowedTutaLinks(locator.logins, InfoLinks.RecoverCode, (link) => [m(MoreInfoLink, { link: link })]) }, value: this._stars(), oninput: this._stars, @@ -137,7 +137,7 @@ export class LoginSettingsViewer implements UpdatableSettingsViewer { this._usageTestModel.setOptInDecision(assertNotNull(v)) }, helpLabel: () => { - return ifAllowedTutaLinks(locator.logins, InfoLink.Usage, (link) => [ + return ifAllowedTutaLinks(locator.logins, InfoLinks.Usage, (link) => [ m("span", lang.get("userUsageDataOptInInfo_msg") + " "), m(MoreInfoLink, { link: link }), ]) diff --git a/src/common/settings/login/RecoverCodeDialog.ts b/src/common/settings/login/RecoverCodeDialog.ts index 86868b0ca2de..4d055800971d 100644 --- a/src/common/settings/login/RecoverCodeDialog.ts +++ b/src/common/settings/login/RecoverCodeDialog.ts @@ -1,4 +1,4 @@ -import { InfoLink, lang } from "../../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../misc/LanguageViewModel.js" import { Dialog, DialogType } from "../../gui/base/Dialog.js" import { assertNotNull, Hex, noOp, ofClass } from "@tutao/tutanota-utils" import m, { Child, Children, Vnode } from "mithril" @@ -131,7 +131,7 @@ export class RecoverCodeField { } private renderRecoveryText(): Child { - const link = InfoLink.RecoverCode + const link = InfoLinks.RecoverCode return m(".pt.pb", [lang.get("recoveryCode_msg"), m("", [m(MoreInfoLink, { link, isSmall: true })])]) } } diff --git a/src/common/settings/login/secondfactor/SecondFactorsEditForm.ts b/src/common/settings/login/secondfactor/SecondFactorsEditForm.ts index 5fd8515e4870..0f73e64b449b 100644 --- a/src/common/settings/login/secondfactor/SecondFactorsEditForm.ts +++ b/src/common/settings/login/secondfactor/SecondFactorsEditForm.ts @@ -5,7 +5,7 @@ import { SecondFactorTypeRef } from "../../../api/entities/sys/TypeRefs.js" import { assertNotNull, LazyLoaded, neverNull, ofClass } from "@tutao/tutanota-utils" import { Icons } from "../../../gui/base/icons/Icons.js" import { Dialog } from "../../../gui/base/Dialog.js" -import { InfoLink, lang } from "../../../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../../misc/LanguageViewModel.js" import { assertEnumValue, SecondFactorType } from "../../../api/common/TutanotaConstants.js" import { showProgressDialog } from "../../../gui/dialogs/ProgressDialog.js" import type { TableAttrs, TableLineAttrs } from "../../../gui/base/Table.js" @@ -50,7 +50,7 @@ export class SecondFactorsEditForm { m(".h4.mt-l", lang.get("secondFactorAuthentication_label")), m(Table, secondFactorTableAttrs), this.domainConfigProvider.getCurrentDomainConfig().firstPartyDomain - ? [ifAllowedTutaLinks(locator.logins, InfoLink.SecondFactor, (link) => m(MoreInfoLink, { link: link, isSmall: true }))] + ? [ifAllowedTutaLinks(locator.logins, InfoLinks.SecondFactor, (link) => m(MoreInfoLink, { link: link, isSmall: true }))] : null, ] } diff --git a/src/common/settings/whitelabel/WhitelabelSettingsViewer.ts b/src/common/settings/whitelabel/WhitelabelSettingsViewer.ts index 1a0ff27178b7..271eb29d01d5 100644 --- a/src/common/settings/whitelabel/WhitelabelSettingsViewer.ts +++ b/src/common/settings/whitelabel/WhitelabelSettingsViewer.ts @@ -20,7 +20,7 @@ import { CustomerTypeRef, WhitelabelConfigTypeRef, } from "../../api/entities/sys/TypeRefs.js" -import { InfoLink, lang } from "../../misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../misc/LanguageViewModel.js" import { FeatureType, OperationType } from "../../api/common/TutanotaConstants.js" import { progressIcon } from "../../gui/base/Icon.js" import { showProgressDialog } from "../../gui/dialogs/ProgressDialog.js" @@ -88,7 +88,7 @@ export class WhitelabelSettingsViewer implements UpdatableSettingsViewer { ? [ m(".h4.mt-l", lang.get("whitelabel_label")), m(".small", lang.get("whitelabelDomainLinkInfo_msg") + " "), - m("small.text-break", [m(`a[href=${InfoLink.Whitelabel}][target=_blank]`, InfoLink.Whitelabel)]), + m("small.text-break", [m(`a[href=${InfoLinks.Whitelabel}][target=_blank]`, InfoLinks.Whitelabel)]), this._renderWhitelabelStatusSettings(), this._renderNotificationEmailSettings(), m(".h4.mt-l", lang.get("whitelabelDomain_label")), diff --git a/src/common/subscription/PaymentViewer.ts b/src/common/subscription/PaymentViewer.ts index 7cf1aa6a4a12..1fa76cf3c259 100644 --- a/src/common/subscription/PaymentViewer.ts +++ b/src/common/subscription/PaymentViewer.ts @@ -1,7 +1,7 @@ import m, { Children } from "mithril" import { assertMainOrNode, isIOSApp } from "../api/common/Env" import { assertNotNull, last, neverNull, ofClass } from "@tutao/tutanota-utils" -import { InfoLink, lang, TranslationKey } from "../misc/LanguageViewModel" +import { InfoLinks, lang, TranslationKey } from "../misc/LanguageViewModel" import { AccountingInfo, AccountingInfoTypeRef, @@ -151,7 +151,7 @@ export class PaymentViewer implements UpdatableSettingsViewer { // they must downgrade to Free first. const isResubscribe = await Dialog.choice( - () => lang.get("storeDowngradeOrResubscribe_msg", { "{AppStoreDowngrade}": InfoLink.AppStoreDowngrade }), + () => lang.get("storeDowngradeOrResubscribe_msg", { "{AppStoreDowngrade}": InfoLinks.AppStoreDowngrade }), [ { text: "changePlan_action", @@ -333,7 +333,7 @@ export class PaymentViewer implements UpdatableSettingsViewer { ) } else { if (client.device == DeviceType.ANDROID) { - return Dialog.message("invoiceFailedWebview_msg", () => m("div", m("a", { href: InfoLink.Webview, target: "_blank" }, InfoLink.Webview))) + return Dialog.message("invoiceFailedWebview_msg", () => m("div", m("a", { href: InfoLinks.Webview, target: "_blank" }, InfoLinks.Webview))) } else if (client.isIos()) { return Dialog.message("invoiceFailedIOS_msg") } else { @@ -537,7 +537,7 @@ function getPostingTypeText(posting: CustomerAccountPosting): string { export async function showManageThroughAppStoreDialog(): Promise { const confirmed = await Dialog.confirm(() => lang.get("storeSubscription_msg", { - "{AppStorePayment}": InfoLink.AppStorePayment, + "{AppStorePayment}": InfoLinks.AppStorePayment, }), ) if (confirmed) { diff --git a/src/common/subscription/SignupForm.ts b/src/common/subscription/SignupForm.ts index 177391c547a1..7de565b0d130 100644 --- a/src/common/subscription/SignupForm.ts +++ b/src/common/subscription/SignupForm.ts @@ -18,7 +18,7 @@ import { Checkbox } from "../gui/base/Checkbox.js" import type { lazy } from "@tutao/tutanota-utils" import { getFirstOrThrow, ofClass } from "@tutao/tutanota-utils" import type { TranslationKey } from "../misc/LanguageViewModel" -import { InfoLink, lang } from "../misc/LanguageViewModel" +import { InfoLinks, lang } from "../misc/LanguageViewModel" import { showProgressDialog } from "../gui/dialogs/ProgressDialog" import { InvalidDataError } from "../api/common/error/RestError" import { locator } from "../api/main/CommonLocator" @@ -187,7 +187,7 @@ export class SignupForm implements Component { m(SelectMailAddressForm, mailAddressFormAttrs), // Leave as is a.isPaidSubscription() ? m(".small.mt-s", lang.get("configureCustomDomainAfterSignup_msg"), [ - m(ExternalLink, { href: InfoLink.DomainInfo, isCompanySite: true }), + m(ExternalLink, { href: InfoLinks.DomainInfo, isCompanySite: true }), ]) : null, m(PasswordForm, { diff --git a/src/common/subscription/SubscriptionViewer.ts b/src/common/subscription/SubscriptionViewer.ts index 0d6ec6f11391..e3f0973a8297 100644 --- a/src/common/subscription/SubscriptionViewer.ts +++ b/src/common/subscription/SubscriptionViewer.ts @@ -26,7 +26,7 @@ import { UserTypeRef, } from "../api/entities/sys/TypeRefs.js" import { assertNotNull, base64ExtToBase64, base64ToUint8Array, downcast, incrementDate, neverNull, promiseMap } from "@tutao/tutanota-utils" -import { InfoLink, lang, TranslationKey } from "../misc/LanguageViewModel" +import { InfoLinks, lang, TranslationKey } from "../misc/LanguageViewModel" import { Icons } from "../gui/base/icons/Icons" import { asPaymentInterval, formatPrice, formatPriceDataWithInfo, PaymentInterval } from "./PriceUtils" import { formatDate, formatStorageSize } from "../misc/Formatter" @@ -291,7 +291,7 @@ export class SubscriptionViewer implements UpdatableSettingsViewer { if (appStoreSubscriptionOwnership !== MobilePaymentSubscriptionOwnership.NoSubscription) { return Dialog.message(() => lang.get("storeMultiSubscriptionError_msg", { - "{AppStorePayment}": InfoLink.AppStorePayment, + "{AppStorePayment}": InfoLinks.AppStorePayment, }), ) } @@ -323,7 +323,7 @@ export class SubscriptionViewer implements UpdatableSettingsViewer { // There's a subscription with this apple account that doesn't belong to this user return Dialog.message(() => lang.get("storeMultiSubscriptionError_msg", { - "{AppStorePayment}": InfoLink.AppStorePayment, + "{AppStorePayment}": InfoLinks.AppStorePayment, }), ) } else if ( @@ -333,12 +333,12 @@ export class SubscriptionViewer implements UpdatableSettingsViewer { ) { // User has an ongoing subscriptions but not on the current Apple Account, so we shouldn't allow them to change their plan with this account // instead of the account owner of the subscriptions - return Dialog.message(() => lang.get("storeNoSubscription_msg", { "{AppStorePayment}": InfoLink.AppStorePayment })) + return Dialog.message(() => lang.get("storeNoSubscription_msg", { "{AppStorePayment}": InfoLinks.AppStorePayment })) } else if (appStoreSubscriptionOwnership === MobilePaymentSubscriptionOwnership.NoSubscription) { // User has no ongoing subscription and isn't approved. We should allow them to downgrade their accounts or resubscribe and // restart an Apple Subscription flow const isResubscribe = await Dialog.choice( - () => lang.get("storeDowngradeOrResubscribe_msg", { "{AppStoreDowngrade}": InfoLink.AppStoreDowngrade }), + () => lang.get("storeDowngradeOrResubscribe_msg", { "{AppStoreDowngrade}": InfoLinks.AppStoreDowngrade }), [ { text: "changePlan_action", diff --git a/src/common/subscription/TermsAndConditions.ts b/src/common/subscription/TermsAndConditions.ts index 4c35b0dc9331..50d14a524264 100644 --- a/src/common/subscription/TermsAndConditions.ts +++ b/src/common/subscription/TermsAndConditions.ts @@ -2,7 +2,7 @@ * The most recently published version of the terms and conditions */ import m, { Children } from "mithril" -import { InfoLink, lang } from "../misc/LanguageViewModel" +import { InfoLinks, lang } from "../misc/LanguageViewModel" import { isApp } from "../api/common/Env" import { requestFromWebsite } from "../misc/Website" import { Dialog } from "../gui/base/Dialog" @@ -29,15 +29,15 @@ export function renderTermsAndConditionsButton(terms: TermsSection, version: str switch (terms) { case TermsSection.GiftCards: label = lang.get("giftCardTerms_label") - link = InfoLink.GiftCardsTerms + link = InfoLinks.GiftCardsTerms break case TermsSection.Terms: label = lang.get("termsAndConditionsLink_label") - link = InfoLink.Terms + link = InfoLinks.Terms break case TermsSection.Privacy: label = lang.get("privacyLink_label") - link = InfoLink.Privacy + link = InfoLinks.Privacy break } return m( diff --git a/src/common/subscription/UpgradeSubscriptionWizard.ts b/src/common/subscription/UpgradeSubscriptionWizard.ts index eb7a4cd8260f..09a243819ab0 100644 --- a/src/common/subscription/UpgradeSubscriptionWizard.ts +++ b/src/common/subscription/UpgradeSubscriptionWizard.ts @@ -15,7 +15,7 @@ import { getByAbbreviation } from "../api/common/CountryList" import { UpgradeSubscriptionPage, UpgradeSubscriptionPageAttrs } from "./UpgradeSubscriptionPage" import m from "mithril" import stream from "mithril/stream" -import { InfoLink, lang, TranslationKey, TranslationText } from "../misc/LanguageViewModel" +import { InfoLinks, lang, TranslationKey, TranslationText } from "../misc/LanguageViewModel" import { createWizardDialog, wizardPageWrapper } from "../gui/base/WizardDialog.js" import { InvoiceAndPaymentDataPage, InvoiceAndPaymentDataPageAttrs } from "./InvoiceAndPaymentDataPage" import { UpgradeCongratulationsPage, UpgradeCongratulationsPageAttrs } from "./UpgradeCongratulationsPage.js" @@ -170,7 +170,7 @@ export async function loadSignupWizard( } message = appstoreSubscriptionOwnership != MobilePaymentSubscriptionOwnership.NoSubscription - ? () => lang.get("storeMultiSubscriptionError_msg", { "{AppStorePayment}": InfoLink.AppStorePayment }) + ? () => lang.get("storeMultiSubscriptionError_msg", { "{AppStorePayment}": InfoLinks.AppStorePayment }) : null } else { message = null diff --git a/src/common/support/FaqModel.ts b/src/common/support/FaqModel.ts index 972b7c5a395f..ac4860866b55 100644 --- a/src/common/support/FaqModel.ts +++ b/src/common/support/FaqModel.ts @@ -4,6 +4,8 @@ import { delay, downcast, LazyLoaded } from "@tutao/tutanota-utils" import { search } from "../api/common/utils/PlainTextSearch" import { ProgrammingError } from "../api/common/error/ProgrammingError.js" import { htmlSanitizer } from "../misc/HtmlSanitizer.js" +import { locator } from "../api/main/CommonLocator.js" +import { DomainConfigProvider } from "../api/common/DomainConfigProvider.js" export type FaqEntry = { id: string @@ -45,13 +47,32 @@ export class FaqModel { return Promise.all([this.fetchFAQ("en"), this.fetchFAQ(lang.code)]).then(([defaultTranslations, currentLanguageTranslations]) => { if (defaultTranslations != null || currentLanguageTranslations != null) { const faqLanguageViewModel = new LanguageViewModel() - faqLanguageViewModel.initWithTranslations(lang.code, lang.languageTag, defaultTranslations, currentLanguageTranslations) + const isProd = this.websiteBaseUrl === "https://tuta.com" + faqLanguageViewModel.initWithTranslations( + lang.code, + lang.languageTag, + isProd ? defaultTranslations : this.replaceWebsiteUrls(defaultTranslations), + isProd ? defaultTranslations : this.replaceWebsiteUrls(currentLanguageTranslations), + ) this.faqLanguages = faqLanguageViewModel } }) }) } + // Replaces any instances of the production website in the faq entry with the correct stage (test, local etc.) of the website + private replaceWebsiteUrls(translation: Translation): Translation { + const filteredKeys: Record = Object.fromEntries( + Object.entries(translation.keys).map((entry) => { + const key = entry[0] + const value = entry[1].replaceAll("https://tuta.com", this.websiteBaseUrl) + return [key, value] + }), + ) + // Expand translation to include extra fields, e.g. `name` + return { ...translation, code: translation.code, keys: filteredKeys } + } + async init(websiteBaseUrl: string): Promise { //resetting the lazy reload whenever the language preference change to clear caching. if (this.currentLanguageCode !== lang.code) { diff --git a/src/common/termination/TerminationViewModel.ts b/src/common/termination/TerminationViewModel.ts index 2a8d943f692e..d84aaef80a17 100644 --- a/src/common/termination/TerminationViewModel.ts +++ b/src/common/termination/TerminationViewModel.ts @@ -1,6 +1,6 @@ import { SessionType } from "../api/common/SessionType.js" import { LoginState } from "../login/LoginViewModel.js" -import { InfoLink, lang, TranslationText } from "../misc/LanguageViewModel.js" +import { InfoLinks, lang, TranslationText } from "../misc/LanguageViewModel.js" import { LoginController } from "../api/main/LoginController.js" import { getLoginErrorStateAndMessage } from "../misc/LoginUtils.js" import { SecondFactorHandler } from "../misc/2fa/SecondFactorHandler.js" @@ -73,7 +73,7 @@ export class TerminationViewModel { break case "hasAppStoreSubscription": this.onTerminationRequestFailed(() => - lang.get("deleteAccountWithAppStoreSubscription_msg", { "{AppStorePayment}": InfoLink.AppStorePayment }), + lang.get("deleteAccountWithAppStoreSubscription_msg", { "{AppStorePayment}": InfoLinks.AppStorePayment }), ) break default: diff --git a/src/mail-app/app.ts b/src/mail-app/app.ts index 3f25ef94b04d..2adf0bdcbb7b 100644 --- a/src/mail-app/app.ts +++ b/src/mail-app/app.ts @@ -1,7 +1,7 @@ import { client } from "../common/misc/ClientDetector.js" import m from "mithril" import Mithril, { Children, ClassComponent, Component, RouteDefs, RouteResolver, Vnode, VnodeDOM } from "mithril" -import { lang, languageCodeToTag, languages } from "../common/misc/LanguageViewModel.js" +import { lang, languageCodeToTag, languages, setInfoLinks } from "../common/misc/LanguageViewModel.js" import { root } from "../RootView.js" import { assertNotNull, neverNull } from "@tutao/tutanota-utils" import { windowFacade } from "../common/misc/WindowFacade.js" @@ -575,6 +575,8 @@ import("./translations/en.js") const serviceworker = await import("../common/serviceworker/ServiceWorkerClient.js") serviceworker.init(domainConfig) + setInfoLinks(domainConfig.websiteBaseUrl) + printJobsMessage(domainConfig) }) diff --git a/src/mail-app/mail/editor/MailEditor.ts b/src/mail-app/mail/editor/MailEditor.ts index b16e46897046..61bf4e76e8a6 100644 --- a/src/mail-app/mail/editor/MailEditor.ts +++ b/src/mail-app/mail/editor/MailEditor.ts @@ -4,7 +4,7 @@ import Stream from "mithril/stream" import { Editor, ImagePasteEvent } from "../../../common/gui/editor/Editor" import type { Attachment, InitAsResponseArgs, SendMailModel } from "../../../common/mailFunctionality/SendMailModel.js" import { Dialog } from "../../../common/gui/base/Dialog" -import { InfoLink, lang } from "../../../common/misc/LanguageViewModel" +import { InfoLinks, lang } from "../../../common/misc/LanguageViewModel" import type { MailboxDetail } from "../../../common/mailFunctionality/MailboxModel.js" import { checkApprovalStatus } from "../../../common/misc/LoginUtils" import { locator } from "../../../common/api/main/CommonLocator" @@ -546,7 +546,7 @@ export class MailEditor implements Component { return m(InfoBanner, { message: "contentBlocked_msg", icon: Icons.Picture, - helpLink: canSeeTutaLinks(attrs.model.logins) ? InfoLink.LoadImages : null, + helpLink: canSeeTutaLinks(attrs.model.logins) ? InfoLinks.LoadImages : null, buttons: [showButton], }) } diff --git a/src/mail-app/mail/signature/Signature.ts b/src/mail-app/mail/signature/Signature.ts index 2dbb81a73a5e..6b6ed611ab3b 100644 --- a/src/mail-app/mail/signature/Signature.ts +++ b/src/mail-app/mail/signature/Signature.ts @@ -1,4 +1,4 @@ -import { InfoLink, lang } from "../../../common/misc/LanguageViewModel" +import { InfoLinks, lang } from "../../../common/misc/LanguageViewModel" import type { TutanotaProperties } from "../../../common/api/entities/tutanota/TypeRefs.js" import { EmailSignatureType as TutanotaConstants } from "../../../common/api/common/TutanotaConstants" import { htmlSanitizer } from "../../../common/misc/HtmlSanitizer" @@ -14,7 +14,7 @@ export function getDefaultSignature(): string { LINE_BREAK + htmlSanitizer.sanitizeHTML( lang.get("defaultEmailSignature_msg", { - "{1}": `${InfoLink.HomePageFreeSignup}`, + "{1}": `${InfoLinks.HomePageFreeSignup}`, }), ).html ) diff --git a/src/mail-app/mail/view/MailViewerHeader.ts b/src/mail-app/mail/view/MailViewerHeader.ts index 32524505404f..abee8ba87627 100644 --- a/src/mail-app/mail/view/MailViewerHeader.ts +++ b/src/mail-app/mail/view/MailViewerHeader.ts @@ -1,5 +1,5 @@ import m, { Children, Component, Vnode } from "mithril" -import { InfoLink, lang } from "../../../common/misc/LanguageViewModel.js" +import { InfoLinks, lang } from "../../../common/misc/LanguageViewModel.js" import { theme } from "../../../common/gui/theme.js" import { styles } from "../../../common/gui/styles.js" import { ExpanderButton, ExpanderPanel } from "../../../common/gui/base/Expander.js" @@ -610,7 +610,7 @@ export class MailViewerHeader implements Component { message: "phishingMessageBody_msg", icon: Icons.Warning, type: BannerType.Warning, - helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLink.Phishing : null, + helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLinks.Phishing : null, buttons: [ { label: "markAsNotPhishing_action", @@ -629,7 +629,7 @@ export class MailViewerHeader implements Component { return m(InfoBanner, { message: "mailAuthFailed_msg", icon: Icons.Warning, - helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLink.MailAuth : null, + helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLinks.MailAuth : null, type: BannerType.Warning, buttons: [ { @@ -651,7 +651,7 @@ export class MailViewerHeader implements Component { }) : lang.get("mailAuthMissing_label"), icon: Icons.Warning, - helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLink.MailAuth : null, + helpLink: canSeeTutaLinks(viewModel.logins) ? InfoLinks.MailAuth : null, buttons: [ { label: "close_alt", @@ -704,7 +704,7 @@ export class MailViewerHeader implements Component { return m(InfoBanner, { message: "contentBlocked_msg", icon: Icons.Picture, - helpLink: canSeeTutaLinks(attrs.viewModel.logins) ? InfoLink.LoadImages : null, + helpLink: canSeeTutaLinks(attrs.viewModel.logins) ? InfoLinks.LoadImages : null, buttons: [showButton, ...maybeDropdownButtons], }) } diff --git a/src/mail-app/mail/view/MailViewerUtils.ts b/src/mail-app/mail/view/MailViewerUtils.ts index 6716f7d51654..0fb7d203966b 100644 --- a/src/mail-app/mail/view/MailViewerUtils.ts +++ b/src/mail-app/mail/view/MailViewerUtils.ts @@ -1,6 +1,6 @@ import { Keys, MailReportType, MailState, ReplyType, SYSTEM_GROUP_MAIL_ADDRESS } from "../../../common/api/common/TutanotaConstants" import { assertNotNull, neverNull, ofClass } from "@tutao/tutanota-utils" -import { InfoLink, lang } from "../../../common/misc/LanguageViewModel" +import { InfoLinks, lang } from "../../../common/misc/LanguageViewModel" import { Dialog } from "../../../common/gui/base/Dialog" import m from "mithril" import { Button, ButtonType } from "../../../common/gui/base/Button.js" @@ -237,7 +237,7 @@ function reportMail(viewModel: MailViewerViewModel) { }, [ m("div", lang.get("phishingReport_msg")), - ifAllowedTutaLinks(locator.logins, InfoLink.Phishing, (link) => + ifAllowedTutaLinks(locator.logins, InfoLinks.Phishing, (link) => m(ExternalLink, { href: link, text: lang.get("whatIsPhishing_msg"), diff --git a/src/mail-app/settings/DesktopSettingsViewer.ts b/src/mail-app/settings/DesktopSettingsViewer.ts index 767cbf62dfe7..7efd904046ed 100644 --- a/src/mail-app/settings/DesktopSettingsViewer.ts +++ b/src/mail-app/settings/DesktopSettingsViewer.ts @@ -1,5 +1,5 @@ import m, { Children } from "mithril" -import { InfoLink, lang } from "../../common/misc/LanguageViewModel" +import { InfoLinks, lang } from "../../common/misc/LanguageViewModel" import stream from "mithril/stream" import Stream from "mithril/stream" import { showProgressDialog } from "../../common/gui/dialogs/ProgressDialog" @@ -87,7 +87,7 @@ export class DesktopSettingsViewer implements UpdatableSettingsViewer { const setRunInBackgroundAttrs: DropDownSelectorAttrs = { label: "runInBackground_action", helpLabel: () => { - return ifAllowedTutaLinks(locator.logins, InfoLink.RunInBackground, (link) => [ + return ifAllowedTutaLinks(locator.logins, InfoLinks.RunInBackground, (link) => [ m("span", lang.get("runInBackground_msg") + " "), m(MoreInfoLink, { link: link }), ]) diff --git a/src/mail-app/settings/GlobalSettingsViewer.ts b/src/mail-app/settings/GlobalSettingsViewer.ts index 03f8959476ad..d5ed86ce8487 100644 --- a/src/mail-app/settings/GlobalSettingsViewer.ts +++ b/src/mail-app/settings/GlobalSettingsViewer.ts @@ -1,6 +1,6 @@ import m, { Children } from "mithril" import { DAY_IN_MILLIS, LazyLoaded, neverNull, noOp, ofClass, promiseMap } from "@tutao/tutanota-utils" -import { InfoLink, lang } from "../../common/misc/LanguageViewModel" +import { InfoLinks, lang } from "../../common/misc/LanguageViewModel" import { getSpamRuleFieldToName, getSpamRuleTypeNameMapping, showAddSpamRuleDialog } from "./AddSpamRuleDialog" import { getSpamRuleField, GroupType, OperationType, SpamRuleFieldType, SpamRuleType } from "../../common/api/common/TutanotaConstants" import { @@ -141,7 +141,7 @@ export class GlobalSettingsViewer implements UpdatableSettingsViewer { title: "adminSpam_action", table: spamRuleTableAttrs, infoMsg: "adminSpamRuleInfo_msg", - infoLinkId: InfoLink.SpamRules, + infoLinkId: InfoLinks.SpamRules, }), m(ExpandableTable, { title: "rejectedEmails_label", @@ -153,7 +153,7 @@ export class GlobalSettingsViewer implements UpdatableSettingsViewer { title: "customEmailDomains_label", table: customDomainTableAttrs, infoMsg: "moreInfo_msg", - infoLinkId: InfoLink.DomainInfo, + infoLinkId: InfoLinks.DomainInfo, }), m(AccountMaintenanceSettings, { customerServerProperties: this.props, diff --git a/src/mail-app/settings/emaildomain/VerifyDnsRecordsPage.ts b/src/mail-app/settings/emaildomain/VerifyDnsRecordsPage.ts index 25dc432ac2e5..5f0bc339a0a4 100644 --- a/src/mail-app/settings/emaildomain/VerifyDnsRecordsPage.ts +++ b/src/mail-app/settings/emaildomain/VerifyDnsRecordsPage.ts @@ -1,7 +1,7 @@ import { DomainDnsStatus } from "../DomainDnsStatus" import m, { Children, Vnode, VnodeDOM } from "mithril" import { assertEnumValue, CustomDomainCheckResult, DnsRecordType, DnsRecordValidation } from "../../../common/api/common/TutanotaConstants" -import { InfoLink, lang, TranslationKey } from "../../../common/misc/LanguageViewModel" +import { InfoLinks, lang, TranslationKey } from "../../../common/misc/LanguageViewModel" import type { AddDomainData, ValidatedDnSRecord } from "./AddDomainWizard" import { Dialog } from "../../../common/gui/base/Dialog" import type { WizardPageAttrs } from "../../../common/gui/base/WizardDialog.js" @@ -187,7 +187,7 @@ export function renderCheckResult(domainStatus: DomainDnsStatus, hideRefreshButt click: () => _updateDnsStatus(domainStatus), }, ), - m(MoreInfoLink, { link: InfoLink.DomainInfo, class: "mt-m", isSmall: true }), + m(MoreInfoLink, { link: InfoLinks.DomainInfo, class: "mt-m", isSmall: true }), ] } else { const errorMessageMap: Record = { diff --git a/src/mail-app/settings/groups/AddGroupDialog.ts b/src/mail-app/settings/groups/AddGroupDialog.ts index 13d7c06c45b1..fe95106df1d3 100644 --- a/src/mail-app/settings/groups/AddGroupDialog.ts +++ b/src/mail-app/settings/groups/AddGroupDialog.ts @@ -5,7 +5,7 @@ import type { ValidationResult } from "../../../common/settings/SelectMailAddres import { SelectMailAddressForm } from "../../../common/settings/SelectMailAddressForm.js" import { getGroupTypeDisplayName } from "../../../common/settings/groups/GroupDetailsView.js" import { showProgressDialog } from "../../../common/gui/dialogs/ProgressDialog.js" -import { InfoLink, lang, TranslationKey } from "../../../common/misc/LanguageViewModel.js" +import { InfoLinks, lang, TranslationKey } from "../../../common/misc/LanguageViewModel.js" import { showBuyDialog } from "../../../common/subscription/BuyDialog.js" import { PreconditionFailedError } from "../../../common/api/common/error/RestError.js" import { showPlanUpgradeRequiredDialog } from "../../../common/misc/SubscriptionDialogs.js" @@ -67,7 +67,7 @@ export class AddGroupDialog implements Component { onDomainChanged, }), m(".mt-m", ""), - m(MoreInfoLink, { link: InfoLink.SharedMailboxes, isSmall: true }), + m(MoreInfoLink, { link: InfoLinks.SharedMailboxes, isSmall: true }), ]) : m(""), ] diff --git a/src/types.d.ts b/src/types.d.ts index ebc8451f2681..79d8ba93bb65 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -57,7 +57,7 @@ type DomainConfig = { giftCardBaseUrl: string /** Which URL to use to build the referral URL. */ referralBaseUrl: string - /** Base URL for requesting any information from de website */ + /** Base URL for requesting any information from the website */ websiteBaseUrl: string } diff --git a/test/TestBuilder.js b/test/TestBuilder.js index 399979ebc28f..a0483b3030a9 100644 --- a/test/TestBuilder.js +++ b/test/TestBuilder.js @@ -152,7 +152,7 @@ async function createUnitTestHtml(localEnv) { console.log(`Generating browser tests at "${htmlFilePath}"`) - const html = await renderHtml(imports, localEnv) + const html = await renderHtml(imports, localEnv, "local") await writeFile(htmlFilePath, html) } diff --git a/test/tests/misc/news/items/ReferralLinkNewsTest.ts b/test/tests/misc/news/items/ReferralLinkNewsTest.ts index a5d5213f9f35..60c1796cfe54 100644 --- a/test/tests/misc/news/items/ReferralLinkNewsTest.ts +++ b/test/tests/misc/news/items/ReferralLinkNewsTest.ts @@ -31,7 +31,7 @@ o.spec("ReferralLinkNews", function () { u2fAppId: "https://app.test.tuta.com/u2f-appid.json", giftCardBaseUrl: "https://app.test.tuta.com/giftcard", referralBaseUrl: "https://app.test.tuta.com/signup", - websiteBaseUrl: "https://tuta.com", + websiteBaseUrl: "https://test.tuta.com", } o.beforeEach(function () {