diff --git a/app/features/notifications/components/NotificationList.tsx b/app/features/notifications/components/NotificationList.tsx
index 3d2b29d99..2ec373477 100644
--- a/app/features/notifications/components/NotificationList.tsx
+++ b/app/features/notifications/components/NotificationList.tsx
@@ -4,7 +4,6 @@ import { Link } from "react-router";
import { Image } from "~/components/Image";
import type { LoaderNotification } from "~/components/layout/NotificationPopover";
import {
- mapMetaForTranslation,
notificationLink,
notificationNavIcon,
} from "~/features/notifications/notifications-utils";
@@ -23,7 +22,7 @@ export function NotificationItem({
notification: LoaderNotification;
onClose?: () => void;
}) {
- const { t, i18n } = useTranslation(["common"]);
+ const { t } = useTranslation(["common"]);
return (
: null}
- {t(
- `common:notifications.text.${notification.type}`,
- mapMetaForTranslation(notification, i18n.language),
- )}
+ {t(`common:notifications.text.${notification.type}`, notification.meta)}
{formatDistance(
diff --git a/app/features/notifications/core/notify.server.test.ts b/app/features/notifications/core/notify.server.test.ts
index 3cb31ac2a..3e0126f52 100644
--- a/app/features/notifications/core/notify.server.test.ts
+++ b/app/features/notifications/core/notify.server.test.ts
@@ -139,7 +139,7 @@ describe("notify()", () => {
userIds: [10, 11],
notification: {
type: "SCRIM_SCHEDULED",
- meta: { id: 1, at: 123 },
+ meta: { id: 1, opponentTeamName: "Alpha" },
},
});
@@ -147,7 +147,7 @@ describe("notify()", () => {
userIds: [10, 11],
notification: {
type: "SCRIM_CANCELED",
- meta: { id: 1, at: 123 },
+ meta: { id: 1, opponentTeamName: "Alpha" },
},
});
@@ -346,7 +346,7 @@ describe("notify() - web push notifications", () => {
expect(mockSendNotification).not.toHaveBeenCalled();
});
- test("formats timestamp for scrim notifications", async () => {
+ test("includes opponent team name for scrim notifications", async () => {
const mockSubscription = {
endpoint: "https://fcm.googleapis.com/fcm/send/test",
keys: {
@@ -367,13 +367,11 @@ describe("notify() - web push notifications", () => {
mockWebPushEnabled.value = true;
- const testTimestamp = new Date("2024-01-15T15:30:00Z").getTime();
-
await notify({
userIds: [1],
notification: {
type: "SCRIM_SCHEDULED",
- meta: { id: 1, at: testTimestamp },
+ meta: { id: 1, opponentTeamName: "Sendou's pickup" },
},
});
@@ -383,8 +381,6 @@ describe("notify() - web push notifications", () => {
const payload = JSON.parse(callArgs);
expect(payload.title).toBe("Scrim Scheduled");
- expect(payload.body).toMatch(
- /New scrim scheduled at \d+\/\d+, \d+:\d+ (AM|PM)/,
- );
+ expect(payload.body).toBe("New scrim scheduled vs. Sendou's pickup");
});
});
diff --git a/app/features/notifications/core/notify.server.ts b/app/features/notifications/core/notify.server.ts
index 1f97022d7..b1e7b13e5 100644
--- a/app/features/notifications/core/notify.server.ts
+++ b/app/features/notifications/core/notify.server.ts
@@ -7,10 +7,7 @@ import { i18next } from "../../../modules/i18n/i18next.server";
import { logger } from "../../../utils/logger";
import * as NotificationRepository from "../NotificationRepository.server";
import type { Notification } from "../notifications-types";
-import {
- mapMetaForTranslation,
- notificationLink,
-} from "../notifications-utils";
+import { notificationLink } from "../notifications-utils";
import webPush, { webPushEnabled } from "./webPush.server";
const NOTIFICATION_URGENCY: Record = {
@@ -168,10 +165,12 @@ function pushNotificationOptions(
): Parameters[1] & {
title: string;
} {
- const meta = mapMetaForTranslation(notification, "en-US");
return {
title: t(`common:notifications.title.${notification.type}`),
- body: t(`common:notifications.text.${notification.type}`, meta),
+ body: t(
+ `common:notifications.text.${notification.type}`,
+ notification.meta,
+ ),
icon: notification.pictureUrl ?? "/static-assets/img/app-icon.png",
data: { url: notificationLink(notification) },
};
diff --git a/app/features/notifications/notifications-types.ts b/app/features/notifications/notifications-types.ts
index 4519d9e1e..f213d2a1e 100644
--- a/app/features/notifications/notifications-types.ts
+++ b/app/features/notifications/notifications-types.ts
@@ -62,9 +62,15 @@ export type Notification =
>
| NotificationItem<"SEASON_STARTED", { seasonNth: number }>
| NotificationItem<"SCRIM_NEW_REQUEST", { fromUsername: string }>
- | NotificationItem<"SCRIM_SCHEDULED", { id: number; at: number }>
- | NotificationItem<"SCRIM_CANCELED", { id: number; at: number }>
- | NotificationItem<"SCRIM_STARTING_SOON", { id: number; at: number }>
+ | NotificationItem<
+ "SCRIM_SCHEDULED",
+ { id: number; opponentTeamName: string }
+ >
+ | NotificationItem<"SCRIM_CANCELED", { id: number; opponentTeamName: string }>
+ | NotificationItem<
+ "SCRIM_STARTING_SOON",
+ { id: number; opponentTeamName: string }
+ >
| NotificationItem<"COMMISSIONS_CLOSED", { discordId: string }>
| NotificationItem<"FRIEND_REQUEST_RECEIVED", { senderUsername: string }>
| NotificationItem<
diff --git a/app/features/notifications/notifications-utils.ts b/app/features/notifications/notifications-utils.ts
index be53272be..642cb87a6 100644
--- a/app/features/notifications/notifications-utils.ts
+++ b/app/features/notifications/notifications-utils.ts
@@ -107,27 +107,3 @@ export const notificationLink = (notification: Notification) => {
assertUnreachable(notification);
}
};
-
-/** Takes the `meta` object of a notification and transforms it (if needed) to show the translated string to user */
-export const mapMetaForTranslation = (
- notification: Notification,
- language: string,
-) => {
- if (
- notification.type === "SCRIM_SCHEDULED" ||
- notification.type === "SCRIM_CANCELED" ||
- notification.type === "SCRIM_STARTING_SOON"
- ) {
- return {
- ...notification.meta,
- timeString: new Date(notification.meta.at).toLocaleString(language, {
- day: "numeric",
- month: "numeric",
- hour: "numeric",
- minute: "numeric",
- }),
- };
- }
-
- return notification.meta;
-};
diff --git a/app/features/scrims/actions/scrims.$id.server.ts b/app/features/scrims/actions/scrims.$id.server.ts
index aa45f25c7..d78e2c1c2 100644
--- a/app/features/scrims/actions/scrims.$id.server.ts
+++ b/app/features/scrims/actions/scrims.$id.server.ts
@@ -8,10 +8,7 @@ import {
parseRequestPayload,
} from "~/utils/remix.server";
import { idObject } from "~/utils/zod";
-import {
- databaseTimestampToDate,
- databaseTimestampToJavascriptTimestamp,
-} from "../../../utils/dates";
+import { databaseTimestampToDate } from "../../../utils/dates";
import { errorToast } from "../../../utils/remix.server";
import { requireUser } from "../../auth/core/user.server";
import * as Scrim from "../core/Scrim";
@@ -42,17 +39,29 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
reason: data.reason,
});
- notify({
- userIds: Scrim.participantIdsListFromAccepted(post),
- defaultSeenUserIds: [user.id],
- notification: {
- type: "SCRIM_CANCELED",
- meta: {
- id: post.id,
- at: databaseTimestampToJavascriptTimestamp(Scrim.getStartTime(post)),
+ const acceptedRequest = post.requests.find((r) => r.isAccepted);
+ if (acceptedRequest) {
+ const postTeamName = Scrim.sideDisplayName(post);
+ const requestTeamName = Scrim.sideDisplayName(acceptedRequest);
+
+ notify({
+ userIds: post.users.map((m) => m.id),
+ defaultSeenUserIds: [user.id],
+ notification: {
+ type: "SCRIM_CANCELED",
+ meta: { id: post.id, opponentTeamName: requestTeamName },
},
- },
- });
+ });
+
+ notify({
+ userIds: acceptedRequest.users.map((m) => m.id),
+ defaultSeenUserIds: [user.id],
+ notification: {
+ type: "SCRIM_CANCELED",
+ meta: { id: post.id, opponentTeamName: postTeamName },
+ },
+ });
+ }
return null;
};
diff --git a/app/features/scrims/actions/scrims.server.ts b/app/features/scrims/actions/scrims.server.ts
index 260bf369b..e1a29dff1 100644
--- a/app/features/scrims/actions/scrims.server.ts
+++ b/app/features/scrims/actions/scrims.server.ts
@@ -11,7 +11,6 @@ import * as UserRepository from "~/features/user-page/UserRepository.server";
import { requirePermission } from "~/modules/permissions/guards.server";
import {
databaseTimestampToDate,
- databaseTimestampToJavascriptTimestamp,
dateToDatabaseTimestamp,
} from "~/utils/dates";
import { ConcurrentModificationError } from "~/utils/errors";
@@ -157,18 +156,24 @@ export const action = async ({ request }: ActionFunctionArgs) => {
});
}
+ const postTeamName = Scrim.sideDisplayName(post);
+ const requestTeamName = Scrim.sideDisplayName(request);
+
notify({
- userIds: [
- ...post.users.map((m) => m.id),
- ...request.users.map((m) => m.id),
- ],
+ userIds: post.users.map((m) => m.id),
defaultSeenUserIds: [user.id],
notification: {
type: "SCRIM_SCHEDULED",
- meta: {
- id: post.id,
- at: databaseTimestampToJavascriptTimestamp(request.at ?? post.at),
- },
+ meta: { id: post.id, opponentTeamName: requestTeamName },
+ },
+ });
+
+ notify({
+ userIds: request.users.map((m) => m.id),
+ defaultSeenUserIds: [user.id],
+ notification: {
+ type: "SCRIM_SCHEDULED",
+ meta: { id: post.id, opponentTeamName: postTeamName },
},
});
diff --git a/app/features/scrims/core/Scrim.test.ts b/app/features/scrims/core/Scrim.test.ts
index afdf51f9f..602a96b48 100644
--- a/app/features/scrims/core/Scrim.test.ts
+++ b/app/features/scrims/core/Scrim.test.ts
@@ -1,7 +1,11 @@
import { describe, expect, it } from "vitest";
import { databaseTimestampNow, dateToDatabaseTimestamp } from "~/utils/dates";
import type { ScrimFilters, ScrimPost } from "../scrims-types";
-import { applyFilters, participantIdsListFromAccepted } from "./Scrim";
+import {
+ applyFilters,
+ participantIdsListFromAccepted,
+ sideDisplayName,
+} from "./Scrim";
type MockUser = { id: number };
type MockRequest = { isAccepted: boolean; users: MockUser[] };
@@ -78,6 +82,27 @@ describe("participantIdsListFromAccepted", () => {
});
});
+describe("sideDisplayName", () => {
+ it("returns the team name when team is set", () => {
+ const result = sideDisplayName({
+ team: { name: "Team Olive" },
+ users: [{ username: "sendou", isOwner: true }],
+ });
+ expect(result).toBe("Team Olive");
+ });
+
+ it("falls back to {owner}'s pickup when team is null", () => {
+ const result = sideDisplayName({
+ team: null,
+ users: [
+ { username: "alice", isOwner: false },
+ { username: "sendou", isOwner: true },
+ ],
+ });
+ expect(result).toBe("sendou's pickup");
+ });
+});
+
describe("applyFilters", () => {
function createPostForFilters(
at: Date,
diff --git a/app/features/scrims/core/Scrim.ts b/app/features/scrims/core/Scrim.ts
index e5ca8f842..1a45a425d 100644
--- a/app/features/scrims/core/Scrim.ts
+++ b/app/features/scrims/core/Scrim.ts
@@ -50,6 +50,19 @@ export function getStartTime(post: ScrimPost): number {
return acceptedRequest?.at ?? post.at;
}
+/**
+ * Returns a display name for a scrim side: the team name when set,
+ * otherwise "{ownerUsername}'s pickup".
+ */
+export function sideDisplayName(side: {
+ team: { name: string } | null;
+ users: Array<{ username: string; isOwner: boolean }>;
+}): string {
+ if (side.team) return side.team.name;
+ const owner = side.users.find((u) => u.isOwner) ?? side.users[0];
+ return `${owner.username}'s pickup`;
+}
+
export function applyFilters(post: ScrimPost, filters: ScrimFilters): boolean {
const hasMinFilter = filters.divs?.min !== null;
const hasMaxFilter = filters.divs?.max !== null;
diff --git a/app/routines/notifyScrimStartingSoon.ts b/app/routines/notifyScrimStartingSoon.ts
index 436b490ec..8b72c3f25 100644
--- a/app/routines/notifyScrimStartingSoon.ts
+++ b/app/routines/notifyScrimStartingSoon.ts
@@ -2,7 +2,6 @@ import { add, sub } from "date-fns";
import { notify } from "../features/notifications/core/notify.server";
import * as Scrim from "../features/scrims/core/Scrim";
import * as ScrimPostRepository from "../features/scrims/ScrimPostRepository.server";
-import { databaseTimestampToJavascriptTimestamp } from "../utils/dates";
import { logger } from "../utils/logger";
import { Routine } from "./routine.server";
@@ -19,23 +18,30 @@ export const NotifyScrimStartingSoonRoutine = new Routine({
});
for (const scrim of scrims) {
- const participantIds = Scrim.participantIdsListFromAccepted(scrim);
+ const acceptedRequest = scrim.requests.find((r) => r.isAccepted);
+ if (!acceptedRequest) continue;
+
+ const postTeamName = Scrim.sideDisplayName(scrim);
+ const requestTeamName = Scrim.sideDisplayName(acceptedRequest);
logger.info(
- `Notifying scrim starting soon for scrim ${scrim.id} with ${participantIds.length} participants`,
+ `Notifying scrim starting soon for scrim ${scrim.id} with ${scrim.users.length + acceptedRequest.users.length} participants`,
);
await notify({
notification: {
type: "SCRIM_STARTING_SOON",
- meta: {
- id: scrim.id,
- at: databaseTimestampToJavascriptTimestamp(
- Scrim.getStartTime(scrim),
- ),
- },
+ meta: { id: scrim.id, opponentTeamName: requestTeamName },
},
- userIds: participantIds,
+ userIds: scrim.users.map((u) => u.id),
+ });
+
+ await notify({
+ notification: {
+ type: "SCRIM_STARTING_SOON",
+ meta: { id: scrim.id, opponentTeamName: postTeamName },
+ },
+ userIds: acceptedRequest.users.map((u) => u.id),
});
}
},
diff --git a/locales/en/common.json b/locales/en/common.json
index aec67cec0..7cecf893b 100644
--- a/locales/en/common.json
+++ b/locales/en/common.json
@@ -79,11 +79,11 @@
"notifications.title.SCRIM_NEW_REQUEST": "New Scrim Request",
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} requested a scrim",
"notifications.title.SCRIM_SCHEDULED": "Scrim Scheduled",
- "notifications.text.SCRIM_SCHEDULED": "New scrim scheduled at {{timeString}}",
+ "notifications.text.SCRIM_SCHEDULED": "New scrim scheduled vs. {{opponentTeamName}}",
"notifications.title.SCRIM_CANCELED": "Scrim Canceled",
- "notifications.text.SCRIM_CANCELED": "The scrim at {{timeString}} was canceled",
+ "notifications.text.SCRIM_CANCELED": "The scrim vs. {{opponentTeamName}} was canceled",
"notifications.title.SCRIM_STARTING_SOON": "Scrim Starting Soon",
- "notifications.text.SCRIM_STARTING_SOON": "Your scrim at {{timeString}} is starting soon",
+ "notifications.text.SCRIM_STARTING_SOON": "Your scrim vs. {{opponentTeamName}} is starting soon",
"notifications.title.COMMISSIONS_CLOSED": "Commissions Closed",
"notifications.text.COMMISSIONS_CLOSED": "If your commissions are still open, please re-enable them",
"notifications.title.FRIEND_REQUEST_RECEIVED": "Friend Request",
diff --git a/locales/es-ES/common.json b/locales/es-ES/common.json
index 7963639bf..89999e731 100644
--- a/locales/es-ES/common.json
+++ b/locales/es-ES/common.json
@@ -79,11 +79,11 @@
"notifications.title.SCRIM_NEW_REQUEST": "Nueva Solicitud de Scrim",
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} ha solicitado un scrim",
"notifications.title.SCRIM_SCHEDULED": "Scrim Programado",
- "notifications.text.SCRIM_SCHEDULED": "Nuevo scrim programado para las {{timeString}}",
+ "notifications.text.SCRIM_SCHEDULED": "",
"notifications.title.SCRIM_CANCELED": "Scrim Cancelado",
- "notifications.text.SCRIM_CANCELED": "El scrim de las {{timeString}} fue cancelado",
+ "notifications.text.SCRIM_CANCELED": "",
"notifications.title.SCRIM_STARTING_SOON": "El scrim empieza pronto",
- "notifications.text.SCRIM_STARTING_SOON": "Tu scrim de las {{timeString}} empieza pronto",
+ "notifications.text.SCRIM_STARTING_SOON": "",
"notifications.title.COMMISSIONS_CLOSED": "Comisiones Cerradas",
"notifications.text.COMMISSIONS_CLOSED": "Si tus comisiones siguen abiertas, por favor vuelve a activarlas",
"notifications.title.FRIEND_REQUEST_RECEIVED": "",
diff --git a/locales/fr-EU/common.json b/locales/fr-EU/common.json
index dc9fb372c..ff4ceef2c 100644
--- a/locales/fr-EU/common.json
+++ b/locales/fr-EU/common.json
@@ -79,7 +79,7 @@
"notifications.title.SCRIM_NEW_REQUEST": "Nouvelle Demande De Scrim",
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} vous demande de scrim",
"notifications.title.SCRIM_SCHEDULED": "Scrim Programmé",
- "notifications.text.SCRIM_SCHEDULED": "Nouveau scrim programmé à {{timeString}}",
+ "notifications.text.SCRIM_SCHEDULED": "",
"notifications.title.SCRIM_CANCELED": "",
"notifications.text.SCRIM_CANCELED": "",
"notifications.title.SCRIM_STARTING_SOON": "",
diff --git a/locales/ru/common.json b/locales/ru/common.json
index 2f2d22a5a..77246069f 100644
--- a/locales/ru/common.json
+++ b/locales/ru/common.json
@@ -79,7 +79,7 @@
"notifications.title.SCRIM_NEW_REQUEST": "Новый Скрим Запрос",
"notifications.text.SCRIM_NEW_REQUEST": "{{fromUsername}} запросил скрим",
"notifications.title.SCRIM_SCHEDULED": "Скрим Запланирован",
- "notifications.text.SCRIM_SCHEDULED": "Новый скрим запланирован на {{timeString}}",
+ "notifications.text.SCRIM_SCHEDULED": "",
"notifications.title.SCRIM_CANCELED": "",
"notifications.text.SCRIM_CANCELED": "",
"notifications.title.SCRIM_STARTING_SOON": "",