mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-05 20:56:13 -05:00
Merge remote main into css-rework
This commit is contained in:
commit
4c0ff39da2
31
app/features/api-public/routes/user.$identifier.ids.ts
Normal file
31
app/features/api-public/routes/user.$identifier.ids.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
import { json, type LoaderFunctionArgs } from "@remix-run/node";
|
||||
import { cors } from "remix-utils/cors";
|
||||
import { z } from "zod/v4";
|
||||
import { identifierToUserIdQuery } from "~/features/user-page/UserRepository.server";
|
||||
import { notFoundIfFalsy, parseParams } from "~/utils/remix.server";
|
||||
import { handleOptionsRequest } from "../api-public-utils.server";
|
||||
import type { GetUserIdsResponse } from "../schema";
|
||||
|
||||
const paramsSchema = z.object({
|
||||
identifier: z.string(),
|
||||
});
|
||||
|
||||
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
|
||||
await handleOptionsRequest(request);
|
||||
|
||||
const { identifier } = parseParams({ params, schema: paramsSchema });
|
||||
|
||||
const user = notFoundIfFalsy(
|
||||
await identifierToUserIdQuery(identifier)
|
||||
.select(["User.discordId", "User.customUrl"])
|
||||
.executeTakeFirst(),
|
||||
);
|
||||
|
||||
const result: GetUserIdsResponse = {
|
||||
id: user.id,
|
||||
discordId: user.discordId,
|
||||
customUrl: user.customUrl,
|
||||
};
|
||||
|
||||
return await cors(request, json(result));
|
||||
};
|
||||
|
|
@ -42,6 +42,20 @@ export interface GetUserResponse {
|
|||
currentRank: SeasonalRank | null;
|
||||
}
|
||||
|
||||
/** GET /api/user/{userId|discordId|customUrl}/ids */
|
||||
|
||||
export interface GetUserIdsResponse {
|
||||
id: number;
|
||||
/**
|
||||
* @example "79237403620945920"
|
||||
*/
|
||||
discordId: string;
|
||||
/**
|
||||
* @example "sendou"
|
||||
*/
|
||||
customUrl: string | null;
|
||||
}
|
||||
|
||||
/** GET /api/team/{teamId} */
|
||||
|
||||
export interface GetTeamResponse {
|
||||
|
|
|
|||
|
|
@ -955,6 +955,10 @@
|
|||
"displayName": "Rapid Rushdown",
|
||||
"authorDiscordId": "528851510222782474"
|
||||
},
|
||||
"runspeed": {
|
||||
"displayName": "Run Speed Up",
|
||||
"authorDiscordId": "528851510222782474"
|
||||
},
|
||||
"s50badge": {
|
||||
"displayName": "Anarchy Adventures",
|
||||
"authorDiscordId": "336683473714544641"
|
||||
|
|
|
|||
|
|
@ -261,6 +261,7 @@ describe("matchMapList()", () => {
|
|||
preferences: [{ userId: 2, preferences: emptyPreferences }],
|
||||
id: 2,
|
||||
},
|
||||
["SZ"],
|
||||
);
|
||||
|
||||
const szMaps = result.filter((m) => m.mode === "SZ");
|
||||
|
|
@ -317,6 +318,7 @@ describe("matchMapList()", () => {
|
|||
],
|
||||
id: 2,
|
||||
},
|
||||
["SZ"],
|
||||
);
|
||||
|
||||
const szMaps = result.filter((m) => m.mode === "SZ");
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ type WeightsMap = Map<string, number>;
|
|||
async function calculateMapWeights(
|
||||
groupOnePreferences: UserMapModePreferences[],
|
||||
groupTwoPreferences: UserMapModePreferences[],
|
||||
modesIncluded: ModeShort[],
|
||||
modesIncluded: readonly ModeShort[],
|
||||
): Promise<WeightsMap> {
|
||||
const teamOneVotes: WeightsMap = new Map();
|
||||
const teamTwoVotes: WeightsMap = new Map();
|
||||
|
|
@ -130,7 +130,7 @@ async function applyDefaultWeights(
|
|||
}
|
||||
|
||||
function countVotesForTeam(
|
||||
modesIncluded: ModeShort[],
|
||||
modesIncluded: readonly ModeShort[],
|
||||
preferences: UserMapModePreferences[],
|
||||
votesMap: WeightsMap,
|
||||
) {
|
||||
|
|
@ -160,23 +160,13 @@ export async function matchMapList(
|
|||
groupOne: {
|
||||
preferences: { userId: number; preferences: UserMapModePreferences }[];
|
||||
id: number;
|
||||
ignoreModePreferences?: boolean;
|
||||
},
|
||||
groupTwo: {
|
||||
preferences: { userId: number; preferences: UserMapModePreferences }[];
|
||||
id: number;
|
||||
ignoreModePreferences?: boolean;
|
||||
},
|
||||
modesIncluded: readonly ModeShort[],
|
||||
): Promise<TournamentMapListMap[]> {
|
||||
const modesIncluded = mapModePreferencesToModeList(
|
||||
groupOne.ignoreModePreferences
|
||||
? []
|
||||
: groupOne.preferences.map(({ preferences }) => preferences.modes),
|
||||
groupTwo.ignoreModePreferences
|
||||
? []
|
||||
: groupTwo.preferences.map(({ preferences }) => preferences.modes),
|
||||
);
|
||||
|
||||
const weights = await calculateMapWeights(
|
||||
groupOne.preferences.map((p) => p.preferences),
|
||||
groupTwo.preferences.map((p) => p.preferences),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { groupAfterMorph } from "../core/groups";
|
|||
import { refreshSendouQInstance, SendouQ } from "../core/SendouQ.server";
|
||||
import * as PrivateUserNoteRepository from "../PrivateUserNoteRepository.server";
|
||||
import { lookingSchema } from "../q-schemas.server";
|
||||
import { resolveFutureMatchModes } from "../q-utils";
|
||||
import { SendouQError } from "../q-utils.server";
|
||||
|
||||
// this function doesn't throw normally because we are assuming
|
||||
|
|
@ -137,36 +138,38 @@ export const action: ActionFunction = async ({ request }) => {
|
|||
|
||||
break;
|
||||
}
|
||||
case "MATCH_UP_RECHALLENGE":
|
||||
case "MATCH_UP": {
|
||||
if (!isGroupManager()) return null;
|
||||
|
||||
const ourGroup = SendouQ.findOwnGroup(user.id);
|
||||
const ownGroup = SendouQ.findOwnGroup(user.id);
|
||||
const theirGroup = SendouQ.findUncensoredGroupById(data.targetGroupId);
|
||||
if (!ourGroup || !theirGroup) return null;
|
||||
if (!ownGroup || !theirGroup) return null;
|
||||
|
||||
const ourGroupPreferences =
|
||||
await SQGroupRepository.mapModePreferencesByGroupId(ourGroup.id);
|
||||
const ownGroupPreferences =
|
||||
await SQGroupRepository.mapModePreferencesByGroupId(ownGroup.id);
|
||||
const theirGroupPreferences =
|
||||
await SQGroupRepository.mapModePreferencesByGroupId(theirGroup.id);
|
||||
|
||||
const modesIncluded = resolveFutureMatchModes(ownGroup, theirGroup);
|
||||
|
||||
const mapList = await matchMapList(
|
||||
{
|
||||
id: ourGroup.id,
|
||||
preferences: ourGroupPreferences,
|
||||
id: ownGroup.id,
|
||||
preferences: ownGroupPreferences,
|
||||
},
|
||||
{
|
||||
id: theirGroup.id,
|
||||
preferences: theirGroupPreferences,
|
||||
ignoreModePreferences: data._action === "MATCH_UP_RECHALLENGE",
|
||||
},
|
||||
modesIncluded,
|
||||
);
|
||||
|
||||
const createdMatch = await SQMatchRepository.create({
|
||||
alphaGroupId: ourGroup.id,
|
||||
alphaGroupId: ownGroup.id,
|
||||
bravoGroupId: theirGroup.id,
|
||||
mapList,
|
||||
memento: createMatchMemento({
|
||||
own: { group: ourGroup, preferences: ourGroupPreferences },
|
||||
own: { group: ownGroup, preferences: ownGroupPreferences },
|
||||
their: { group: theirGroup, preferences: theirGroupPreferences },
|
||||
mapList,
|
||||
}),
|
||||
|
|
@ -174,10 +177,10 @@ export const action: ActionFunction = async ({ request }) => {
|
|||
|
||||
await refreshSendouQInstance();
|
||||
|
||||
if (ourGroup.chatCode && theirGroup.chatCode) {
|
||||
if (ownGroup.chatCode && theirGroup.chatCode) {
|
||||
ChatSystemMessage.send([
|
||||
{
|
||||
room: ourGroup.chatCode,
|
||||
room: ownGroup.chatCode,
|
||||
type: "MATCH_STARTED",
|
||||
revalidateOnly: true,
|
||||
},
|
||||
|
|
@ -191,7 +194,7 @@ export const action: ActionFunction = async ({ request }) => {
|
|||
|
||||
notify({
|
||||
userIds: [
|
||||
...ourGroup.members.map((m) => m.id),
|
||||
...ownGroup.members.map((m) => m.id),
|
||||
...theirGroup.members.map((m) => m.id),
|
||||
],
|
||||
defaultSeenUserIds: [user.id],
|
||||
|
|
|
|||
|
|
@ -80,10 +80,7 @@ export function GroupCard({
|
|||
const isOwnGroup = group.id === ownGroup?.id;
|
||||
|
||||
const futureMatchModes = ownGroup
|
||||
? resolveFutureMatchModes({
|
||||
ownGroup,
|
||||
theirGroup: group,
|
||||
})
|
||||
? resolveFutureMatchModes(ownGroup, group)
|
||||
: null;
|
||||
|
||||
const enableKicking = group.usersRole === "OWNER" && !displayOnly;
|
||||
|
|
|
|||
|
|
@ -58,10 +58,6 @@ export const lookingSchema = z.union([
|
|||
_action: _action("MATCH_UP"),
|
||||
targetGroupId: id,
|
||||
}),
|
||||
z.object({
|
||||
_action: _action("MATCH_UP_RECHALLENGE"),
|
||||
targetGroupId: id,
|
||||
}),
|
||||
z.object({
|
||||
_action: _action("GIVE_MANAGER"),
|
||||
userId: id,
|
||||
|
|
|
|||
|
|
@ -27,15 +27,12 @@ export function userCanJoinQueueAt(
|
|||
return canJoinQueueAt;
|
||||
}
|
||||
|
||||
export function resolveFutureMatchModes({
|
||||
ownGroup,
|
||||
theirGroup,
|
||||
}: {
|
||||
ownGroup: Pick<SQGroup, "modePreferences">;
|
||||
theirGroup: Pick<SQGroup, "modePreferences">;
|
||||
}) {
|
||||
const ourModes = ownGroup.modePreferences;
|
||||
const theirModes = theirGroup.modePreferences;
|
||||
export function resolveFutureMatchModes(
|
||||
groupA: Pick<SQGroup, "modePreferences">,
|
||||
groupB: Pick<SQGroup, "modePreferences">,
|
||||
) {
|
||||
const ourModes = groupA.modePreferences;
|
||||
const theirModes = groupB.modePreferences;
|
||||
|
||||
const overlap = ourModes.filter((mode) => theirModes.includes(mode));
|
||||
if (overlap.length > 0) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import {
|
|||
import { safeNumberParse } from "~/utils/number";
|
||||
import type { ChatUser } from "../chat/chat-types";
|
||||
|
||||
const identifierToUserIdQuery = (identifier: string) =>
|
||||
export const identifierToUserIdQuery = (identifier: string) =>
|
||||
db
|
||||
.selectFrom("User")
|
||||
.select("User.id")
|
||||
|
|
|
|||
|
|
@ -256,6 +256,10 @@ export default [
|
|||
"/user/:identifier",
|
||||
"features/api-public/routes/user.$identifier.ts",
|
||||
),
|
||||
route(
|
||||
"/user/:identifier/ids",
|
||||
"features/api-public/routes/user.$identifier.ids.ts",
|
||||
),
|
||||
route(
|
||||
"/calendar/:year/:week",
|
||||
"features/api-public/routes/calendar.$year.$week.ts",
|
||||
|
|
|
|||
BIN
public/static-assets/badges/runspeed.avif
Normal file
BIN
public/static-assets/badges/runspeed.avif
Normal file
Binary file not shown.
BIN
public/static-assets/badges/runspeed.gif
Normal file
BIN
public/static-assets/badges/runspeed.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 957 KiB |
BIN
public/static-assets/badges/runspeed.png
Normal file
BIN
public/static-assets/badges/runspeed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Loading…
Reference in New Issue
Block a user