Refactor looking loader

This commit is contained in:
Kalle
2022-02-23 00:09:37 +02:00
parent ec8437583b
commit 9faef3aed1
3 changed files with 138 additions and 107 deletions

View File

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

View File

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

View File

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