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 pods
或 helm 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 };
+}