Add relative time to recent results, support i18n in formatDistance

Closes #2418
This commit is contained in:
Kalle 2025-11-25 18:54:37 +02:00
parent 819ac4dc26
commit 6a88daf921
6 changed files with 86 additions and 7 deletions

View File

@ -1,6 +1,5 @@
import { Link } from "@remix-run/react";
import clsx from "clsx";
import { formatDistanceToNow } from "date-fns";
import * as React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
@ -171,6 +170,7 @@ function ImagePreview({
}) {
const [imageLoaded, setImageLoaded] = React.useState(false);
const { t } = useTranslation(["common", "art"]);
const { formatDistanceToNow } = useTimeFormat();
const img = (
// biome-ignore lint/a11y/noStaticElementInteractions: Biome v2 migration

View File

@ -19,12 +19,14 @@ import styles from "./TournamentCard.module.css";
export function TournamentCard({
tournament,
className,
withRelativeTime = false,
}: {
tournament: CalendarEvent | ShowcaseCalendarEvent;
className?: string;
withRelativeTime?: boolean;
}) {
const isMounted = useIsMounted();
const { formatDateTime } = useTimeFormat();
const { formatDateTime, formatDistanceToNow } = useTimeFormat();
const isShowcase = tournament.type === "showcase";
const isCalendar = tournament.type === "calendar";
@ -35,6 +37,13 @@ export function TournamentCard({
if (!isMounted) return "Placeholder";
const date = databaseTimestampToDate(tournament.startTime);
if (withRelativeTime) {
return formatDistanceToNow(date, {
addSuffix: true,
});
}
return formatDateTime(date, {
month: "short",
day: "numeric",

View File

@ -283,7 +283,11 @@ function ResultHighlights() {
</h2>
<div className={styles.tournamentCardsSpacer}>
{data.tournaments.results.map((tournament) => (
<TournamentCard key={tournament.id} tournament={tournament} />
<TournamentCard
key={tournament.id}
tournament={tournament}
withRelativeTime
/>
))}
</div>
</>

View File

@ -1,6 +1,5 @@
import { Link, useFetcher } from "@remix-run/react";
import clsx from "clsx";
import { formatDistanceToNow } from "date-fns";
import React from "react";
import { useTranslation } from "react-i18next";
import { Avatar } from "~/components/Avatar";
@ -260,7 +259,7 @@ function PostTime({
updatedAt: number;
}) {
const { t } = useTranslation(["lfg"]);
const { formatDate } = useTimeFormat();
const { formatDate, formatDistanceToNow } = useTimeFormat();
const createdAtDate = databaseTimestampToDate(createdAt);
const updatedAtDate = databaseTimestampToDate(updatedAt);

View File

@ -1,5 +1,6 @@
import { useTranslation } from "react-i18next";
import { useUser } from "~/features/auth/core/user";
import { formatDistanceToNow as formatDistanceToNowUtil } from "~/utils/dates";
const H12_TIME_OPTIONS: Intl.DateTimeFormatOptions = {
hour12: true,
@ -94,7 +95,17 @@ export function useTimeFormat() {
return date.toLocaleDateString(i18n.language, options);
};
return { formatDateTime, formatTime, formatDate };
const formatDistanceToNow = (
date: Parameters<typeof formatDistanceToNowUtil>[0],
options?: Omit<Parameters<typeof formatDistanceToNowUtil>[1], "language">,
) => {
return formatDistanceToNowUtil(date, {
...options,
language: i18n.language,
});
};
return { formatDateTime, formatTime, formatDate, formatDistanceToNow };
}
// Example: "09:00" -> "9:00"

View File

@ -1,8 +1,64 @@
import { CalendarDateTime, parseDate } from "@internationalized/date";
import { getWeek } from "date-fns";
import type { Locale } from "date-fns";
import {
formatDistanceToNow as dateFnsFormatDistanceToNow,
getWeek,
} from "date-fns";
import { da } from "date-fns/locale/da";
import { de } from "date-fns/locale/de";
import { enUS } from "date-fns/locale/en-US";
import { es } from "date-fns/locale/es";
import { fr } from "date-fns/locale/fr";
import { frCA } from "date-fns/locale/fr-CA";
import { he } from "date-fns/locale/he";
import { it } from "date-fns/locale/it";
import { ja } from "date-fns/locale/ja";
import { ko } from "date-fns/locale/ko";
import { nl } from "date-fns/locale/nl";
import { pl } from "date-fns/locale/pl";
import { ptBR } from "date-fns/locale/pt-BR";
import { ru } from "date-fns/locale/ru";
import { zhCN } from "date-fns/locale/zh-CN";
import type { MonthYear } from "~/features/plus-voting/core";
import type { LanguageCode } from "~/modules/i18n/config";
import type { DayMonthYear } from "./zod";
const LOCALE_MAP: Record<LanguageCode, Locale> = {
da,
de,
en: enUS,
"es-ES": es,
"es-US": es,
"fr-CA": frCA,
"fr-EU": fr,
he,
it,
ja,
ko,
nl,
pl,
"pt-BR": ptBR,
ru,
zh: zhCN,
};
export function getDateFnsLocale(language: string) {
return LOCALE_MAP[language as keyof typeof LOCALE_MAP] ?? enUS;
}
export function formatDistanceToNow(
date: Parameters<typeof dateFnsFormatDistanceToNow>[0],
options: Omit<
NonNullable<Parameters<typeof dateFnsFormatDistanceToNow>[1]>,
"locale"
> & { language: string },
) {
return dateFnsFormatDistanceToNow(date, {
...options,
locale: getDateFnsLocale(options.language),
});
}
export function databaseTimestampToDate(timestamp: number) {
return new Date(databaseTimestampToJavascriptTimestamp(timestamp));
}