mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-22 02:55:52 -05:00
Refactor looking loader
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import invariant from "tiny-invariant";
|
||||
import { LFG_GROUP_INACTIVE_MINUTES } from "~/constants";
|
||||
import type { UniteGroupsArgs } from "~/models/LFGGroup.server";
|
||||
import { LFG_GROUP_FULL_SIZE, LFG_GROUP_INACTIVE_MINUTES } from "~/constants";
|
||||
import * as LFGGroup from "~/models/LFGGroup.server";
|
||||
import { LookingLoaderData } from "~/routes/play/looking";
|
||||
import { Unpacked } from "~/utils";
|
||||
import {
|
||||
skillToMMR,
|
||||
teamHasSkill,
|
||||
teamSkillToApproximateMMR,
|
||||
} from "../mmr/utils";
|
||||
import { canUniteWithGroup } from "./validators";
|
||||
|
||||
export interface UniteGroupInfoArg {
|
||||
id: string;
|
||||
@@ -9,7 +17,7 @@ export interface UniteGroupInfoArg {
|
||||
export function uniteGroupInfo(
|
||||
groupA: UniteGroupInfoArg,
|
||||
groupB: UniteGroupInfoArg
|
||||
): UniteGroupsArgs {
|
||||
): LFGGroup.UniteGroupsArgs {
|
||||
const survivingGroupId =
|
||||
groupA.memberCount > groupB.memberCount ? groupA.id : groupB.id;
|
||||
const otherGroupId = survivingGroupId === groupA.id ? groupB.id : groupA.id;
|
||||
@@ -99,3 +107,86 @@ export function groupExpiredDates(): Record<
|
||||
export function groupWillBeInactiveAt(timestamp: number) {
|
||||
return new Date(timestamp + 60_000 * LFG_GROUP_INACTIVE_MINUTES);
|
||||
}
|
||||
|
||||
export function otherGroupsForResponse({
|
||||
groups,
|
||||
likes,
|
||||
lookingForMatch,
|
||||
ownGroup,
|
||||
}: {
|
||||
groups: LFGGroup.FindLooking;
|
||||
likes: {
|
||||
given: Set<string>;
|
||||
received: Set<string>;
|
||||
};
|
||||
lookingForMatch: boolean;
|
||||
ownGroup: Unpacked<LFGGroup.FindLooking>;
|
||||
}) {
|
||||
const { EXPIRED: expiredDate } = groupExpiredDates();
|
||||
|
||||
return groups
|
||||
.filter(
|
||||
(group) =>
|
||||
(lookingForMatch && group.members.length === LFG_GROUP_FULL_SIZE) ||
|
||||
canUniteWithGroup({
|
||||
ownGroupType: ownGroup.type,
|
||||
ownGroupSize: ownGroup.members.length,
|
||||
otherGroupSize: group.members.length,
|
||||
})
|
||||
)
|
||||
.filter((group) => group.id !== ownGroup.id)
|
||||
.filter((group) => group.lastActionAt.getTime() > expiredDate.getTime())
|
||||
.map((group) => {
|
||||
const ranked = () => {
|
||||
if (lookingForMatch && !ownGroup.ranked) return false;
|
||||
|
||||
return group.ranked ?? undefined;
|
||||
};
|
||||
return {
|
||||
id: group.id,
|
||||
// When looking for a match ranked groups are censored
|
||||
// and instead we only reveal their approximate skill level
|
||||
members:
|
||||
ownGroup.ranked && group.ranked && lookingForMatch
|
||||
? undefined
|
||||
: group.members.map((m) => {
|
||||
const { skill, ...rest } = m.user;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
MMR: skillToMMR(skill),
|
||||
};
|
||||
}),
|
||||
ranked: ranked(),
|
||||
teamMMR:
|
||||
lookingForMatch && group.ranked && teamHasSkill(group.members)
|
||||
? {
|
||||
exact: false,
|
||||
value: teamSkillToApproximateMMR(group.members),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
})
|
||||
.reduce(
|
||||
(
|
||||
acc: Omit<
|
||||
LookingLoaderData,
|
||||
"ownGroup" | "type" | "isCaptain" | "lastActionAtTimestamp"
|
||||
>,
|
||||
group
|
||||
) => {
|
||||
// likesReceived first so that if both received like and
|
||||
// given like then handle this edge case by just displaying the
|
||||
// group as waiting like back
|
||||
if (likes.received.has(group.id)) {
|
||||
acc.likerGroups.push(group);
|
||||
} else if (likes.given.has(group.id)) {
|
||||
acc.likedGroups.push(group);
|
||||
} else {
|
||||
acc.neutralGroups.push(group);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ likedGroups: [], neutralGroups: [], likerGroups: [] }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LfgGroupType } from "@prisma/client";
|
||||
import type { LfgGroupType, Prisma } from "@prisma/client";
|
||||
import { generateMapListForLfgMatch } from "~/core/play/mapList";
|
||||
import { db } from "~/utils/db.server";
|
||||
|
||||
@@ -180,6 +180,7 @@ export function findActiveByMember(user: { id: string }) {
|
||||
});
|
||||
}
|
||||
|
||||
export type FindLooking = Prisma.PromiseReturnType<typeof findLooking>;
|
||||
export function findLooking() {
|
||||
return db.lfgGroup.findMany({
|
||||
where: {
|
||||
|
||||
@@ -10,18 +10,18 @@ import {
|
||||
} from "remix";
|
||||
import invariant from "tiny-invariant";
|
||||
import { z } from "zod";
|
||||
import { FinishedGroup } from "~/components/play/FinishedGroup";
|
||||
import { GroupCard } from "~/components/play/GroupCard";
|
||||
import { LookingInfoText } from "~/components/play/LookingInfoText";
|
||||
import { FinishedGroup } from "~/components/play/FinishedGroup";
|
||||
import { Tab } from "~/components/Tab";
|
||||
import { LFG_GROUP_FULL_SIZE } from "~/constants";
|
||||
import {
|
||||
skillToMMR,
|
||||
teamHasSkill,
|
||||
teamSkillToApproximateMMR,
|
||||
teamSkillToExactMMR,
|
||||
} from "~/core/mmr/utils";
|
||||
import { groupExpiredDates, uniteGroupInfo } from "~/core/play/utils";
|
||||
import { addInfoFromOldSendouInk } from "~/core/play/playerInfos/playerInfos.server";
|
||||
import { otherGroupsForResponse, uniteGroupInfo } from "~/core/play/utils";
|
||||
import { canUniteWithGroup, isGroupAdmin } from "~/core/play/validators";
|
||||
import { usePolling } from "~/hooks/common";
|
||||
import * as LFGGroup from "~/models/LFGGroup.server";
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
UserLean,
|
||||
validate,
|
||||
} from "~/utils";
|
||||
import { addInfoFromOldSendouInk } from "~/core/play/playerInfos/playerInfos.server";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
@@ -215,117 +214,57 @@ export const loader: LoaderFunction = async ({ context }) => {
|
||||
|
||||
const groupsOfType = groups.filter((g) => g.type === ownGroup.type);
|
||||
|
||||
const likesGiven = ownGroup.likedGroups.reduce(
|
||||
(acc, lg) => acc.add(lg.targetId),
|
||||
new Set<string>()
|
||||
);
|
||||
const likesReceived = ownGroup.likesReceived.reduce(
|
||||
(acc, lg) => acc.add(lg.likerId),
|
||||
new Set<string>()
|
||||
);
|
||||
|
||||
const isRanked = groupsOfType.some((g) => g.ranked);
|
||||
const ownGroupWithMembers = groupsOfType.find((g) => g.id === ownGroup.id);
|
||||
invariant(ownGroupWithMembers, "ownGroupWithMembers is undefined");
|
||||
const ownGroupForResponse: LookingLoaderDataGroup = {
|
||||
id: ownGroup.id,
|
||||
members: ownGroupWithMembers.members.map((m) => {
|
||||
const { skill, ...rest } = m.user;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
MMR: lookingForMatch && ownGroup.ranked ? undefined : skillToMMR(skill),
|
||||
};
|
||||
}),
|
||||
ranked: ownGroup.ranked ?? undefined,
|
||||
teamMMR:
|
||||
lookingForMatch && isRanked && teamHasSkill(ownGroupWithMembers.members)
|
||||
? {
|
||||
exact: true,
|
||||
value: teamSkillToExactMMR(ownGroupWithMembers.members),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const { EXPIRED: expiredDate } = groupExpiredDates();
|
||||
|
||||
return json<LookingLoaderData>(
|
||||
addInfoFromOldSendouInk(
|
||||
ownGroup.type === "VERSUS" ? "SOLO" : "LEAGUE",
|
||||
!lookingForMatch,
|
||||
{
|
||||
ownGroup: ownGroupForResponse,
|
||||
ownGroup: {
|
||||
id: ownGroup.id,
|
||||
members: ownGroupWithMembers.members.map((m) => {
|
||||
const { skill, ...rest } = m.user;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
MMR:
|
||||
lookingForMatch && ownGroup.ranked
|
||||
? undefined
|
||||
: skillToMMR(skill),
|
||||
};
|
||||
}),
|
||||
ranked: ownGroup.ranked ?? undefined,
|
||||
teamMMR:
|
||||
lookingForMatch &&
|
||||
isRanked &&
|
||||
teamHasSkill(ownGroupWithMembers.members)
|
||||
? {
|
||||
exact: true,
|
||||
value: teamSkillToExactMMR(ownGroupWithMembers.members),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
type: ownGroup.type,
|
||||
isCaptain: isGroupAdmin({ group: ownGroup, user }),
|
||||
lastActionAtTimestamp: ownGroup.lastActionAt.getTime(),
|
||||
...groupsOfType
|
||||
.filter(
|
||||
(group) =>
|
||||
(lookingForMatch &&
|
||||
group.members.length === LFG_GROUP_FULL_SIZE) ||
|
||||
canUniteWithGroup({
|
||||
ownGroupType: ownGroup.type,
|
||||
ownGroupSize: ownGroup.members.length,
|
||||
otherGroupSize: group.members.length,
|
||||
})
|
||||
)
|
||||
.filter((group) => group.id !== ownGroup.id)
|
||||
.filter(
|
||||
(group) => group.lastActionAt.getTime() > expiredDate.getTime()
|
||||
)
|
||||
.map((group) => {
|
||||
const ranked = () => {
|
||||
if (lookingForMatch && !ownGroup.ranked) return false;
|
||||
|
||||
return group.ranked ?? undefined;
|
||||
};
|
||||
return {
|
||||
id: group.id,
|
||||
// When looking for a match ranked groups are censored
|
||||
// and instead we only reveal their approximate skill level
|
||||
members:
|
||||
ownGroup.ranked && group.ranked && lookingForMatch
|
||||
? undefined
|
||||
: group.members.map((m) => {
|
||||
const { skill, ...rest } = m.user;
|
||||
|
||||
return {
|
||||
...rest,
|
||||
MMR: skillToMMR(skill),
|
||||
};
|
||||
}),
|
||||
ranked: ranked(),
|
||||
teamMMR:
|
||||
lookingForMatch && group.ranked && teamHasSkill(group.members)
|
||||
? {
|
||||
exact: false,
|
||||
value: teamSkillToApproximateMMR(group.members),
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
})
|
||||
.reduce(
|
||||
(
|
||||
acc: Omit<
|
||||
LookingLoaderData,
|
||||
"ownGroup" | "type" | "isCaptain" | "lastActionAtTimestamp"
|
||||
>,
|
||||
group
|
||||
) => {
|
||||
// likesReceived first so that if both received like and
|
||||
// given like then handle this edge case by just displaying the
|
||||
// group as waiting like back
|
||||
if (likesReceived.has(group.id)) {
|
||||
acc.likerGroups.push(group);
|
||||
} else if (likesGiven.has(group.id)) {
|
||||
acc.likedGroups.push(group);
|
||||
} else {
|
||||
acc.neutralGroups.push(group);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{ likedGroups: [], neutralGroups: [], likerGroups: [] }
|
||||
),
|
||||
...otherGroupsForResponse({
|
||||
groups,
|
||||
lookingForMatch,
|
||||
ownGroup,
|
||||
likes: {
|
||||
received: ownGroup.likesReceived.reduce(
|
||||
(acc, lg) => acc.add(lg.likerId),
|
||||
new Set<string>()
|
||||
),
|
||||
given: ownGroup.likedGroups.reduce(
|
||||
(acc, lg) => acc.add(lg.targetId),
|
||||
new Set<string>()
|
||||
),
|
||||
},
|
||||
}),
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user