diff --git a/app/models/ApiKey.ts b/app/models/ApiKey.ts
index 324d568f2..dddeddaf6 100644
--- a/app/models/ApiKey.ts
+++ b/app/models/ApiKey.ts
@@ -1,4 +1,5 @@
-import { observable } from "mobx";
+import { isPast } from "date-fns";
+import { computed, observable } from "mobx";
import Model from "./base/Model";
import Field from "./decorators/Field";
@@ -9,15 +10,29 @@ class ApiKey extends Model {
@observable
id: string;
+ /**
+ * The user chosen name of the API key.
+ */
@Field
@observable
name: string;
+ /**
+ * An optional datetime that the API key expires.
+ */
@Field
@observable
expiresAt?: string;
secret: string;
+
+ /**
+ * Whether the API key has an expiry in the past.
+ */
+ @computed
+ get isExpired() {
+ return this.expiresAt ? isPast(new Date(this.expiresAt)) : false;
+ }
}
export default ApiKey;
diff --git a/app/scenes/Settings/components/ApiKeyListItem.tsx b/app/scenes/Settings/components/ApiKeyListItem.tsx
index 5cb4011e1..b60126f31 100644
--- a/app/scenes/Settings/components/ApiKeyListItem.tsx
+++ b/app/scenes/Settings/components/ApiKeyListItem.tsx
@@ -1,4 +1,3 @@
-import { isPast } from "date-fns";
import { CopyIcon } from "outline-icons";
import * as React from "react";
import { useTranslation } from "react-i18next";
@@ -23,17 +22,17 @@ const ApiKeyListItem = ({ apiKey, isCopied, onCopy }: Props) => {
const { t } = useTranslation();
const userLocale = useUserLocale();
- const hasExpired = apiKey.expiresAt
- ? isPast(new Date(apiKey.expiresAt))
- : false;
-
const subtitle = (
-
- {t(`Created`)} ·{" "}
- {apiKey.expiresAt
- ? dateToExpiry(apiKey.expiresAt, t, userLocale)
- : t("No expiry")}
-
+ <>
+
+ {t(`Created`)} ·{" "}
+
+
+ {apiKey.expiresAt
+ ? dateToExpiry(apiKey.expiresAt, t, userLocale)
+ : t("No expiry")}
+
+ >
);
const handleCopy = React.useCallback(() => {
diff --git a/app/utils/date.ts b/app/utils/date.ts
index 8ee1364f7..8918eb2d9 100644
--- a/app/utils/date.ts
+++ b/app/utils/date.ts
@@ -74,6 +74,13 @@ export function dateToHeading(
});
}
+/**
+ * Converts a date string to a human-readable expiry string.
+ *
+ * @param dateTime The date string to convert
+ * @param t The translation function
+ * @param userLocale The user's locale
+ */
export function dateToExpiry(
dateTime: string,
t: TFunction,
@@ -84,30 +91,34 @@ export function dateToExpiry(
const locale = dateLocale(userLocale);
if (isYesterday(date)) {
- return t("Expired Yesterday");
+ return t("Expired yesterday");
}
if (isPast(date)) {
- return `${t("Expired on")} ${formatDate(date, "MMM dd, yyyy", { locale })}`;
- }
-
- if (isToday(date)) {
- return t("Expires Today");
- }
-
- if (isTomorrow(date)) {
- return t("Expires Tomorrow");
- }
-
- const prefix = t("Expires on");
-
- if (isSameWeek(date, now)) {
- return `${prefix} ${formatDate(Date.parse(dateTime), "iiii", {
- locale,
+ return `${t("Expired {{ date }}", {
+ date: formatDate(date, "MMM dd, yyyy", { locale }),
})}`;
}
- return `${prefix} ${formatDate(date, "MMM dd, yyyy", { locale })}`;
+ if (isToday(date)) {
+ return t("Expires today");
+ }
+
+ if (isTomorrow(date)) {
+ return t("Expires tomorrow");
+ }
+
+ if (isSameWeek(date, now)) {
+ return t("Expires {{ date }}", {
+ date: formatDate(Date.parse(dateTime), "iiii", {
+ locale,
+ }),
+ });
+ }
+
+ return t("Expires {{ date }}", {
+ date: formatDate(date, "MMM dd, yyyy", { locale }),
+ });
}
/**
diff --git a/shared/i18n/locales/en_US/translation.json b/shared/i18n/locales/en_US/translation.json
index 575532aa0..1235e6373 100644
--- a/shared/i18n/locales/en_US/translation.json
+++ b/shared/i18n/locales/en_US/translation.json
@@ -969,11 +969,11 @@
"This month": "This month",
"Last month": "Last month",
"This year": "This year",
- "Expired Yesterday": "Expired Yesterday",
- "Expired on": "Expired on",
- "Expires Today": "Expires Today",
- "Expires Tomorrow": "Expires Tomorrow",
- "Expires on": "Expires on",
+ "Expired yesterday": "Expired yesterday",
+ "Expired {{ date }}": "Expired {{ date }}",
+ "Expires today": "Expires today",
+ "Expires tomorrow": "Expires tomorrow",
+ "Expires {{ date }}": "Expires {{ date }}",
"Connect": "Connect",
"Whoops, you need to accept the permissions in GitHub to connect {{appName}} to your workspace. Try again?": "Whoops, you need to accept the permissions in GitHub to connect {{appName}} to your workspace. Try again?",
"Something went wrong while authenticating your request. Please try logging in again.": "Something went wrong while authenticating your request. Please try logging in again.",