diff --git a/web/src/ui/i18n/resources/de.tsx b/web/src/ui/i18n/resources/de.tsx index 60979628a..542900757 100644 --- a/web/src/ui/i18n/resources/de.tsx +++ b/web/src/ui/i18n/resources/de.tsx @@ -70,7 +70,7 @@ export const translations: Translations<"de"> = { "init script section title": "Zugriff auf den Speicher außerhalb der Datalab-Dienste", "init script section helper": `Laden Sie das Initialisierungsskript in der Programmiersprache Ihrer Wahl herunter.`, - "expires in": ({ howMuchTime }) => `Läuft in ${howMuchTime} ab` + "expires in": ({ howMuchTime }) => <>Läuft in {howMuchTime} ab }, AccountKubernetesTab: { "credentials section title": "Verbindung zum Kubernetes-Cluster herstellen", @@ -93,8 +93,9 @@ export const translations: Translations<"de"> = { kubectl get pods oder helm list bestätigen ), - "expires in": ({ howMuchTime }) => - `Diese Anmeldedaten sind für die nächsten ${howMuchTime} gültig` + "expires in": ({ howMuchTime }) => ( + <>Diese Anmeldedaten sind für die nächsten {howMuchTime} gültig + ) }, AccountVaultTab: { "credentials section title": "Vault-Anmeldeinformationen", @@ -118,7 +119,7 @@ export const translations: Translations<"de"> = { zu konfigurieren. ), - "expires in": ({ howMuchTime }) => `Das Token läuft in ${howMuchTime} ab` + "expires in": ({ howMuchTime }) => <>Das Token läuft in {howMuchTime} ab }, ProjectSettings: { "page header title": "Projekteinstellungen", diff --git a/web/src/ui/i18n/resources/en.tsx b/web/src/ui/i18n/resources/en.tsx index 3ceb461e8..842bd0a4c 100644 --- a/web/src/ui/i18n/resources/en.tsx +++ b/web/src/ui/i18n/resources/en.tsx @@ -71,7 +71,7 @@ export const translations: Translations<"en"> = { "init script section title": "To access your storage outside of datalab services", "init script section helper": "Download or copy the init script in the programming language of your choice.", - "expires in": ({ howMuchTime }) => `Expires in ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Expires in {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Connect to the Kubernetes cluster", @@ -92,8 +92,9 @@ export const translations: Translations<"en"> = { kubectl get pods or helm list ), - "expires in": ({ howMuchTime }) => - `Theses credentials are valid for the next ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Theses credentials are valid for the next {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Vault credentials", @@ -115,7 +116,7 @@ export const translations: Translations<"en"> = { ), - "expires in": ({ howMuchTime }) => `The token expires in ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>The token expires in {howMuchTime} }, ProjectSettings: { "page header title": "Project Settings", diff --git a/web/src/ui/i18n/resources/es.tsx b/web/src/ui/i18n/resources/es.tsx index 948e7e00c..756a58061 100644 --- a/web/src/ui/i18n/resources/es.tsx +++ b/web/src/ui/i18n/resources/es.tsx @@ -73,7 +73,7 @@ export const translations: Translations<"en"> = { "Para acceder a tu almacenamiento fuera de los servicios de datalab", "init script section helper": "Descarga o copia el script de inicialización en el lenguaje de programación de tu elección.", - "expires in": ({ howMuchTime }) => `Expira en ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Expira en {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Conéctate al clúster de Kubernetes", @@ -94,8 +94,9 @@ export const translations: Translations<"en"> = { kubectl get pods o helm list ), - "expires in": ({ howMuchTime }) => - `Estas credenciales son válidas por los próximos ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Estas credenciales son válidas por los próximos {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Credenciales de Vault", @@ -117,7 +118,7 @@ export const translations: Translations<"en"> = { ), - "expires in": ({ howMuchTime }) => `El token expira en ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>El token expira en {howMuchTime} }, ProjectSettings: { "page header title": "Configuración del Proyecto", diff --git a/web/src/ui/i18n/resources/fi.tsx b/web/src/ui/i18n/resources/fi.tsx index cb701439c..4a48de2a0 100644 --- a/web/src/ui/i18n/resources/fi.tsx +++ b/web/src/ui/i18n/resources/fi.tsx @@ -72,7 +72,7 @@ export const translations: Translations<"fi"> = { "Pääsy tallennustilaan datalab-palveluiden ulkopuolelta", "init script section helper": "Lataa tai kopioi alustan tukemat aloituskomenskriptit valitsemallasi ohjelmointikielellä.", - "expires in": ({ howMuchTime }) => `Vanhenee ${howMuchTime} kuluttua` + "expires in": ({ howMuchTime }) => <>Vanhenee {howMuchTime} kuluttua }, AccountKubernetesTab: { "credentials section title": "Yhdistä Kubernetes-klusteriin", @@ -93,8 +93,9 @@ export const translations: Translations<"fi"> = { kubectl get pods tai helm list ), - "expires in": ({ howMuchTime }) => - `Nämä käyttöoikeudet ovat voimassa seuraavat ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Nämä käyttöoikeudet ovat voimassa seuraavat {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Vault-todennustiedot", @@ -116,7 +117,7 @@ export const translations: Translations<"fi"> = { ), - "expires in": ({ howMuchTime }) => `Pääte vanhenee ${howMuchTime} kuluttua` + "expires in": ({ howMuchTime }) => <>Pääte vanhenee {howMuchTime} kuluttua }, ProjectSettings: { "page header title": "Projektiasetukset", diff --git a/web/src/ui/i18n/resources/fr.tsx b/web/src/ui/i18n/resources/fr.tsx index 013e196a2..47f03c820 100644 --- a/web/src/ui/i18n/resources/fr.tsx +++ b/web/src/ui/i18n/resources/fr.tsx @@ -73,7 +73,7 @@ export const translations: Translations<"fr"> = { "Pour accéder au stockage en dehors des services du datalab", "init script section helper": "Téléchargez ou copiez le script d'initialisation dans le langage de programmation de votre choix.", - "expires in": ({ howMuchTime }) => `Expire dans ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Expire dans {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Connection au cluster Kubernetes", @@ -95,8 +95,9 @@ export const translations: Translations<"fr"> = { kubectl get pods ou helm list ), - "expires in": ({ howMuchTime }) => - `Ces identifiants sont valables pour les ${howMuchTime} prochaines` + "expires in": ({ howMuchTime }) => ( + <>Ces identifiants sont valables pour les {howMuchTime} prochaines + ) }, AccountVaultTab: { "credentials section title": "Identifiants Vault", @@ -120,7 +121,7 @@ export const translations: Translations<"fr"> = { local. ), - "expires in": ({ howMuchTime }) => `Le token expire dans ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Le token expire dans {howMuchTime} }, ProjectSettings: { "page header title": "Paramètres du projet", diff --git a/web/src/ui/i18n/resources/it.tsx b/web/src/ui/i18n/resources/it.tsx index 1f6d9e096..5a1d42585 100644 --- a/web/src/ui/i18n/resources/it.tsx +++ b/web/src/ui/i18n/resources/it.tsx @@ -71,7 +71,7 @@ export const translations: Translations<"it"> = { "init script section title": "Per accedere allo storage al di fuori dei servizi del datalab", "init script section helper": `Scarica o copia lo script di inizializzazione nel linguaggio di programmazione di tua scelta.`, - "expires in": ({ howMuchTime }) => `Scade in ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Scade in {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Connetti al cluster Kubernetes", @@ -92,8 +92,9 @@ export const translations: Translations<"it"> = { kubectl get pods o helm list ), - "expires in": ({ howMuchTime }) => - `Queste credenziali sono valide per i prossimi ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Queste credenziali sono valide per i prossimi {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Credenziali Vault", @@ -116,7 +117,7 @@ export const translations: Translations<"it"> = { locale. ), - "expires in": ({ howMuchTime }) => `Il token scade in ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Il token scade in {howMuchTime} }, ProjectSettings: { "page header title": "Impostazioni del Progetto", @@ -356,8 +357,9 @@ export const translations: Translations<"it"> = { la nostra documentazione .   - Configurare il tuo Vault CLI locale - . + + Configurare il tuo Vault CLI locale + . ) }, diff --git a/web/src/ui/i18n/resources/nl.tsx b/web/src/ui/i18n/resources/nl.tsx index 715d3a499..5686c605c 100644 --- a/web/src/ui/i18n/resources/nl.tsx +++ b/web/src/ui/i18n/resources/nl.tsx @@ -70,7 +70,7 @@ export const translations: Translations<"nl"> = { "init script section title": "Om toegang te krijgen tot opslag buiten de diensten van het datalab", "init script section helper": `Download of kopieer het initialisatiescript in de programmeertaal van uw keuze.`, - "expires in": ({ howMuchTime }) => `Vervalt binnen ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Vervalt binnen {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Verbind met de Kubernetes-cluster", @@ -92,8 +92,9 @@ export const translations: Translations<"nl"> = { kubectl get pods of helm list uit te voeren ), - "expires in": ({ howMuchTime }) => - `Deze inloggegevens zijn geldig voor de komende ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Deze inloggegevens zijn geldig voor de komende {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Gebrukersnamen Vault", @@ -116,7 +117,7 @@ export const translations: Translations<"nl"> = { in te stellen. ), - "expires in": ({ howMuchTime }) => `Het token vervalt in ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Het token vervalt in {howMuchTime} }, ProjectSettings: { "page header title": "Projectinstellingen", diff --git a/web/src/ui/i18n/resources/no.tsx b/web/src/ui/i18n/resources/no.tsx index 59b88d8fe..1db8e83c4 100644 --- a/web/src/ui/i18n/resources/no.tsx +++ b/web/src/ui/i18n/resources/no.tsx @@ -71,7 +71,7 @@ export const translations: Translations<"no"> = { "For å få tilgang til lagringen din utenfor datalabtjenestene", "init script section helper": "Last ned eller kopier initialiseringskriptet i programingsspråket du foretrekker.", - "expires in": ({ howMuchTime }) => `Utløper om ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Utløper om {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "Koble til Kubernetes-klusteret", @@ -93,8 +93,9 @@ export const translations: Translations<"no"> = { kubectl get pods eller helm list ), - "expires in": ({ howMuchTime }) => - `Disse legitimasjonene er gyldige for de neste ${howMuchTime}` + "expires in": ({ howMuchTime }) => ( + <>Disse legitimasjonene er gyldige for de neste {howMuchTime} + ) }, AccountVaultTab: { "credentials section title": "Vault credentials", @@ -117,7 +118,7 @@ export const translations: Translations<"no"> = { ), - "expires in": ({ howMuchTime }) => `Token går ut om ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>Token går ut om {howMuchTime} }, ProjectSettings: { "page header title": "Prosjektinnstillinger", diff --git a/web/src/ui/i18n/resources/zh-CN.tsx b/web/src/ui/i18n/resources/zh-CN.tsx index 46f05bbd4..47385b935 100644 --- a/web/src/ui/i18n/resources/zh-CN.tsx +++ b/web/src/ui/i18n/resources/zh-CN.tsx @@ -66,7 +66,7 @@ export const translations: Translations<"zh-CN"> = { "accessible as env": "可在您的服务中作为环境变量被访问", "init script section title": "访问datalab服务之外的存储器", "init script section helper": `下载或复制用您选择的编程语言编写的初始化脚本.`, - "expires in": ({ howMuchTime }) => `有效期至 ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>有效期至 {howMuchTime} }, AccountKubernetesTab: { "credentials section title": "连接到 Kubernetes 集群", @@ -85,7 +85,7 @@ export const translations: Translations<"zh-CN"> = { kubectl get podshelm list ), - "expires in": ({ howMuchTime }) => `这些凭证在接下来的 ${howMuchTime} 内有效` + "expires in": ({ howMuchTime }) => <>这些凭证在接下来的 {howMuchTime} 内有效 }, AccountVaultTab: { "credentials section title": "保险库凭证", @@ -108,7 +108,7 @@ export const translations: Translations<"zh-CN"> = { 的 ENV 变量。 ), - "expires in": ({ howMuchTime }) => `该令牌有效期至 ${howMuchTime}` + "expires in": ({ howMuchTime }) => <>该令牌有效期至 {howMuchTime} }, ProjectSettings: { "page header title": "项目设置", diff --git a/web/src/ui/pages/account/AccountKubernetesTab.tsx b/web/src/ui/pages/account/AccountKubernetesTab.tsx index 3227db3b0..8b3da418f 100644 --- a/web/src/ui/pages/account/AccountKubernetesTab.tsx +++ b/web/src/ui/pages/account/AccountKubernetesTab.tsx @@ -43,7 +43,7 @@ export const AccountKubernetesTab = memo((props: Props) => { shellScript } = useCoreState("k8sCodeSnippets", "main"); - const { fromNowText } = useFromNow({ dateTime: expirationTime ?? 0 }); + const { fromNowText } = useFromNow({ dateTime: expirationTime }); useEffect(() => { k8sCodeSnippets.refresh(); @@ -170,7 +170,7 @@ const { i18n } = declareComponentKeys< P: { installKubectlUrl: string }; R: JSX.Element; } - | { K: "expires in"; P: { howMuchTime: string } } + | { K: "expires in"; P: { howMuchTime: JSX.Element }; R: JSX.Element } >()({ AccountKubernetesTab }); export type I18n = typeof i18n; diff --git a/web/src/ui/pages/account/AccountStorageTab.tsx b/web/src/ui/pages/account/AccountStorageTab.tsx index 98568e8b1..bfb8fc2fc 100644 --- a/web/src/ui/pages/account/AccountStorageTab.tsx +++ b/web/src/ui/pages/account/AccountStorageTab.tsx @@ -66,7 +66,7 @@ export const AccountStorageTab = memo((props: Props) => { selectedTechnology } = useCoreState("s3CodeSnippets", "main"); - const { fromNowText } = useFromNow({ dateTime: expirationTime ?? 0 }); + const { fromNowText } = useFromNow({ dateTime: expirationTime }); const onSelectChangeTechnology = useConstCallback((e: SelectChangeEvent) => s3CodeSnippets.changeTechnology({ technology: e.target.value as Technology @@ -188,7 +188,7 @@ const { i18n } = declareComponentKeys< | "accessible as env" | "init script section title" | "init script section helper" - | { K: "expires in"; P: { howMuchTime: string } } + | { K: "expires in"; P: { howMuchTime: JSX.Element }; R: JSX.Element } >()({ AccountStorageTab }); export type I18n = typeof i18n; diff --git a/web/src/ui/pages/account/AccountVaultTab.tsx b/web/src/ui/pages/account/AccountVaultTab.tsx index 6fe161512..b4ca3e137 100644 --- a/web/src/ui/pages/account/AccountVaultTab.tsx +++ b/web/src/ui/pages/account/AccountVaultTab.tsx @@ -35,7 +35,7 @@ export const AccountVaultTab = memo((props: Props) => { const uiState = useCoreState("vaultCredentials", "main"); - const { fromNowText } = useFromNow({ dateTime: uiState?.expirationTime ?? 0 }); + const { fromNowText } = useFromNow({ dateTime: uiState?.expirationTime }); useEffect(() => { vaultCredentials.refresh({ doForceRenewToken: false }); @@ -140,7 +140,7 @@ const { i18n } = declareComponentKeys< } | "init script section title" | { K: "init script section helper"; P: { vaultCliDocLink: string }; R: JSX.Element } - | { K: "expires in"; P: { howMuchTime: string } } + | { K: "expires in"; P: { howMuchTime: JSX.Element }; R: JSX.Element } >()({ AccountVaultTab }); export type I18n = typeof i18n; diff --git a/web/src/ui/shared/formattedDate/useFormattedDate.ts b/web/src/ui/shared/formattedDate/useFormattedDate.ts deleted file mode 100644 index e7d6d2d7a..000000000 --- a/web/src/ui/shared/formattedDate/useFormattedDate.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { useMemo, useEffect, useState } from "react"; -import { useLang } from "ui/i18n"; -import { getFormattedDate } from "./getFormattedDate"; -import { fromNow } from "./dateTimeFormatter"; - -export function useFormattedDate(params: { time: number }): string { - const { time } = params; - - // NOTE: So that we get a refresh when the lang is changed. - const { lang } = useLang(); - - return useMemo(() => getFormattedDate({ time, lang }), [time, lang]); -} - -export function useFromNow(params: { dateTime: number }) { - const { dateTime } = params; - - const { lang } = useLang(); - - const [fromNowText, setFromNowText] = useState(() => fromNow({ dateTime })); - - useEffect(() => { - const updateText = () => { - const newText = fromNow({ dateTime }); - - if (fromNowText !== newText) { - setFromNowText(newText); - } - }; - - updateText(); - - const timer = setInterval(updateText, 1000); - - return () => { - clearInterval(timer); - }; - }, [dateTime, lang]); - - return { fromNowText }; -} diff --git a/web/src/ui/shared/formattedDate/useFormattedDate.tsx b/web/src/ui/shared/formattedDate/useFormattedDate.tsx new file mode 100644 index 000000000..5a7cd3873 --- /dev/null +++ b/web/src/ui/shared/formattedDate/useFormattedDate.tsx @@ -0,0 +1,48 @@ +import { useMemo, useEffect, useId } from "react"; +import { useLang } from "ui/i18n"; +import { getFormattedDate } from "./getFormattedDate"; +import { fromNow } from "./dateTimeFormatter"; +import { useConstCallback } from "powerhooks/useConstCallback"; + +export function useFormattedDate(params: { time: number }): string { + const { time } = params; + + // NOTE: So that we get a refresh when the lang is changed. + const { lang } = useLang(); + + return useMemo(() => getFormattedDate({ time, lang }), [time, lang]); +} + +export function useFromNow(params: { dateTime: number | undefined }) { + const { dateTime } = params; + + const id = useId(); + + const fromNowText = useMemo(() => , [id]); + + const updateNode = useConstCallback(() => { + if (dateTime === undefined) { + return; + } + + const el = document.getElementById(id); + + if (el === null) { + return; + } + + el.innerHTML = fromNow({ dateTime }); + }); + + useEffect(() => { + updateNode(); + }, [dateTime, id]); + + useEffect(() => { + const timer = setInterval(updateNode, 1000); + + return () => clearInterval(timer); + }, []); + + return { fromNowText }; +}