From 5c8bcc11b4f31069564189325068207e3a7d4f1d Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 7 Sep 2023 22:48:19 -0400 Subject: [PATCH] fix: App switches back to default installation language when navigating to root --- app/components/ChangeLanguage.tsx | 17 +++++ .../AuthenticationProvider.tsx | 0 app/scenes/Login/components/BackButton.tsx | 51 +++++++++++++ app/scenes/Login/{ => components}/Notices.tsx | 0 app/scenes/Login/index.tsx | 72 +++++-------------- app/utils/language.ts | 24 +++++-- shared/i18n/locales/en_US/translation.json | 30 ++++---- 7 files changed, 117 insertions(+), 77 deletions(-) create mode 100644 app/components/ChangeLanguage.tsx rename app/scenes/Login/{ => components}/AuthenticationProvider.tsx (100%) create mode 100644 app/scenes/Login/components/BackButton.tsx rename app/scenes/Login/{ => components}/Notices.tsx (100%) diff --git a/app/components/ChangeLanguage.tsx b/app/components/ChangeLanguage.tsx new file mode 100644 index 0000000000..c5f9650719 --- /dev/null +++ b/app/components/ChangeLanguage.tsx @@ -0,0 +1,17 @@ +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import { changeLanguage } from "~/utils/language"; + +type Props = { + locale: string; +}; + +export default function ChangeLanguage({ locale }: Props) { + const { i18n } = useTranslation(); + + React.useEffect(() => { + void changeLanguage(locale, i18n); + }, [locale, i18n]); + + return null; +} diff --git a/app/scenes/Login/AuthenticationProvider.tsx b/app/scenes/Login/components/AuthenticationProvider.tsx similarity index 100% rename from app/scenes/Login/AuthenticationProvider.tsx rename to app/scenes/Login/components/AuthenticationProvider.tsx diff --git a/app/scenes/Login/components/BackButton.tsx b/app/scenes/Login/components/BackButton.tsx new file mode 100644 index 0000000000..28ce8aa6b2 --- /dev/null +++ b/app/scenes/Login/components/BackButton.tsx @@ -0,0 +1,51 @@ +import { BackIcon } from "outline-icons"; +import * as React from "react"; +import { useTranslation } from "react-i18next"; +import styled from "styled-components"; +import { parseDomain } from "@shared/utils/domains"; +import { Config } from "~/stores/AuthStore"; +import env from "~/env"; +import Desktop from "~/utils/Desktop"; +import isCloudHosted from "~/utils/isCloudHosted"; + +type Props = { + config?: Config; +}; + +export default function BackButton({ config }: Props) { + const { t } = useTranslation(); + const isSubdomain = !!config?.hostname; + + if (!isCloudHosted || parseDomain(window.location.origin).custom) { + return null; + } + + if (Desktop.isElectron() && !isSubdomain) { + return null; + } + + return ( + + {Desktop.isElectron() ? t("Back") : t("Back to home")} + + ); +} + +const Link = styled.a` + display: flex; + align-items: center; + color: inherit; + padding: ${Desktop.isElectron() ? "48px 32px" : "32px"}; + font-weight: 500; + position: absolute; + + svg { + transition: transform 100ms ease-in-out; + } + + &:hover { + svg { + transform: translateX(-4px); + } + } +`; diff --git a/app/scenes/Login/Notices.tsx b/app/scenes/Login/components/Notices.tsx similarity index 100% rename from app/scenes/Login/Notices.tsx rename to app/scenes/Login/components/Notices.tsx diff --git a/app/scenes/Login/index.tsx b/app/scenes/Login/index.tsx index ca9500614e..de42458902 100644 --- a/app/scenes/Login/index.tsx +++ b/app/scenes/Login/index.tsx @@ -1,6 +1,6 @@ import find from "lodash/find"; import { observer } from "mobx-react"; -import { BackIcon, EmailIcon } from "outline-icons"; +import { EmailIcon } from "outline-icons"; import * as React from "react"; import { Trans, useTranslation } from "react-i18next"; import { useLocation, Link, Redirect } from "react-router-dom"; @@ -11,6 +11,7 @@ import { UserPreference } from "@shared/types"; import { parseDomain } from "@shared/utils/domains"; import { Config } from "~/stores/AuthStore"; import ButtonLarge from "~/components/ButtonLarge"; +import ChangeLanguage from "~/components/ChangeLanguage"; import Fade from "~/components/Fade"; import Flex from "~/components/Flex"; import Heading from "~/components/Heading"; @@ -27,28 +28,10 @@ import useStores from "~/hooks/useStores"; import { draggableOnDesktop } from "~/styles"; import Desktop from "~/utils/Desktop"; import isCloudHosted from "~/utils/isCloudHosted"; -import { changeLanguage, detectLanguage } from "~/utils/language"; -import AuthenticationProvider from "./AuthenticationProvider"; -import Notices from "./Notices"; - -function Header({ config }: { config?: Config | undefined }) { - const { t } = useTranslation(); - const isSubdomain = !!config?.hostname; - - if (!isCloudHosted || parseDomain(window.location.origin).custom) { - return null; - } - - if (Desktop.isElectron() && !isSubdomain) { - return null; - } - - return ( - - {Desktop.isElectron() ? t("Back") : t("Back to home")} - - ); -} +import { detectLanguage } from "~/utils/language"; +import AuthenticationProvider from "./components/AuthenticationProvider"; +import BackButton from "./components/BackButton"; +import Notices from "./components/Notices"; type Props = { children?: (config?: Config) => React.ReactNode; @@ -59,7 +42,7 @@ function Login({ children }: Props) { const query = useQuery(); const notice = query.get("notice"); - const { t, i18n } = useTranslation(); + const { t } = useTranslation(); const { auth } = useStores(); const { config } = auth; const [error, setError] = React.useState(null); @@ -97,13 +80,6 @@ function Login({ children }: Props) { auth.fetchConfig().catch(setError); }, [auth]); - // TODO: Persist detected language to new user - // Try to detect the user's language and show the login page on its idiom - // if translation is available - React.useEffect(() => { - void changeLanguage(detectLanguage(), i18n); - }, [i18n]); - React.useEffect(() => { const entries = Object.fromEntries(query.entries()); const existing = getCookie("signupQueryParams"); @@ -134,7 +110,8 @@ function Login({ children }: Props) { if (error) { return ( -
+ + {t("Error")} @@ -165,7 +142,8 @@ function Login({ children }: Props) { if (isCloudHosted && isCustomDomain && !config.name) { return ( -
+ + {t("Almost there")}… @@ -182,7 +160,8 @@ function Login({ children }: Props) { if (Desktop.isElectron() && notice === "domain-required") { return ( -
+ + -
+ @@ -248,7 +227,9 @@ function Login({ children }: Props) { return ( -
+ + + If you were invited to a workspace, you will find a link to it in the invite email.": "Unable to sign-in. Please navigate to your workspace's custom URL, then try to sign-in again.<1>If you were invited to a workspace, you will find a link to it in the invite email.", + "Sorry, a new account cannot be created with a personal Gmail address.<1>Please use a Google Workspaces account instead.": "Sorry, a new account cannot be created with a personal Gmail address.<1>Please use a Google Workspaces account instead.", + "The workspace associated with your user is scheduled for deletion and cannot at accessed at this time.": "The workspace associated with your user is scheduled for deletion and cannot at accessed at this time.", + "The workspace you authenticated with is not authorized on this installation. Try another?": "The workspace you authenticated with is not authorized on this installation. Try another?", + "We could not read the user info supplied by your identity provider.": "We could not read the user info supplied by your identity provider.", + "Your account uses email sign-in, please sign-in with email to continue.": "Your account uses email sign-in, please sign-in with email to continue.", + "An email sign-in link was recently sent, please check your inbox or try again in a few minutes.": "An email sign-in link was recently sent, please check your inbox or try again in a few minutes.", + "Authentication failed – we were unable to sign you in at this time. Please try again.": "Authentication failed – we were unable to sign you in at this time. Please try again.", + "Authentication failed – you do not have permission to access this workspace.": "Authentication failed – you do not have permission to access this workspace.", + "Sorry, it looks like that sign-in link is no longer valid, please try requesting another.": "Sorry, it looks like that sign-in link is no longer valid, please try requesting another.", + "Your account has been suspended. To re-activate your account, please contact a workspace admin.": "Your account has been suspended. To re-activate your account, please contact a workspace admin.", + "Authentication failed – this login method was disabled by a team admin.": "Authentication failed – this login method was disabled by a team admin.", + "The workspace you are trying to join requires an invite before you can create an account.<1>Please request an invite from your workspace admin and try again.": "The workspace you are trying to join requires an invite before you can create an account.<1>Please request an invite from your workspace admin and try again.", + "Sorry, your domain is not allowed. Please try again with an allowed workspace domain.": "Sorry, your domain is not allowed. Please try again with an allowed workspace domain.", "Login": "Login", "Error": "Error", "Failed to load configuration.": "Failed to load configuration.", @@ -681,21 +696,6 @@ "You signed in with {{ authProviderName }} last time.": "You signed in with {{ authProviderName }} last time.", "Or": "Or", "Already have an account? Go to <1>login.": "Already have an account? Go to <1>login.", - "The domain associated with your email address has not been allowed for this workspace.": "The domain associated with your email address has not been allowed for this workspace.", - "Unable to sign-in. Please navigate to your workspace's custom URL, then try to sign-in again.<1>If you were invited to a workspace, you will find a link to it in the invite email.": "Unable to sign-in. Please navigate to your workspace's custom URL, then try to sign-in again.<1>If you were invited to a workspace, you will find a link to it in the invite email.", - "Sorry, a new account cannot be created with a personal Gmail address.<1>Please use a Google Workspaces account instead.": "Sorry, a new account cannot be created with a personal Gmail address.<1>Please use a Google Workspaces account instead.", - "The workspace associated with your user is scheduled for deletion and cannot at accessed at this time.": "The workspace associated with your user is scheduled for deletion and cannot at accessed at this time.", - "The workspace you authenticated with is not authorized on this installation. Try another?": "The workspace you authenticated with is not authorized on this installation. Try another?", - "We could not read the user info supplied by your identity provider.": "We could not read the user info supplied by your identity provider.", - "Your account uses email sign-in, please sign-in with email to continue.": "Your account uses email sign-in, please sign-in with email to continue.", - "An email sign-in link was recently sent, please check your inbox or try again in a few minutes.": "An email sign-in link was recently sent, please check your inbox or try again in a few minutes.", - "Authentication failed – we were unable to sign you in at this time. Please try again.": "Authentication failed – we were unable to sign you in at this time. Please try again.", - "Authentication failed – you do not have permission to access this workspace.": "Authentication failed – you do not have permission to access this workspace.", - "Sorry, it looks like that sign-in link is no longer valid, please try requesting another.": "Sorry, it looks like that sign-in link is no longer valid, please try requesting another.", - "Your account has been suspended. To re-activate your account, please contact a workspace admin.": "Your account has been suspended. To re-activate your account, please contact a workspace admin.", - "Authentication failed – this login method was disabled by a team admin.": "Authentication failed – this login method was disabled by a team admin.", - "The workspace you are trying to join requires an invite before you can create an account.<1>Please request an invite from your workspace admin and try again.": "The workspace you are trying to join requires an invite before you can create an account.<1>Please request an invite from your workspace admin and try again.", - "Sorry, your domain is not allowed. Please try again with an allowed workspace domain.": "Sorry, your domain is not allowed. Please try again with an allowed workspace domain.", "Any collection": "Any collection", "Any time": "Any time", "Past day": "Past day",