diff --git a/app/components/Chart.tsx b/app/components/Chart.tsx index 940723ad3..582ca84af 100644 --- a/app/components/Chart.tsx +++ b/app/components/Chart.tsx @@ -126,10 +126,10 @@ function ChartTooltip({ return (
-

+

{header()} {headerSuffix} -

+ {dataPoints.map((dataPoint, index) => { const color = dataPoint.style?.fill ?? "var(--color-accent)"; diff --git a/app/components/MobileNav.tsx b/app/components/MobileNav.tsx index 7d7f18c89..cba06a610 100644 --- a/app/components/MobileNav.tsx +++ b/app/components/MobileNav.tsx @@ -380,9 +380,9 @@ function MenuOverlay({
-

+

{t("front:sideNav.streams")} -

+ {streams.length === 0 ? (
diff --git a/app/components/layout/ChatSidebar.tsx b/app/components/layout/ChatSidebar.tsx index 16f1ba711..4087b93fc 100644 --- a/app/components/layout/ChatSidebar.tsx +++ b/app/components/layout/ChatSidebar.tsx @@ -3,12 +3,15 @@ import { ArrowLeft, MessageSquare, X } from "lucide-react"; import { Button } from "react-aria-components"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; +import { resolveDatePlaceholders } from "~/features/chat/chat-utils"; import { Chat } from "~/features/chat/components/Chat"; import { useChatContext } from "~/features/chat/useChatContext"; import { useTimeFormat } from "~/hooks/useTimeFormat"; import sideNavStyles from "../SideNav.module.css"; import styles from "./ChatSidebar.module.css"; +// xxx: add navIcon/url per room + export function ChatSidebar({ onClose }: { onClose?: () => void }) { const chatContext = useChatContext(); @@ -94,7 +97,14 @@ function RoomList({ onClose }: { onClose?: () => void }) { styles.roomName, )} > - {room.header} + {resolveDatePlaceholders(room.header, (d) => + formatDateTime(d, { + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", + }), + )} {room.subtitle} @@ -122,6 +132,7 @@ function RoomList({ onClose }: { onClose?: () => void }) { function ChatView({ onClose }: { onClose?: () => void }) { const chatContext = useChatContext()!; const activeRoom = chatContext.activeRoom!; + const { formatDateTime } = useTimeFormat(); const otherRoomsUnreadCount = Object.entries(chatContext.unreadCounts) .filter(([code]) => code !== activeRoom) @@ -147,7 +158,14 @@ function ChatView({ onClose }: { onClose?: () => void }) { const headerContent = ( <> - {room?.header ?? activeRoom} + {resolveDatePlaceholders(room?.header ?? activeRoom, (d) => + formatDateTime(d, { + month: "short", + day: "numeric", + hour: "numeric", + minute: "numeric", + }), + )} {room?.subtitle ? ( {room.subtitle} diff --git a/app/features/badges/routes/badges.$id.edit.tsx b/app/features/badges/routes/badges.$id.edit.tsx index c7aea85de..702b2f207 100644 --- a/app/features/badges/routes/badges.$id.edit.tsx +++ b/app/features/badges/routes/badges.$id.edit.tsx @@ -54,7 +54,7 @@ function Managers({ data }: { data: BadgeDetailsLoaderData }) { return (
-

Managers

+

Managers

m.id).join("-")} label="Add new manager" @@ -135,7 +135,7 @@ function Owners({ data }: { data: BadgeDetailsLoaderData }) { return (
-

Owners

+

Owners

-

+

{title}{" "} {popoverInfo && ( )} -

+

diff --git a/app/features/chat/chat-utils.test.ts b/app/features/chat/chat-utils.test.ts new file mode 100644 index 000000000..f6c8349c5 --- /dev/null +++ b/app/features/chat/chat-utils.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, test } from "vitest"; +import { datePlaceholder, resolveDatePlaceholders } from "./chat-utils"; + +describe("datePlaceholder", () => { + test("returns correctly formatted placeholder string", () => { + const date = new Date(1700000000000); + + expect(datePlaceholder(date)).toBe("{{date:1700000000000}}"); + }); +}); + +describe("resolveDatePlaceholders", () => { + const mockFormat = (d: Date) => `FORMATTED:${d.getTime()}`; + + test("replaces a single placeholder with formatted date", () => { + const text = "Starts at {{date:1700000000000}}"; + + expect(resolveDatePlaceholders(text, mockFormat)).toBe( + "Starts at FORMATTED:1700000000000", + ); + }); + + test("replaces multiple placeholders in one string", () => { + const text = "From {{date:1700000000000}} to {{date:1700003600000}}"; + + expect(resolveDatePlaceholders(text, mockFormat)).toBe( + "From FORMATTED:1700000000000 to FORMATTED:1700003600000", + ); + }); + + test("returns text unchanged when no placeholders present", () => { + const text = "Just a normal string"; + + expect(resolveDatePlaceholders(text, mockFormat)).toBe(text); + }); + + test("handles text that is only a placeholder", () => { + const text = "{{date:1700000000000}}"; + + expect(resolveDatePlaceholders(text, mockFormat)).toBe( + "FORMATTED:1700000000000", + ); + }); +}); diff --git a/app/features/chat/chat-utils.ts b/app/features/chat/chat-utils.ts index d23594587..56de7d0e1 100644 --- a/app/features/chat/chat-utils.ts +++ b/app/features/chat/chat-utils.ts @@ -1,5 +1,20 @@ import type { ChatMessage } from "./chat-types"; +const DATE_PLACEHOLDER_PATTERN = /\{\{date:(\d+)\}\}/g; + +export function datePlaceholder(date: Date): string { + return `{{date:${date.getTime()}}}`; +} + +export function resolveDatePlaceholders( + text: string, + formatDateTime: (date: Date) => string, +): string { + return text.replace(DATE_PLACEHOLDER_PATTERN, (_match, ts) => + formatDateTime(new Date(Number(ts))), + ); +} + export function messageTypeToSound(type: ChatMessage["type"]) { if (type === "LIKE_RECEIVED") return "sq_like"; if (type === "MATCH_STARTED") return "sq_match"; diff --git a/app/features/plus-voting/routes/plus.voting.results.tsx b/app/features/plus-voting/routes/plus.voting.results.tsx index 6796ed17f..08ccdbe5d 100644 --- a/app/features/plus-voting/routes/plus.voting.results.tsx +++ b/app/features/plus-voting/routes/plus.voting.results.tsx @@ -80,9 +80,9 @@ function Results({
{results.map((tiersResults) => (
-

+

+{tiersResults.tier} -

+

{(["passed", "failed"] as const).map((status) => (

diff --git a/app/features/scrims/actions/scrims.server.ts b/app/features/scrims/actions/scrims.server.ts index 96cd5cb5a..a894b975b 100644 --- a/app/features/scrims/actions/scrims.server.ts +++ b/app/features/scrims/actions/scrims.server.ts @@ -3,6 +3,7 @@ import type { ActionFunctionArgs } from "react-router"; import { redirect } from "react-router"; import { requireUser } from "~/features/auth/core/user.server"; import * as ChatSystemMessage from "~/features/chat/ChatSystemMessage.server"; +import { datePlaceholder } from "~/features/chat/chat-utils"; import { notify } from "~/features/notifications/core/notify.server"; import * as UserRepository from "~/features/user-page/UserRepository.server"; import { requirePermission } from "~/modules/permissions/guards.server"; @@ -113,9 +114,10 @@ export const action = async ({ request }: ActionFunctionArgs) => { if (fullPost?.chatCode) { ChatSystemMessage.setMetadata({ chatCode: fullPost.chatCode, - header: "Scrim", - // xxx: better subtitle? - subtitle: `Scrim #${post.id}`, + header: datePlaceholder( + databaseTimestampToDate(request.at ?? post.at), + ), + subtitle: "Scrim", url: scrimPage(post.id), participantUserIds: Scrim.participantIdsListFromAccepted(fullPost), expiresAt: add(databaseTimestampToDate(request.at ?? post.at), { diff --git a/app/features/scrims/components/ScrimCard.tsx b/app/features/scrims/components/ScrimCard.tsx index 94d04da44..b1f4a3141 100644 --- a/app/features/scrims/components/ScrimCard.tsx +++ b/app/features/scrims/components/ScrimCard.tsx @@ -61,7 +61,7 @@ export function ScrimPostCard({ owner={owner} />

-

+

{isPickup ? ( <> {t("scrims:pickupBy")} @@ -70,7 +70,7 @@ export function ScrimPostCard({ ) : ( teamName )} -

+
{post.isPrivate ? : null} @@ -496,7 +496,7 @@ export function ScrimRequestCard({ owner={owner} />
-

+

{isPickup ? ( <> {t("scrims:pickupBy")} @@ -505,7 +505,7 @@ export function ScrimRequestCard({ ) : ( teamName )} -

+
diff --git a/app/features/sendouq-match/routes/q.match.$id.tsx b/app/features/sendouq-match/routes/q.match.$id.tsx index e7c9b2cf5..474e2c5ac 100644 --- a/app/features/sendouq-match/routes/q.match.$id.tsx +++ b/app/features/sendouq-match/routes/q.match.$id.tsx @@ -492,7 +492,7 @@ function ReportWeaponsForm() { value={JSON.stringify(weaponsUsage)} />
-

{t("q:match.report.whoToReport")}

+

{t("q:match.report.whoToReport")}