mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-26 09:20:24 -05:00
Expiring chat codes
This commit is contained in:
parent
b9486c4f03
commit
1edb2809b5
|
|
@ -1,5 +1,52 @@
|
||||||
|
import { sub } from "date-fns";
|
||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
import { datePlaceholder, resolveDatePlaceholders } from "./chat-utils";
|
import {
|
||||||
|
chatAccessible,
|
||||||
|
datePlaceholder,
|
||||||
|
resolveDatePlaceholders,
|
||||||
|
} from "./chat-utils";
|
||||||
|
|
||||||
|
describe("chatCodeVisible", () => {
|
||||||
|
test("visible when within expiration window", () => {
|
||||||
|
const result = chatAccessible({
|
||||||
|
isStaff: false,
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("not visible when past expiration window", () => {
|
||||||
|
const result = chatAccessible({
|
||||||
|
isStaff: false,
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: sub(new Date(), { days: 3 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("staff gets 7 extra days", () => {
|
||||||
|
const result = chatAccessible({
|
||||||
|
isStaff: true,
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: sub(new Date(), { days: 5 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("staff extra days are not infinite", () => {
|
||||||
|
const result = chatAccessible({
|
||||||
|
isStaff: true,
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: sub(new Date(), { days: 10 }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe("datePlaceholder", () => {
|
describe("datePlaceholder", () => {
|
||||||
test("returns correctly formatted placeholder string", () => {
|
test("returns correctly formatted placeholder string", () => {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
|
import { differenceInDays } from "date-fns";
|
||||||
import type { ChatMessage } from "./chat-types";
|
import type { ChatMessage } from "./chat-types";
|
||||||
|
|
||||||
|
const STAFF_EXTRA_DAYS = 7;
|
||||||
|
|
||||||
|
export function chatAccessible(args: {
|
||||||
|
isStaff: boolean;
|
||||||
|
expiresAfterDays: number;
|
||||||
|
comparedTo: Date;
|
||||||
|
}): boolean {
|
||||||
|
const extraDays = args.isStaff ? STAFF_EXTRA_DAYS : 0;
|
||||||
|
return (
|
||||||
|
differenceInDays(new Date(), args.comparedTo) <=
|
||||||
|
args.expiresAfterDays + extraDays
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const DATE_PLACEHOLDER_PATTERN = /\{\{date:(\d+)\}\}/g;
|
const DATE_PLACEHOLDER_PATTERN = /\{\{date:(\d+)\}\}/g;
|
||||||
|
|
||||||
export function datePlaceholder(date: Date): string {
|
export function datePlaceholder(date: Date): string {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import type { ChatContextValue } from "./chat-provider-types";
|
import type { ChatContextValue } from "./chat-provider-types";
|
||||||
|
|
||||||
// xxx: think how chats should expire in relation to chatCode getting returned from loaders etc.
|
|
||||||
|
|
||||||
export const ChatContext = React.createContext<ChatContextValue | null>(null);
|
export const ChatContext = React.createContext<ChatContextValue | null>(null);
|
||||||
|
|
||||||
export function useChatContext(): ChatContextValue | null {
|
export function useChatContext(): ChatContextValue | null {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import type { LoaderFunctionArgs } from "react-router";
|
import type { LoaderFunctionArgs } from "react-router";
|
||||||
|
import { chatAccessible } from "~/features/chat/chat-utils";
|
||||||
import { tournamentDataCached } from "~/features/tournament-bracket/core/Tournament.server";
|
import { tournamentDataCached } from "~/features/tournament-bracket/core/Tournament.server";
|
||||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||||
|
import { databaseTimestampToDate } from "~/utils/dates";
|
||||||
import { notFoundIfFalsy } from "../../../utils/remix.server";
|
import { notFoundIfFalsy } from "../../../utils/remix.server";
|
||||||
import {
|
import {
|
||||||
type AuthenticatedUser,
|
type AuthenticatedUser,
|
||||||
|
|
@ -29,7 +31,12 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
return {
|
return {
|
||||||
post,
|
post,
|
||||||
chatCode:
|
chatCode:
|
||||||
user.roles.includes("STAFF") || participantIds.includes(user.id)
|
(user.roles.includes("STAFF") || participantIds.includes(user.id)) &&
|
||||||
|
chatAccessible({
|
||||||
|
isStaff: user.roles.includes("STAFF"),
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: databaseTimestampToDate(Scrim.getStartTime(post)),
|
||||||
|
})
|
||||||
? post.chatCode
|
? post.chatCode
|
||||||
: undefined,
|
: undefined,
|
||||||
anyUserPrefersNoScreen:
|
anyUserPrefersNoScreen:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
import type { LoaderFunctionArgs } from "react-router";
|
import type { LoaderFunctionArgs } from "react-router";
|
||||||
import { getUser } from "~/features/auth/core/user.server";
|
import { getUser } from "~/features/auth/core/user.server";
|
||||||
|
import { chatAccessible } from "~/features/chat/chat-utils";
|
||||||
import { SendouQ } from "~/features/sendouq/core/SendouQ.server";
|
import { SendouQ } from "~/features/sendouq/core/SendouQ.server";
|
||||||
import * as PrivateUserNoteRepository from "~/features/sendouq/PrivateUserNoteRepository.server";
|
import * as PrivateUserNoteRepository from "~/features/sendouq/PrivateUserNoteRepository.server";
|
||||||
import { reportedWeaponsToArrayOfArrays } from "~/features/sendouq-match/core/reported-weapons.server";
|
import { reportedWeaponsToArrayOfArrays } from "~/features/sendouq-match/core/reported-weapons.server";
|
||||||
import * as ReportedWeaponRepository from "~/features/sendouq-match/ReportedWeaponRepository.server";
|
import * as ReportedWeaponRepository from "~/features/sendouq-match/ReportedWeaponRepository.server";
|
||||||
import * as SQMatchRepository from "~/features/sendouq-match/SQMatchRepository.server";
|
import * as SQMatchRepository from "~/features/sendouq-match/SQMatchRepository.server";
|
||||||
|
import { databaseTimestampToDate } from "~/utils/dates";
|
||||||
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
|
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
|
||||||
import { qMatchPageParamsSchema } from "../q-match-schemas";
|
import { qMatchPageParamsSchema } from "../q-match-schemas";
|
||||||
|
|
||||||
|
|
@ -44,7 +46,13 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
: null,
|
: null,
|
||||||
rawReportedWeapons,
|
rawReportedWeapons,
|
||||||
chatCode:
|
chatCode:
|
||||||
user?.roles.includes("STAFF") || (user && matchUsers.includes(user.id))
|
(user?.roles.includes("STAFF") ||
|
||||||
|
(user && matchUsers.includes(user.id))) &&
|
||||||
|
chatAccessible({
|
||||||
|
isStaff: user?.roles.includes("STAFF") ?? false,
|
||||||
|
expiresAfterDays: 1,
|
||||||
|
comparedTo: databaseTimestampToDate(matchUnmapped.createdAt),
|
||||||
|
})
|
||||||
? match.chatCode
|
? match.chatCode
|
||||||
: null,
|
: null,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import cachified from "@epic-web/cachified";
|
||||||
import type { LoaderFunctionArgs } from "react-router";
|
import type { LoaderFunctionArgs } from "react-router";
|
||||||
import { getUser } from "~/features/auth/core/user.server";
|
import { getUser } from "~/features/auth/core/user.server";
|
||||||
import * as ChatSystemMessage from "~/features/chat/ChatSystemMessage.server";
|
import * as ChatSystemMessage from "~/features/chat/ChatSystemMessage.server";
|
||||||
|
import { chatAccessible } from "~/features/chat/chat-utils";
|
||||||
import * as TournamentRepository from "~/features/tournament/TournamentRepository.server";
|
import * as TournamentRepository from "~/features/tournament/TournamentRepository.server";
|
||||||
import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server";
|
import * as TournamentTeamRepository from "~/features/tournament/TournamentTeamRepository.server";
|
||||||
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
import * as UserRepository from "~/features/user-page/UserRepository.server";
|
||||||
|
|
@ -125,6 +126,18 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
tournament.isOrganizerOrStreamer(user) ||
|
tournament.isOrganizerOrStreamer(user) ||
|
||||||
match.players.some((p) => p.id === user?.id);
|
match.players.some((p) => p.id === user?.id);
|
||||||
|
|
||||||
|
const isStaff = user?.roles.includes("STAFF") ?? false;
|
||||||
|
const chatCodeExpired = tournament.ctx.isFinalized
|
||||||
|
? true
|
||||||
|
: !chatAccessible({
|
||||||
|
isStaff,
|
||||||
|
expiresAfterDays: 90,
|
||||||
|
comparedTo: tournament.ctx.startTime,
|
||||||
|
});
|
||||||
|
|
||||||
|
const visibleChatCode =
|
||||||
|
shouldSeeChat && !chatCodeExpired ? match.chatCode : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
match: shouldSeeChat ? match : { ...match, chatCode: undefined },
|
match: shouldSeeChat ? match : { ...match, chatCode: undefined },
|
||||||
results,
|
results,
|
||||||
|
|
@ -132,6 +145,6 @@ export const loader = async ({ params }: LoaderFunctionArgs) => {
|
||||||
matchIsOver,
|
matchIsOver,
|
||||||
endedEarly,
|
endedEarly,
|
||||||
noScreen,
|
noScreen,
|
||||||
chatCode: shouldSeeChat ? match.chatCode : undefined,
|
chatCode: visibleChatCode,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user