Look again with same group after reporting score

This commit is contained in:
Kalle
2022-03-04 20:59:37 +02:00
parent bf1a61e6b7
commit 2e93148746
5 changed files with 150 additions and 3 deletions

View File

@@ -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

View File

@@ -7,6 +7,7 @@ export function findById(id: string) {
return db.lfgGroupMatch.findUnique({
where: { id },
select: {
createdAt: true,
stages: {
select: {
stage: {

View File

@@ -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) => {

View File

@@ -529,6 +529,10 @@ hr {
margin-block: var(--s-4);
}
.text-color-error {
color: var(--theme-error);
}
.text-center {
text-align: center;
}

View File

@@ -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);
}