mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-13 22:56:05 -05:00
Look again with same group after reporting score
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { LfgGroupType, Prisma } from "@prisma/client";
|
||||
import type { LfgGroupStatus, LfgGroupType, Prisma } from "@prisma/client";
|
||||
import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
|
||||
import { generateMapListForLfgMatch } from "~/core/play/mapList";
|
||||
import { db } from "~/utils/db.server";
|
||||
@@ -29,6 +29,25 @@ export function create({
|
||||
});
|
||||
}
|
||||
|
||||
export function createPrefilled({
|
||||
ranked,
|
||||
members,
|
||||
}: {
|
||||
ranked: boolean | null;
|
||||
members: { memberId: string; captain: boolean }[];
|
||||
}) {
|
||||
return db.lfgGroup.create({
|
||||
data: {
|
||||
type: "VERSUS",
|
||||
ranked,
|
||||
status: "PRE_ADD",
|
||||
members: {
|
||||
createMany: { data: members },
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function like({
|
||||
likerId,
|
||||
targetId,
|
||||
@@ -229,9 +248,25 @@ export function findActiveByMember(user: { id: string }) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function activeUserIds() {
|
||||
const rows = (await db.$queryRaw`
|
||||
SELECT "LfgGroupMember"."memberId", "LfgGroup".status
|
||||
FROM "LfgGroupMember"
|
||||
JOIN "LfgGroup" ON ("LfgGroupMember"."groupId" = "LfgGroup".id)
|
||||
WHERE "LfgGroup".status != 'INACTIVE';`) as {
|
||||
memberId: string;
|
||||
status: LfgGroupStatus;
|
||||
}[];
|
||||
|
||||
return new Map<string, LfgGroupStatus>(
|
||||
rows.map((r) => [r.memberId, r.status])
|
||||
);
|
||||
}
|
||||
|
||||
export type FindLookingAndOwnActive = Prisma.PromiseReturnType<
|
||||
typeof findLookingAndOwnActive
|
||||
>["groups"];
|
||||
// TODO: refactor -> true by default
|
||||
export async function findLookingAndOwnActive(
|
||||
userId?: string,
|
||||
showPreAddMatch = false
|
||||
|
||||
@@ -7,6 +7,7 @@ export function findById(id: string) {
|
||||
return db.lfgGroupMatch.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
createdAt: true,
|
||||
stages: {
|
||||
select: {
|
||||
stage: {
|
||||
|
||||
@@ -39,7 +39,11 @@ import {
|
||||
UserLean,
|
||||
validate,
|
||||
} from "~/utils";
|
||||
import { oldSendouInkUserProfile } from "~/utils/urls";
|
||||
import {
|
||||
oldSendouInkUserProfile,
|
||||
sendouQAddPlayersPage,
|
||||
sendouQFrontPage,
|
||||
} from "~/utils/urls";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
@@ -77,10 +81,16 @@ const matchActionSchema = z.union([
|
||||
.max(LFG_AMOUNT_OF_STAGES_TO_GENERATE)
|
||||
),
|
||||
}),
|
||||
z.object({
|
||||
_action: z.literal("PLAY_AGAIN_SAME_GROUP"),
|
||||
}),
|
||||
z.object({
|
||||
_action: z.literal("PLAY_AGAIN_DIFFERENT_GROUP"),
|
||||
}),
|
||||
]);
|
||||
|
||||
type ActionData = {
|
||||
error?: "DIFFERENT_SCORE";
|
||||
error?: "DIFFERENT_SCORE" | "ALREADY_IN_GROUP";
|
||||
ok?: z.infer<typeof matchActionSchema>["_action"];
|
||||
};
|
||||
|
||||
@@ -140,6 +150,40 @@ export const action: ActionFunction = async ({
|
||||
await LFGGroup.setInactive(ownGroup.id);
|
||||
return redirect("/play");
|
||||
}
|
||||
case "PLAY_AGAIN_SAME_GROUP": {
|
||||
const ids = await LFGGroup.activeUserIds();
|
||||
for (const member of ownGroup.members) {
|
||||
if (ids.has(member.memberId)) {
|
||||
return { error: "ALREADY_IN_GROUP" };
|
||||
}
|
||||
}
|
||||
|
||||
await LFGGroup.createPrefilled({
|
||||
ranked: ownGroup.ranked,
|
||||
members: ownGroup.members.map((m) => ({
|
||||
memberId: m.memberId,
|
||||
captain: m.captain,
|
||||
})),
|
||||
});
|
||||
return redirect(sendouQAddPlayersPage());
|
||||
}
|
||||
case "PLAY_AGAIN_DIFFERENT_GROUP": {
|
||||
const ids = await LFGGroup.activeUserIds();
|
||||
if (ids.has(user.id)) {
|
||||
return redirect(sendouQFrontPage());
|
||||
}
|
||||
|
||||
await LFGGroup.createPrefilled({
|
||||
ranked: ownGroup.ranked,
|
||||
members: [
|
||||
{
|
||||
memberId: user.id,
|
||||
captain: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
return redirect(sendouQAddPlayersPage());
|
||||
}
|
||||
default: {
|
||||
const exhaustive: never = data;
|
||||
throw new Response(`Unknown action: ${JSON.stringify(exhaustive)}`, {
|
||||
@@ -156,6 +200,7 @@ interface LFGMatchLoaderData {
|
||||
isCaptain: boolean;
|
||||
isOwnMatch: boolean;
|
||||
isRanked: boolean;
|
||||
createdAtTimestamp: number;
|
||||
groups: { id: string; members: UserLean[] }[];
|
||||
mapList: {
|
||||
name: string;
|
||||
@@ -224,6 +269,7 @@ export const loader: LoaderFunction = async ({ params, context }) => {
|
||||
isOwnMatch,
|
||||
groups,
|
||||
scores,
|
||||
createdAtTimestamp: new Date(match.createdAt).getTime(),
|
||||
mapList: match.stages
|
||||
.map(({ stage, winnerGroupId }) => {
|
||||
const winner = () => {
|
||||
@@ -246,6 +292,18 @@ export default function LFGMatchPage() {
|
||||
const transition = useTransition();
|
||||
const actionData = useActionData<ActionData>();
|
||||
|
||||
const showPlayAgainSection = () => {
|
||||
if (!data.isCaptain) return false;
|
||||
if (!data.scores) return false;
|
||||
|
||||
const oneHourAgo = new Date(
|
||||
new Date().getTime() - 60 * 60 * 1_000
|
||||
).getTime();
|
||||
if (data.createdAtTimestamp < oneHourAgo) return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
{actionData?.error === "DIFFERENT_SCORE" && (
|
||||
@@ -300,6 +358,38 @@ export default function LFGMatchPage() {
|
||||
<code>#match-meetup</code> channel.
|
||||
</div>
|
||||
)}
|
||||
{showPlayAgainSection() && (
|
||||
<Form method="post">
|
||||
<div className="play-match__waves-section play-match__play-again-container">
|
||||
{actionData?.error === "ALREADY_IN_GROUP" ? (
|
||||
<span className="text-color-error">
|
||||
Some group member is already in a new group
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
Want to play again?
|
||||
<Button
|
||||
name="_action"
|
||||
value="PLAY_AGAIN_SAME_GROUP"
|
||||
tiny
|
||||
className="play-match__play-again-button"
|
||||
>
|
||||
Same group
|
||||
</Button>
|
||||
<Button
|
||||
name="_action"
|
||||
value="PLAY_AGAIN_DIFFERENT_GROUP"
|
||||
variant="outlined"
|
||||
tiny
|
||||
className="play-match__play-again-button"
|
||||
>
|
||||
New group
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
{data.scores && (
|
||||
<div className="play-match__played-map-list">
|
||||
{data.mapList.map((stage) => {
|
||||
|
||||
@@ -529,6 +529,10 @@ hr {
|
||||
margin-block: var(--s-4);
|
||||
}
|
||||
|
||||
.text-color-error {
|
||||
color: var(--theme-error);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -100,6 +100,23 @@
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.play-match__play-again-container {
|
||||
display: flex;
|
||||
max-width: calc(28rem + var(--s-4));
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--s-2);
|
||||
margin-block-start: var(--s-4);
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.play-match__play-again-button {
|
||||
width: min-content;
|
||||
grid-column: 1 / 3;
|
||||
place-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.play-match__score.winner {
|
||||
background-color: var(--theme-transparent);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user