SQL solution migration to Kysely + getting rid of routes folder (#1530)

* Kysely initial

* Badges initial

* Badge routes migrated

* Badges migrated

* Calendar work

* Fix one type problem

* Calendar work

* findResultsByUserId work

* Calendar reworking finished

* PlusSuggestions work

* Migrated suggestions

* Builds progress

* Migrated builds

* Admin migrated

* Migrate articles

* User search

* Faster getUser

* Selectable/insertable as global

* Refresh prod db script + patronTier index

* identifierToUserId

* updateProfile

* findByIdentifier

* More indexes

* User upsert

* upsertLite

* findAllPlusMembers

* updateResultHighlights

* updateMany

* User finished migration

* Fix types

* Fix PlusVotingResult typing

* PlusVotingRepository WIP

* Migrated resultsByMonthYear

* Migrated plusVotes (done with db. related migrations)

* Plus code to features folder

* Fix TODOs

* Export

* Fix range

* Migrate some user pages

* Move rest user routes

* Move /play

* Map list generator

* Front page

* Move map list generation logic

* Move plus voting logic

* Info

* API

* Adjust TODOs

* theme

* Auth

* Remove TODO
This commit is contained in:
Kalle 2023-11-04 13:15:36 +02:00 committed by GitHub
parent e166ae4e0f
commit c8ea75ebb6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
273 changed files with 3696 additions and 3422 deletions

View File

@ -15,7 +15,10 @@ module.exports = {
"plugin:react-hooks/recommended",
],
rules: {
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
"@typescript-eslint/no-unused-vars": [
"warn",
{ argsIgnorePattern: "^_", ignoreRestSiblings: true },
],
"no-constant-condition": ["error", { checkLoops: false }],
"no-console": ["warn", { allow: ["warn", "error"] }],
"no-warning-comments": ["warn", { terms: ["xxx"] }],

View File

@ -1,4 +1,3 @@
import type { Badge as BadgeDBType } from "~/db/types";
import { badgeUrl } from "~/utils/urls";
import { Image } from "./Image";
@ -8,7 +7,7 @@ export function Badge({
isAnimated,
size,
}: {
badge: Pick<BadgeDBType, "displayName" | "code" | "hue">;
badge: { displayName: string; hue?: number | null; code: string };
onClick?: () => void;
isAnimated: boolean;
size: number;

View File

@ -8,7 +8,7 @@ import type {
UserWithPlusTier,
} from "~/db/types";
import { useIsMounted } from "~/hooks/useIsMounted";
import { useUser } from "~/modules/auth";
import { useUser } from "~/features/auth/core";
import type {
Ability as AbilityType,
ModeShort,

View File

@ -1,6 +1,6 @@
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
import { Button } from "~/components/Button";
import { useUser } from "~/modules/auth";
import { useUser } from "~/features/auth/core";
import {
ERROR_GIRL_IMAGE_PATH,
LOG_IN_URL,

View File

@ -4,7 +4,7 @@ import { type AxisOptions, Chart as ReactChart } from "react-charts";
import type { TooltipRendererProps } from "react-charts/types/components/TooltipRenderer";
import { useTranslation } from "~/hooks/useTranslation";
import { useIsMounted } from "~/hooks/useIsMounted";
import { Theme, useTheme } from "~/modules/theme";
import { Theme, useTheme } from "~/features/theme/core/provider";
export default function Chart({
options,

View File

@ -19,7 +19,7 @@ import {
nonDamagingSpecialWeaponIds,
specialWeaponIds,
} from "~/modules/in-game-lists/weapon-ids";
import { type SerializedMapPoolEvent } from "~/routes/calendar/map-pool-events";
import { type SerializedMapPoolEvent } from "~/features/calendar/routes/map-pool-events";
import type { Unpacked } from "~/utils/types";
import {
gearImageUrl,

View File

@ -2,7 +2,7 @@ import { useCatch, useLocation } from "@remix-run/react";
import clsx from "clsx";
import type * as React from "react";
import { useMatches } from "react-router";
import { useUser } from "~/modules/auth";
import { useUser } from "~/features/auth/core";
import type { RootLoaderData } from "~/root";
import { SideNav } from "app/components/layout/SideNav";

View File

@ -7,7 +7,7 @@ import {
type StageId,
} from "~/modules/in-game-lists";
import { modes, stageIds } from "~/modules/in-game-lists";
import { MapPool } from "~/modules/map-pool-serializer";
import { MapPool } from "~/features/map-list-generator/core/map-pool";
import { modeImageUrl, stageImageUrl } from "~/utils/urls";
import { Button } from "~/components/Button";
import { split, startsWith } from "~/utils/strings";
@ -15,7 +15,7 @@ import { CrossIcon } from "./icons/Cross";
import { ArrowLongLeftIcon } from "./icons/ArrowLongLeft";
import * as React from "react";
import type { CalendarEvent } from "~/db/types";
import type { SerializedMapPoolEvent } from "~/routes/calendar/map-pool-events";
import type { SerializedMapPoolEvent } from "~/features/calendar/routes/map-pool-events";
import { assertType } from "~/utils/types";
import { MapPoolEventsCombobox } from "./Combobox";

View File

@ -3,7 +3,7 @@ import { useFetcher } from "@remix-run/react";
import clsx from "clsx";
import * as React from "react";
import { useDebounce } from "react-use";
import type { UserSearchLoaderData } from "~/routes/u";
import type { UserSearchLoaderData } from "~/features/user-search/routes/u";
import { Avatar } from "./Avatar";
import { useTranslation } from "~/hooks/useTranslation";

View File

@ -1,8 +1,8 @@
import { useTranslation } from "~/hooks/useTranslation";
import { Theme, useTheme } from "~/modules/theme";
import { MoonIcon } from "../icons/Moon";
import { SunIcon } from "../icons/Sun";
import { SunAndMoonIcon } from "../icons/SunAndMoon";
import { Theme, useTheme } from "~/features/theme/core/provider";
const ThemeIcons = {
[Theme.LIGHT]: SunIcon,

View File

@ -1,11 +1,11 @@
import { useTranslation } from "~/hooks/useTranslation";
import { Theme, useTheme } from "~/modules/theme";
import { Button } from "../Button";
import { MoonIcon } from "../icons/Moon";
import { SunIcon } from "../icons/Sun";
import { SunAndMoonIcon } from "../icons/SunAndMoon";
import { Popover } from "../Popover";
import { SelectedThemeIcon } from "./SelectedThemeIcon";
import { Theme, useTheme } from "~/features/theme/core/provider";
const ThemeIcons = {
[Theme.LIGHT]: SunIcon,

View File

@ -1,6 +1,6 @@
import { Link } from "@remix-run/react";
import { useTranslation } from "~/hooks/useTranslation";
import { useUser } from "~/modules/auth";
import { useUser } from "~/features/auth/core";
import { userPage } from "~/utils/urls";
import { Avatar } from "../Avatar";
import { LogInIcon } from "../icons/LogIn";

View File

@ -1,4 +1,4 @@
import allTags from "~/routes/calendar/tags.json";
import allTags from "~/features/calendar/tags.json";
import type { CalendarEventTag } from "./db/types";
import type { BuildAbilitiesTupleWithUnknown } from "./modules/in-game-lists";

View File

@ -1,15 +0,0 @@
import * as users from "./models/users/queries.server";
import * as plusSuggestions from "./models/plusSuggestions/queries.server";
import * as plusVotes from "./models/plusVotes/queries.server";
import * as badges from "./models/badges/queries.server";
import * as calendarEvents from "./models/calendar/queries.server";
import * as builds from "./models/builds/queries.server";
export const db = {
users,
plusSuggestions,
plusVotes,
badges,
calendarEvents,
builds,
};

View File

@ -1,4 +0,0 @@
insert into
"BadgeManager" ("badgeId", "userId")
values
(@badgeId, @userId)

View File

@ -1,4 +0,0 @@
insert into
"TournamentBadgeOwner" ("badgeId", "userId")
values
(@badgeId, @userId)

View File

@ -1,4 +0,0 @@
delete from
"BadgeManager"
where
"badgeId" = @badgeId

View File

@ -1,4 +0,0 @@
delete from
"TournamentBadgeOwner"
where
"badgeId" = @badgeId

View File

@ -1,7 +0,0 @@
select
"Badge"."id",
"Badge"."code",
"Badge"."displayName",
"Badge"."hue"
from
"Badge"

View File

@ -1,24 +0,0 @@
select
"Badge"."code",
"Badge"."displayName",
"Badge"."id",
"Badge"."hue",
count("BadgeOwner"."badgeId") as count
from
"BadgeOwner"
join "Badge" on "Badge"."id" = "BadgeOwner"."badgeId"
where
"BadgeOwner"."userId" = @userId
group by
"BadgeOwner"."badgeId",
"BadgeOwner"."userId"
order by
"Badge"."id" = (
select
"favoriteBadgeId"
from
"User"
where
"id" = @userId
) desc,
"Badge"."id" asc

View File

@ -1,10 +0,0 @@
select
"Badge"."id",
"Badge"."code",
"Badge"."displayName",
"Badge"."hue"
from
"BadgeManager"
join "Badge" on "Badge"."id" = "BadgeManager"."badgeId"
where
"userId" = @userId

View File

@ -1,10 +0,0 @@
select
"User"."id",
"User"."discordId",
"User"."discordName",
"User"."discordDiscriminator"
from
"BadgeManager"
join "User" on "User"."id" = "BadgeManager"."userId"
where
"BadgeManager"."badgeId" = @id

View File

@ -1,16 +0,0 @@
select
count("BadgeOwner"."badgeId") as count,
"User"."id",
"User"."discordId",
"User"."discordName",
"User"."discordDiscriminator"
from
"BadgeOwner"
join "User" on "User"."id" = "BadgeOwner"."userId"
where
"BadgeOwner"."badgeId" = @id
group by
"User"."id"
order by
count desc,
"User"."discordName" collate nocase asc

View File

@ -1,106 +0,0 @@
import { sql } from "../../sql";
import type { Badge, User } from "../../types";
import deleteManyManagersSql from "./deleteManyManagers.sql";
import createManagerSql from "./createManager.sql";
import deleteManyOwnersSql from "./deleteManyOwners.sql";
import createOwnerSql from "./createOwner.sql";
import findByOwnerIdSql from "./findByOwnerId.sql";
import findAllSql from "./findAll.sql";
import ownersByBadgeIdSql from "./ownersByBadgeId.sql";
import managersByBadgeIdSql from "./managersByBadgeId.sql";
import managedByUserIdSql from "./managedByUserId.sql";
const deleteManyManagersStm = sql.prepare(deleteManyManagersSql);
const createManagerStm = sql.prepare(createManagerSql);
export const upsertManyManagers = sql.transaction(
({
badgeId,
managerIds,
}: {
badgeId: Badge["id"];
managerIds: Array<User["id"]>;
}) => {
deleteManyManagersStm.run({
badgeId,
});
for (const userId of managerIds) {
createManagerStm.run({
userId,
badgeId,
});
}
},
);
const deleteManyOwnersStm = sql.prepare(deleteManyOwnersSql);
const createOwnerStm = sql.prepare(createOwnerSql);
export const upsertManyOwners = sql.transaction(
({
badgeId,
ownerIds,
}: {
badgeId: Badge["id"];
ownerIds: Array<User["id"]>;
}) => {
deleteManyOwnersStm.run({
badgeId,
});
for (const userId of ownerIds) {
createOwnerStm.run({
userId,
badgeId,
});
}
},
);
const findByOwnerIdStm = sql.prepare(findByOwnerIdSql);
export type FindByOwnerId = Array<
Pick<Badge, "code" | "displayName" | "id" | "hue"> & {
count: number;
}
>;
export function findByOwnerId(userId: User["id"]) {
return findByOwnerIdStm.all({ userId }) as FindByOwnerId;
}
const findAllStm = sql.prepare(findAllSql);
export type FindAll = Array<Pick<Badge, "id" | "displayName" | "code" | "hue">>;
export function all() {
return findAllStm.all() as FindAll;
}
export type OwnersByBadgeId = Array<
Pick<User, "id" | "discordId" | "discordName" | "discordDiscriminator"> & {
count: number;
}
>;
const ownersByBadgeIdStm = sql.prepare(ownersByBadgeIdSql);
export function ownersByBadgeId(id: Badge["id"]) {
return ownersByBadgeIdStm.all({ id }) as OwnersByBadgeId;
}
export type ManagersByBadgeId = Array<
Pick<User, "id" | "discordId" | "discordName" | "discordDiscriminator">
>;
const managersByBadgeIdStm = sql.prepare(managersByBadgeIdSql);
export function managersByBadgeId(id: Badge["id"]) {
return managersByBadgeIdStm.all({ id }) as ManagersByBadgeId;
}
const managedByUserIdStm = sql.prepare(managedByUserIdSql);
export function managedByUserId(userId: User["id"]) {
return managedByUserIdStm.all({ userId }) as Array<
Pick<Badge, "id" | "code" | "displayName" | "hue">
>;
}

View File

@ -1,10 +0,0 @@
select
count(*) as "count"
from
"Build"
where
"Build"."ownerId" = @userId
and (
"Build"."private" = 0
or "Build"."ownerId" = @loggedInUserId
)

View File

@ -1,22 +0,0 @@
insert into
"Build" (
"ownerId",
"title",
"description",
"modes",
"headGearSplId",
"clothesGearSplId",
"shoesGearSplId",
"private"
)
values
(
@ownerId,
@title,
@description,
@modes,
@headGearSplId,
@clothesGearSplId,
@shoesGearSplId,
@private
) returning *

View File

@ -1,4 +0,0 @@
insert into
"BuildAbility" ("buildId", "gearType", "ability", "slotIndex")
values
(@buildId, @gearType, @ability, @slotIndex)

View File

@ -1,4 +0,0 @@
insert into
"BuildWeapon" ("buildId", "weaponSplId")
values
(@buildId, @weaponSplId)

View File

@ -1,4 +0,0 @@
delete from
"Build"
where
"id" = @id;

View File

@ -1,96 +0,0 @@
import { sql } from "~/db/sql";
import type {
Build,
BuildWeapon,
GearType,
UserWithPlusTier,
} from "~/db/types";
import { modesShort, type ModeShort } from "~/modules/in-game-lists";
import type { BuildAbilitiesTuple } from "~/modules/in-game-lists/types";
import countByUserIdSql from "./countByUserId.sql";
import createBuildSql from "./createBuild.sql";
import createBuildAbilitySql from "./createBuildAbility.sql";
import createBuildWeaponSql from "./createBuildWeapon.sql";
import deleteByIdSql from "./deleteById.sql";
const createBuildStm = sql.prepare(createBuildSql);
const createBuildWeaponStm = sql.prepare(createBuildWeaponSql);
const createBuildAbilityStm = sql.prepare(createBuildAbilitySql);
const countByUserIdStm = sql.prepare(countByUserIdSql);
const deleteByIdStm = sql.prepare(deleteByIdSql);
interface CreateArgs {
ownerId: Build["ownerId"];
title: Build["title"];
description: Build["description"];
modes: Array<ModeShort> | null;
headGearSplId: Build["headGearSplId"];
clothesGearSplId: Build["clothesGearSplId"];
shoesGearSplId: Build["shoesGearSplId"];
weaponSplIds: Array<BuildWeapon["weaponSplId"]>;
abilities: BuildAbilitiesTuple;
private: Build["private"];
}
export const create = sql.transaction((build: CreateArgs) => {
const createdBuild = createBuildStm.get({
ownerId: build.ownerId,
title: build.title,
description: build.description,
modes:
build.modes && build.modes.length > 0
? JSON.stringify(
build.modes
.slice()
.sort((a, b) => modesShort.indexOf(a) - modesShort.indexOf(b)),
)
: null,
headGearSplId: build.headGearSplId,
clothesGearSplId: build.clothesGearSplId,
shoesGearSplId: build.shoesGearSplId,
private: build.private,
}) as Build;
for (const weaponSplId of build.weaponSplIds) {
createBuildWeaponStm.run({
buildId: createdBuild.id,
weaponSplId,
});
}
for (const [rowI, row] of build.abilities.entries()) {
const gearType: GearType =
rowI === 0 ? "HEAD" : rowI === 1 ? "CLOTHES" : "SHOES";
for (const [abilityI, ability] of row.entries()) {
createBuildAbilityStm.run({
buildId: createdBuild.id,
gearType,
ability,
slotIndex: abilityI,
});
}
}
});
export const updateByReplacing = sql.transaction(
(build: CreateArgs & { id: Build["id"] }) => {
deleteByIdStm.run({ id: build.id });
create(build);
},
);
export function countByUserId({
userId,
loggedInUserId,
}: {
userId: Build["ownerId"];
loggedInUserId?: UserWithPlusTier["id"];
}) {
return ((countByUserIdStm.get({ userId, loggedInUserId }) as any)?.count ??
0) as number;
}
export function deleteById(id: Build["id"]) {
deleteByIdStm.run({ id });
}

View File

@ -1,20 +0,0 @@
insert into
"CalendarEvent" (
"name",
"authorId",
"tags",
"description",
"discordInviteCode",
"bracketUrl",
"tournamentId"
)
values
(
@name,
@authorId,
@tags,
@description,
@discordInviteCode,
@bracketUrl,
@tournamentId
) returning *

View File

@ -1,4 +0,0 @@
insert into
"CalendarEventBadge" ("eventId", "badgeId")
values
(@eventId, @badgeId)

View File

@ -1,4 +0,0 @@
insert into
"CalendarEventDate" ("eventId", "startTime")
values
(@eventId, @startTime)

View File

@ -1,4 +0,0 @@
insert into
"MapPoolMap" ("calendarEventId", "stageId", "mode")
values
(@calendarEventId, @stageId, @mode)

View File

@ -1,4 +0,0 @@
insert into
"MapPoolMap" ("tieBreakerCalendarEventId", "stageId", "mode")
values
(@calendarEventId, @stageId, @mode)

View File

@ -1,4 +0,0 @@
insert into
"Tournament" ("mapPickingStyle", "format")
values
(@mapPickingStyle, @format) returning *

View File

@ -1,4 +0,0 @@
delete from
"CalendarEventBadge"
where
"eventId" = @eventId

View File

@ -1,4 +0,0 @@
delete from
"CalendarEvent"
where
"id" = @id

View File

@ -1,4 +0,0 @@
delete from
"CalendarEventDate"
where
"eventId" = @eventId

View File

@ -1,5 +0,0 @@
delete from
"MapPoolMap"
where
"calendarEventId" = @calendarEventId
or "tieBreakerCalendarEventId" = @calendarEventId

View File

@ -1,4 +0,0 @@
delete from
"CalendarEventResultTeam"
where
"eventId" = @eventId

View File

@ -1,4 +0,0 @@
delete from
"Tournament"
where
id = @id

View File

@ -1,19 +0,0 @@
select
"CalendarEvent"."id",
"CalendarEvent"."name",
(
select
max("startTime")
from
"CalendarEventDate"
where
"eventId" = "CalendarEvent"."id"
) as "startTime"
from
"CalendarEvent"
where
"CalendarEvent"."authorId" = @authorId
and "startTime" > @lowerLimitTime
and "startTime" < @upperLimitTime
and "CalendarEvent"."participantCount" is null
and "CalendarEvent"."tournamentId" is null

View File

@ -1,42 +0,0 @@
select
"CalendarEvent"."name",
"CalendarEvent"."discordUrl",
"CalendarEvent"."bracketUrl",
"CalendarEvent"."tags",
"CalendarEvent"."tournamentId",
"CalendarEventDate"."id" as "eventDateId",
"CalendarEventDate"."eventId",
"CalendarEventDate"."startTime",
"User"."discordName",
"User"."discordDiscriminator",
"CalendarEventRanks"."nthAppearance",
exists (
select
1
from
"CalendarEventBadge"
where
"CalendarEventBadge"."eventId" = "CalendarEventDate"."eventId"
) as "hasBadge"
from
"CalendarEvent"
join "CalendarEventDate" on "CalendarEvent"."id" = "CalendarEventDate"."eventId"
join "User" on "CalendarEvent"."authorId" = "User"."id"
join (
select
"id",
"eventId",
"startTime",
rank() over(
partition by "eventId"
order by
"startTime" asc
) "nthAppearance"
from
"CalendarEventDate"
) "CalendarEventRanks" on "CalendarEventDate"."id" = "CalendarEventRanks"."id"
where
"CalendarEventDate"."startTime" between @startTime
and @endTime
order by
"CalendarEventDate"."startTime" asc

View File

@ -1,18 +0,0 @@
select
"CalendarEvent"."id",
"CalendarEvent"."name",
json_group_array(
json_object(
'stageId',
"MapPoolMap"."stageId",
'mode',
"MapPoolMap"."mode"
)
) as "mapPool"
from
"CalendarEvent"
join "MapPoolMap" on "CalendarEvent"."id" = "MapPoolMap"."calendarEventId"
group by
"CalendarEvent"."id"
order by
"CalendarEvent"."id" desc

View File

@ -1,10 +0,0 @@
select
"Badge"."id",
"Badge"."code",
"Badge"."hue",
"Badge"."displayName"
from
"CalendarEventBadge"
join "Badge" on "CalendarEventBadge"."badgeId" = "Badge"."id"
where
"CalendarEventBadge"."eventId" = @eventId

View File

@ -1,34 +0,0 @@
select
"CalendarEvent"."name",
"CalendarEvent"."description",
"CalendarEvent"."discordInviteCode",
"CalendarEvent"."discordUrl",
"CalendarEvent"."bracketUrl",
"CalendarEvent"."tags",
"CalendarEvent"."tournamentId",
"CalendarEvent"."participantCount",
"Tournament"."mapPickingStyle",
"User"."id" as "authorId",
exists (
select
1
from
"CalendarEventBadge"
where
"CalendarEventBadge"."eventId" = "CalendarEventDate"."eventId"
) as "hasBadge",
"CalendarEventDate"."startTime",
"CalendarEventDate"."eventId",
"User"."discordName",
"User"."discordDiscriminator",
"User"."discordId",
"User"."discordAvatar"
from
"CalendarEvent"
join "CalendarEventDate" on "CalendarEvent"."id" = "CalendarEventDate"."eventId"
join "User" on "CalendarEvent"."authorId" = "User"."id"
left join "Tournament" on "CalendarEvent"."tournamentId" = "Tournament"."id"
where
"CalendarEvent"."id" = @id
order by
"CalendarEventDate"."startTime" asc

View File

@ -1,7 +0,0 @@
select
"stageId",
"mode"
from
"MapPoolMap"
where
"calendarEventId" = @calendarEventId

View File

@ -1,16 +0,0 @@
select
"CalendarEventResultPlayer"."name",
"User"."id",
"User"."discordName" as "discordName",
"User"."discordDiscriminator" as "discordDiscriminator",
"User"."discordId" as "discordId",
"User"."discordAvatar" as "discordAvatar"
from
"CalendarEventResultPlayer"
left join "User" on "User"."id" = "CalendarEventResultPlayer"."userId"
where
"teamId" = @teamId
and (
"CalendarEventResultPlayer"."userId" is null
or "CalendarEventResultPlayer"."userId" != @userId
)

View File

@ -1,12 +0,0 @@
select
"User"."id",
"User"."discordName",
"User"."discordDiscriminator",
"User"."discordId",
"User"."discordAvatar"
from
"TournamentTeamMember"
left join "User" on "User"."id" = "TournamentTeamMember"."userId"
where
"TournamentTeamMember"."tournamentTeamId" = @teamId
and "TournamentTeamMember"."userId" != @userId

View File

@ -1,21 +0,0 @@
select
"CalendarEvent"."id",
"CalendarEvent"."name",
json_group_array(
json_object(
'stageId',
"MapPoolMap"."stageId",
'mode',
"MapPoolMap"."mode"
)
) as "mapPool"
from
"CalendarEvent"
join "MapPoolMap" on "CalendarEvent"."id" = "MapPoolMap"."calendarEventId"
where
"CalendarEvent"."authorId" = @authorId
group by
"CalendarEvent"."id"
order by
"CalendarEvent"."id" desc
limit 5

View File

@ -1,58 +0,0 @@
select
"CalendarEvent"."id" as "eventId",
null as "tournamentId",
"CalendarEventResultTeam"."placement",
"CalendarEvent"."participantCount",
"CalendarEvent"."name" as "eventName",
"CalendarEventResultTeam"."id" as "teamId",
"CalendarEventResultTeam"."name" as "teamName",
(
select
max("startTime")
from
"CalendarEventDate"
where
"eventId" = "CalendarEvent"."id"
) as "startTime",
exists (
select
1
from
"UserResultHighlight"
where
"userId" = @userId
and "teamId" = "CalendarEventResultTeam"."id"
) as "isHighlight"
from
"CalendarEventResultPlayer"
join "CalendarEventResultTeam" on "CalendarEventResultTeam"."id" = "CalendarEventResultPlayer"."teamId"
join "CalendarEvent" on "CalendarEvent"."id" = "CalendarEventResultTeam"."eventId"
where
"CalendarEventResultPlayer"."userId" = @userId
union
all
select
null as "eventId",
"TournamentResult"."tournamentId",
"TournamentResult"."placement",
"TournamentResult"."participantCount",
"CalendarEvent"."name" as "eventName",
"TournamentTeam"."id" as "teamId",
"TournamentTeam"."name" as "teamName",
(
select
max("startTime")
from
"CalendarEventDate"
where
"eventId" = "CalendarEvent"."id"
) as "startTime",
"TournamentResult"."isHighlight"
from
"TournamentResult"
left join "TournamentTeam" on "TournamentTeam"."id" = "TournamentResult"."tournamentTeamId"
left join "CalendarEvent" on "CalendarEvent"."tournamentId" = "TournamentResult"."tournamentId"
where
"TournamentResult"."userId" = @userId
order by
"startTime" desc

View File

@ -1,7 +0,0 @@
select
"stageId",
"mode"
from
"MapPoolMap"
where
"tieBreakerCalendarEventId" = @calendarEventId

View File

@ -1,9 +0,0 @@
select
count(*) as "memberCount"
from
"TournamentTeam"
left join "TournamentTeamMember" on "TournamentTeamMember"."tournamentTeamId" = "TournamentTeam"."id"
where
"TournamentTeam"."tournamentId" = @tournamentId
group by
"TournamentTeam"."id";

View File

@ -1,18 +0,0 @@
select
"CalendarEventResultTeam"."id",
"CalendarEventResultTeam"."name" as "teamName",
"CalendarEventResultTeam"."placement",
"CalendarEventResultPlayer"."userId" as "playerId",
"CalendarEventResultPlayer"."name" as "playerName",
"User"."discordName" as "playerDiscordName",
"User"."discordDiscriminator" as "playerDiscordDiscriminator",
"User"."discordId" as "playerDiscordId",
"User"."discordAvatar" as "playerDiscordAvatar"
from
"CalendarEventResultTeam"
left join "CalendarEventResultPlayer" on "CalendarEventResultPlayer"."teamId" = "CalendarEventResultTeam"."id"
left join "User" on "User"."id" = "CalendarEventResultPlayer"."userId"
where
"CalendarEventResultTeam"."eventId" = @eventId
order by
"CalendarEventResultTeam"."placement" asc

View File

@ -1,4 +0,0 @@
insert into
"CalendarEventResultPlayer" ("teamId", "userId", "name")
values
(@teamId, @userId, @name)

View File

@ -1,4 +0,0 @@
insert into
"CalendarEventResultTeam" ("eventId", "name", "placement")
values
(@eventId, @name, @placement) returning *

View File

@ -1,564 +0,0 @@
import { dateToDatabaseTimestamp } from "~/utils/dates";
import { sql } from "../../sql";
import type {
CalendarEvent,
CalendarEventDate,
User,
Badge,
CalendarEventTag,
CalendarEventBadge,
CalendarEventResultTeam,
CalendarEventResultPlayer,
MapPoolMap,
Tournament,
} from "../../types";
import { MapPool } from "~/modules/map-pool-serializer";
import createSql from "./create.sql";
import updateSql from "./update.sql";
import createDateSql from "./createDate.sql";
import deleteDatesByEventIdSql from "./deleteDatesByEventId.sql";
import createBadgeSql from "./createBadge.sql";
import deleteBadgesByEventIdSql from "./deleteBadgesByEventId.sql";
import updateParticipantsCountSql from "./updateParticipantsCount.sql";
import deleteResultTeamsByEventIdSql from "./deleteResultTeamsByEventId.sql";
import insertResultTeamSql from "./insertResultTeam.sql";
import insertResultPlayerSql from "./insertResultPlayer.sql";
import findWinnersByEventIdSql from "./findWinnersByEventId.sql";
import findResultsByUserIdSql from "./findResultsByUserId.sql";
import findMatesByTournamentTeamIdSql from "./findMatesByTournamentTeamId.sql";
import findMatesByResultTeamIdSql from "./findMatesByResultTeamId.sql";
import findAllBetweenTwoTimestampsSql from "./findAllBetweenTwoTimestamps.sql";
import findByIdSql from "./findById.sql";
import startTimesOfRangeSql from "./startTimesOfRange.sql";
import findBadgesByEventIdSql from "./findBadgesByEventId.sql";
import eventsToReportSql from "./eventsToReport.sql";
import createMapPoolMapSql from "./createMapPoolMap.sql";
import deleteMapPoolMapsSql from "./deleteMapPoolMaps.sql";
import createTieBreakerMapPoolMapSql from "./createTieBreakerMapPoolMap.sql";
import findMapPoolByEventIdSql from "./findMapPoolByEventId.sql";
import findRecentMapPoolsByAuthorIdSql from "./findRecentMapPoolsByAuthorId.sql";
import findAllEventsWithMapPoolsSql from "./findAllEventsWithMapPools.sql";
import findTieBreakerMapPoolByEventIdSql from "./findTieBreakerMapPoolByEventId.sql";
import deleteByIdSql from "./deleteById.sql";
import createTournamentSql from "./createTournament.sql";
import deleteTournamentByIdSql from "./deleteTournamentById.sql";
import findTournamentTeamMemberCountsByEventIdSql from "./findTournamentTeamMemberCountsByEventId.sql";
import { sumArray } from "~/utils/number";
const createStm = sql.prepare(createSql);
const updateStm = sql.prepare(updateSql);
const createDateStm = sql.prepare(createDateSql);
const deleteDatesByEventIdStm = sql.prepare(deleteDatesByEventIdSql);
const createBadgeStm = sql.prepare(createBadgeSql);
const deleteBadgesByEventIdStm = sql.prepare(deleteBadgesByEventIdSql);
const createMapPoolMapStm = sql.prepare(createMapPoolMapSql);
const deleteMapPoolMapsStm = sql.prepare(deleteMapPoolMapsSql);
const createTieBreakerMapPoolMapStm = sql.prepare(
createTieBreakerMapPoolMapSql,
);
const findMapPoolByEventIdStm = sql.prepare(findMapPoolByEventIdSql);
const findTieBreakerMapPoolByEventIdtm = sql.prepare(
findTieBreakerMapPoolByEventIdSql,
);
const deleteByIdStm = sql.prepare(deleteByIdSql);
const deleteTournamentByIdStm = sql.prepare(deleteTournamentByIdSql);
const createTournamentStm = sql.prepare(createTournamentSql);
const createTournament = (
args: Omit<Tournament, "id" | "showMapListGenerator">,
) => {
return createTournamentStm.get(args) as Tournament;
};
export type CreateArgs = Pick<
CalendarEvent,
| "name"
| "authorId"
| "tags"
| "description"
| "discordInviteCode"
| "bracketUrl"
> & {
startTimes: Array<CalendarEventDate["startTime"]>;
badges: Array<CalendarEventBadge["badgeId"]>;
mapPoolMaps?: Array<Pick<MapPoolMap, "mode" | "stageId">>;
createTournament: boolean;
mapPickingStyle: Tournament["mapPickingStyle"];
};
export const create = sql.transaction(
({
startTimes,
badges,
mapPoolMaps = [],
...calendarEventArgs
}: CreateArgs) => {
let tournamentId;
if (calendarEventArgs.createTournament) {
tournamentId = createTournament({
// TODO: format picking
format: "DE",
mapPickingStyle: calendarEventArgs.mapPickingStyle,
}).id;
}
const createdEvent = createStm.get({
...calendarEventArgs,
tournamentId,
}) as CalendarEvent;
for (const startTime of startTimes) {
createDateStm.run({
eventId: createdEvent.id,
startTime,
});
}
for (const badgeId of badges) {
createBadgeStm.run({
eventId: createdEvent.id,
badgeId,
});
}
upsertMapPool({
eventId: createdEvent.id,
mapPoolMaps,
isFullTournament: calendarEventArgs.createTournament,
});
return createdEvent.id;
},
);
export type Update = Omit<
CreateArgs,
"authorId" | "createTournament" | "mapPickingStyle"
> & {
eventId: CalendarEvent["id"];
};
export const update = sql.transaction(
({
startTimes,
badges,
mapPoolMaps = [],
eventId,
...calendarEventArgs
}: Update) => {
const event = updateStm.get({
...calendarEventArgs,
eventId,
}) as CalendarEvent;
deleteDatesByEventIdStm.run({ eventId });
for (const startTime of startTimes) {
createDateStm.run({
eventId,
startTime,
});
}
deleteBadgesByEventIdStm.run({ eventId });
for (const badgeId of badges) {
createBadgeStm.run({
eventId,
badgeId,
});
}
// can't edit tournament specific info after creation
if (!event.tournamentId) {
upsertMapPool({
eventId,
mapPoolMaps,
isFullTournament: false,
});
}
},
);
function upsertMapPool({
eventId,
mapPoolMaps,
isFullTournament,
}: {
eventId: Update["eventId"];
mapPoolMaps: NonNullable<Update["mapPoolMaps"]>;
isFullTournament: boolean;
}) {
deleteMapPoolMapsStm.run({ calendarEventId: eventId });
if (isFullTournament) {
for (const mapPoolArgs of mapPoolMaps) {
createTieBreakerMapPoolMapStm.run({
calendarEventId: eventId,
...mapPoolArgs,
});
}
} else {
for (const mapPoolArgs of mapPoolMaps) {
createMapPoolMapStm.run({
calendarEventId: eventId,
...mapPoolArgs,
});
}
}
}
const updateParticipantsCountStm = sql.prepare(updateParticipantsCountSql);
const deleteResultTeamsByEventIdStm = sql.prepare(
deleteResultTeamsByEventIdSql,
);
const insertResultTeamStm = sql.prepare(insertResultTeamSql);
const insertResultPlayerStm = sql.prepare(insertResultPlayerSql);
export const upsertReportedScores = sql.transaction(
({
eventId,
participantCount,
results,
}: {
eventId: CalendarEvent["id"];
participantCount: CalendarEvent["participantCount"];
results: Array<{
teamName: CalendarEventResultTeam["name"];
placement: CalendarEventResultTeam["placement"];
players: Array<{
userId: CalendarEventResultPlayer["userId"];
name: CalendarEventResultPlayer["name"];
}>;
}>;
}) => {
updateParticipantsCountStm.run({ eventId, participantCount });
deleteResultTeamsByEventIdStm.run({ eventId });
for (const { players, ...teamArgs } of results) {
const teamInDb = insertResultTeamStm.get({
eventId,
name: teamArgs.teamName,
placement: teamArgs.placement,
}) as CalendarEventResultTeam;
for (const playerArgs of players) {
insertResultPlayerStm.run({
teamId: teamInDb.id,
userId: playerArgs.userId,
name: playerArgs.name,
});
}
}
},
);
const findWinnersByEventIdStm = sql.prepare(findWinnersByEventIdSql);
export function findResultsByEventId(eventId: CalendarEvent["id"]) {
const rows = findWinnersByEventIdStm.all({ eventId }) as Array<{
id: CalendarEventResultTeam["id"];
teamName: CalendarEventResultTeam["name"];
placement: CalendarEventResultTeam["placement"];
playerId: CalendarEventResultPlayer["userId"];
playerName: CalendarEventResultPlayer["name"] | null;
playerDiscordName: User["discordName"] | null;
playerDiscordDiscriminator: User["discordDiscriminator"] | null;
playerDiscordId: User["discordId"] | null;
playerDiscordAvatar: User["discordAvatar"];
}>;
const result: Array<{
teamName: CalendarEventResultTeam["name"];
placement: CalendarEventResultTeam["placement"];
players: Array<
| string
| Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
>
>;
}> = [];
for (const row of rows) {
const team = result.find((team) => team.teamName === row.teamName);
const player = row.playerName ?? {
// player name and user id are mutually exclusive
// also if user id exists we know a joined user also has to exist
id: row.playerId!,
discordId: row.playerDiscordId!,
discordName: row.playerDiscordName!,
discordDiscriminator: row.playerDiscordDiscriminator!,
discordAvatar: row.playerDiscordAvatar,
};
if (team) {
team.players.push(player);
} else {
result.push({
teamName: row.teamName,
placement: row.placement,
players: [player],
});
}
}
return result;
}
const findResultsByUserIdStm = sql.prepare(findResultsByUserIdSql);
const findMatesByResultTeamIdStm = sql.prepare(findMatesByResultTeamIdSql);
const findMatesByTournamentTeamIdStm = sql.prepare(
findMatesByTournamentTeamIdSql,
);
export function findResultsByUserId(userId: User["id"]) {
return (
findResultsByUserIdStm.all({ userId }) as Array<{
eventId: CalendarEvent["id"] | null;
tournamentId: CalendarEvent["tournamentId"] | null;
teamId: CalendarEventResultTeam["id"];
eventName: CalendarEvent["name"];
teamName: CalendarEventResultTeam["name"];
placement: CalendarEventResultTeam["placement"];
participantCount: CalendarEvent["participantCount"];
startTime: CalendarEventDate["startTime"];
isHighlight: number;
}>
).map((row) => ({
...row,
isHighlight: Boolean(row.isHighlight),
mates: (
(row.tournamentId
? findMatesByTournamentTeamIdStm
: findMatesByResultTeamIdStm
).all({
teamId: row.teamId,
userId,
}) as Array<{
name: CalendarEventResultPlayer["name"];
id: User["id"];
discordName: User["discordName"];
discordDiscriminator: User["discordDiscriminator"];
discordId: User["discordId"];
discordAvatar: User["discordAvatar"];
}>
).map(({ name, ...mate }) => name ?? mate),
}));
}
const findAllBetweenTwoTimestampsStm = sql.prepare(
findAllBetweenTwoTimestampsSql,
);
function addTagArray<
T extends {
hasBadge: number;
tags?: CalendarEvent["tags"];
tournamentId: CalendarEvent["tournamentId"];
},
>(arg: T) {
const { hasBadge, ...row } = arg;
const tags = (row.tags ? row.tags.split(",") : []) as Array<CalendarEventTag>;
if (hasBadge) tags.unshift("BADGE");
if (row.tournamentId) tags.unshift("FULL_TOURNAMENT");
return { ...row, tags };
}
const findTournamentTeamMemberCountsByEventIdStm = sql.prepare(
findTournamentTeamMemberCountsByEventIdSql,
);
function addParticipantsCounts<
T extends { tournamentId: CalendarEvent["tournamentId"] },
>(arg: T) {
if (!arg.tournamentId) return arg;
const counts = findTournamentTeamMemberCountsByEventIdStm.all({
tournamentId: arg.tournamentId,
}) as Array<{
memberCount: number;
}>;
return {
...arg,
participantCounts: {
teams: counts.length,
players: sumArray(counts.map((c) => c.memberCount)),
},
};
}
export function findAllBetweenTwoTimestamps({
startTime,
endTime,
}: {
startTime: Date;
endTime: Date;
}) {
const rows = findAllBetweenTwoTimestampsStm.all({
startTime: dateToDatabaseTimestamp(startTime),
endTime: dateToDatabaseTimestamp(endTime),
}) as Array<
Pick<
CalendarEvent,
"name" | "discordUrl" | "bracketUrl" | "tags" | "tournamentId"
> &
Pick<CalendarEventDate, "eventId" | "startTime"> & {
eventDateId: CalendarEventDate["id"];
} & Pick<User, "discordName" | "discordDiscriminator"> & {
nthAppearance: number;
} & { hasBadge: number } & {
participantCounts?: { teams: number; players: number };
}
>;
return rows.map(addTagArray).map(addBadges).map(addParticipantsCounts);
}
const findByIdStm = sql.prepare(findByIdSql);
export function findById(id: CalendarEvent["id"]) {
const [firstRow, ...rest] = findByIdStm.all({ id }) as Array<
Pick<
CalendarEvent,
| "name"
| "description"
| "discordUrl"
| "discordInviteCode"
| "bracketUrl"
| "tags"
| "authorId"
| "participantCount"
| "tournamentId"
> &
Pick<Tournament, "mapPickingStyle"> &
Pick<CalendarEventDate, "startTime" | "eventId"> &
Pick<
User,
"discordName" | "discordDiscriminator" | "discordId" | "discordAvatar"
> & { hasBadge: number }
>;
if (!firstRow) return null;
return addTagArray({
...firstRow,
startTimes: [firstRow, ...rest].map((row) => row.startTime),
startTime: undefined,
});
}
function addBadges<
T extends { eventId: CalendarEvent["id"]; tags?: CalendarEvent["tags"] },
>(arg: T) {
return {
...arg,
badgePrizes: arg.tags?.includes("BADGE")
? findBadgesByEventId(arg.eventId)
: [],
};
}
const startTimesOfRangeStm = sql.prepare(startTimesOfRangeSql);
export function startTimesOfRange({
startTime,
endTime,
}: {
startTime: Date;
endTime: Date;
}) {
return (
startTimesOfRangeStm.all({
startTime: dateToDatabaseTimestamp(startTime),
endTime: dateToDatabaseTimestamp(endTime),
}) as Array<Pick<CalendarEventDate, "startTime">>
).map(({ startTime }) => startTime);
}
const findBadgesByEventIdStm = sql.prepare(findBadgesByEventIdSql);
export function findBadgesByEventId(eventId: CalendarEvent["id"]) {
return findBadgesByEventIdStm.all({ eventId }) as Array<
Pick<Badge, "id" | "code" | "hue" | "displayName">
>;
}
export function findMapPoolByEventId(calendarEventId: CalendarEvent["id"]) {
const rows = findMapPoolByEventIdStm.all({ calendarEventId }) as Array<
Pick<MapPoolMap, "stageId" | "mode">
>;
if (rows.length === 0) return;
return MapPool.parse(rows);
}
const eventsToReportStm = sql.prepare(eventsToReportSql);
export function eventsToReport(authorId?: CalendarEvent["authorId"]) {
if (!authorId) return [];
const oneMonthAgo = new Date();
oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);
return (
eventsToReportStm.all({
authorId,
upperLimitTime: dateToDatabaseTimestamp(new Date()),
lowerLimitTime: dateToDatabaseTimestamp(oneMonthAgo),
}) as Array<Pick<CalendarEvent, "id" | "name">>
).map((row) => ({ id: row.id, name: row.name }));
}
const findRecentMapPoolsByAuthorIdStm = sql.prepare(
findRecentMapPoolsByAuthorIdSql,
);
export function findRecentMapPoolsByAuthorId(
authorId: CalendarEvent["authorId"],
) {
return (
findRecentMapPoolsByAuthorIdStm.all({ authorId }) as Array<
Pick<CalendarEvent, "id" | "name"> & {
mapPool: string;
}
>
).map((row) => ({
id: row.id,
name: row.name,
serializedMapPool: MapPool.serialize(JSON.parse(row.mapPool)),
}));
}
const findAllEventsWithMapPoolsStm = sql.prepare(findAllEventsWithMapPoolsSql);
export function findAllEventsWithMapPools() {
return (
findAllEventsWithMapPoolsStm.all() as Array<
Pick<CalendarEvent, "id" | "name"> & {
mapPool: string;
}
>
).map((row) => ({
id: row.id,
name: row.name,
serializedMapPool: MapPool.serialize(JSON.parse(row.mapPool)),
}));
}
export function findTieBreakerMapPoolByEventId(
calendarEventId: string | number,
) {
return findTieBreakerMapPoolByEventIdtm.all({ calendarEventId }) as Array<
Pick<MapPoolMap, "mode" | "stageId">
>;
}
export const deleteById = sql.transaction(
({
eventId,
tournamentId,
}: {
eventId: number;
tournamentId: number | null;
}) => {
deleteByIdStm.run({ id: eventId });
if (tournamentId) deleteTournamentByIdStm.run({ id: tournamentId });
},
);

View File

@ -1,7 +0,0 @@
select
"CalendarEventDate"."startTime"
from
"CalendarEventDate"
where
"CalendarEventDate"."startTime" between @startTime
and @endTime

View File

@ -1,10 +0,0 @@
update
"CalendarEvent"
set
"name" = @name,
"tags" = @tags,
"description" = @description,
"discordInviteCode" = @discordInviteCode,
"bracketUrl" = @bracketUrl
where
"id" = @eventId returning *;

View File

@ -1,6 +0,0 @@
update
"CalendarEvent"
set
"participantCount" = @participantCount
where
"id" = @eventId

View File

@ -1,18 +0,0 @@
insert into
"PlusSuggestion" (
"text",
"authorId",
"suggestedId",
"month",
"year",
"tier"
)
values
(
@text,
@authorId,
@suggestedId,
@month,
@year,
@tier
)

View File

@ -1,4 +0,0 @@
delete from
"PlusSuggestion"
where
"id" = @id

View File

@ -1,7 +0,0 @@
delete from
"PlusSuggestion"
where
"suggestedId" = @suggestedId
and "tier" = @tier
and "month" = @month
and "year" = @year

View File

@ -1,7 +0,0 @@
delete from
"PlusSuggestion"
where
"month" = @month
and "year" = @year
and "suggestedId" = @suggestedId
and "tier" = @tier

View File

@ -1,25 +0,0 @@
select
suggestion."id",
suggestion."createdAt",
suggestion."text",
suggestion."tier",
author."id" as "authorId",
author."discordId" as "authorDiscordId",
author."discordName" as "authorDiscordName",
author."discordDiscriminator" as "authorDiscordDiscriminator",
suggested."id" as "suggestedId",
suggested."discordId" as "suggestedDiscordId",
suggested."discordName" as "suggestedDiscordName",
suggested."discordDiscriminator" as "suggestedDiscordDiscriminator",
suggested."discordAvatar" as "suggestedDiscordAvatar",
suggested."bio" as "suggestedBio"
from
"PlusSuggestion" as suggestion
join "User" as author on suggestion."authorId" = author."id"
join "User" as suggested on suggestion."suggestedId" = suggested."id"
where
"month" = @month
and "year" = @year
and "tier" >= @plusTier
order by
suggestion."id" asc

View File

@ -1,158 +0,0 @@
import { formatDistance } from "date-fns";
import type { MonthYear } from "~/modules/plus-server";
import { nextNonCompletedVoting } from "~/modules/plus-server";
import { atOrError } from "~/utils/arrays";
import { databaseTimestampToDate } from "~/utils/dates";
import { sql } from "../../sql";
import type { PlusSuggestion, User, UserWithPlusTier } from "../../types";
import createSql from "./create.sql";
import findVisibleForUserSql from "./findVisibleForUser.sql";
import tiersSuggestedForSql from "./tiersSuggestedFor.sql";
import deleteSql from "./delete.sql";
import deleteAllSql from "./deleteAll.sql";
import deleteSuggestionWithCommentsSql from "./deleteSuggestionWithComments.sql";
const createStm = sql.prepare(createSql);
export function create(
args: Pick<
PlusSuggestion,
"text" | "authorId" | "suggestedId" | "month" | "year" | "tier"
>,
) {
createStm.run(args);
}
const findVisibleForUserStm = sql.prepare(findVisibleForUserSql);
export interface FindVisibleForUserSuggestedUserInfo {
suggestedUser: Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
| "bio"
>;
suggestions: (Pick<PlusSuggestion, "id" | "text" | "createdAt"> & {
createdAtRelative: string;
author: Pick<
User,
"id" | "discordId" | "discordName" | "discordDiscriminator"
>;
})[];
}
export interface FindVisibleForUser {
[tier: string]: FindVisibleForUserSuggestedUserInfo[];
}
export function findVisibleForUser(
args: MonthYear &
Pick<UserWithPlusTier, "plusTier"> & { includeBio?: boolean },
): FindVisibleForUser | undefined {
if (typeof args.plusTier !== "number") return;
return sortNewestPlayersToBeSuggestedFirst(
mapFindVisibleForUserRowsToResult(
findVisibleForUserStm.all(args),
args.includeBio,
),
);
}
export function findAll(args: MonthYear & { includeBio?: boolean }) {
// plusTier 0 is a bit hacky way to get all suggestions
// while reusing the query
return findVisibleForUser({ ...args, plusTier: 0 })!;
}
function mapFindVisibleForUserRowsToResult(
rows: any[],
includeBio?: boolean,
): FindVisibleForUser {
return rows.reduce((result: FindVisibleForUser, row) => {
const usersOfTier = result[row.tier] ?? [];
result[row.tier] = usersOfTier;
const suggestionInfo = {
id: row.id,
createdAt: row.createdAt,
createdAtRelative: formatDistance(
databaseTimestampToDate(row.createdAt),
new Date(),
{ addSuffix: true },
),
text: row.text,
author: {
id: row.authorId,
discordId: row.authorDiscordId,
discordName: row.authorDiscordName,
discordDiscriminator: row.authorDiscordDiscriminator,
},
};
const existingSuggestion = usersOfTier.find(
(suggestion) => suggestion.suggestedUser.id === row.suggestedId,
);
if (existingSuggestion) {
existingSuggestion.suggestions.push(suggestionInfo);
} else {
usersOfTier.push({
suggestedUser: {
id: row.suggestedId,
discordId: row.suggestedDiscordId,
discordName: row.suggestedDiscordName,
discordDiscriminator: row.suggestedDiscordDiscriminator,
discordAvatar: row.suggestedDiscordAvatar,
bio: includeBio ? row.suggestedBio : null,
},
suggestions: [suggestionInfo],
});
}
return result;
}, {});
}
function sortNewestPlayersToBeSuggestedFirst(
suggestions: FindVisibleForUser,
): FindVisibleForUser {
return Object.fromEntries(
Object.entries(suggestions).map(([tier, suggestions]) => [
tier,
suggestions.sort(
(a, b) =>
atOrError(b.suggestions, 0).createdAt -
atOrError(a.suggestions, 0).createdAt,
),
]),
);
}
const tiersSuggestedForStm = sql.prepare(tiersSuggestedForSql);
export function tiersSuggestedFor(args: MonthYear & { userId: User["id"] }) {
return JSON.parse(
tiersSuggestedForStm.pluck().get(args) as any,
) as User["id"][];
}
const deleteStm = sql.prepare(deleteSql);
export function del(id: PlusSuggestion["id"]) {
deleteStm.run({ id });
}
const deleteAllStm = sql.prepare(deleteAllSql);
export function deleteAll(args: Pick<PlusSuggestion, "suggestedId" | "tier">) {
deleteAllStm.run({ ...args, ...nextNonCompletedVoting(new Date()) });
}
const deleteSuggestionWithCommentsStm = sql.prepare(
deleteSuggestionWithCommentsSql,
);
export function deleteSuggestionWithComments(
args: Pick<PlusSuggestion, "suggestedId" | "tier" | "month" | "year">,
) {
deleteSuggestionWithCommentsStm.run(args);
}

View File

@ -1,15 +0,0 @@
select
json_group_array("tier")
from
(
select
distinct "tier"
from
"PlusSuggestion"
where
"month" = @month
and "year" = @year
and "suggestedId" = @userId
order by
"tier" asc
)

View File

@ -1,20 +0,0 @@
insert into
"PlusVote" (
"month",
"year",
"tier",
"authorId",
"votedId",
"score",
"validAfter"
)
values
(
@month,
@year,
@tier,
@authorId,
@votedId,
@score,
@validAfter
)

View File

@ -1,6 +0,0 @@
delete from
"PlusVote"
where
"authorId" = @authorId
and "month" = @month
and "year" = @year

View File

@ -1,8 +0,0 @@
select
1
from
"PlusVote"
where
"authorId" = @userId
and "month" = @month
and "year" = @year

View File

@ -1,12 +0,0 @@
select
"User"."id",
"User"."discordId",
"User"."discordName",
"User"."discordDiscriminator",
"User"."discordAvatar",
"User"."bio"
from
"User"
join "PlusTier" on "User"."id" = "PlusTier"."userId"
where
"PlusTier"."tier" = @plusTier

View File

@ -1,215 +0,0 @@
import shuffle from "just-shuffle";
import invariant from "tiny-invariant";
import type { MonthYear } from "~/modules/plus-server";
import {
nextNonCompletedVoting,
rangeToMonthYear,
} from "~/modules/plus-server";
import { atOrError } from "~/utils/arrays";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import type { Unpacked } from "~/utils/types";
import { db } from "../..";
import { sql } from "../../sql";
import type {
PlusVote,
PlusVotingResult,
User,
UserWithPlusTier,
} from "../../types";
import type { FindVisibleForUserSuggestedUserInfo } from "../plusSuggestions/queries.server";
import createSql from "./create.sql";
import deleteManySql from "./deleteMany.sql";
import resultsByMonthYearSql from "./resultsByMonthYear.sql";
import plusServerMembersSql from "./plusServerMembers.sql";
import hasVotedSql from "./hasVoted.sql";
const createStm = sql.prepare(createSql);
const deleteManyStm = sql.prepare(deleteManySql);
export type UpsertManyPlusVotesArgs = (Pick<
PlusVote,
"month" | "year" | "tier" | "authorId" | "votedId" | "score"
> & { validAfter: Date })[];
export const upsertMany = sql.transaction((votes: UpsertManyPlusVotesArgs) => {
const firstVote = atOrError(votes, 0);
deleteManyStm.run({
authorId: firstVote.authorId,
month: firstVote.month,
year: firstVote.year,
});
for (const vote of votes) {
const { validAfter, ...rest } = vote;
createStm.run({
...rest,
validAfter: dateToDatabaseTimestamp(validAfter),
});
}
});
type PlusVotingResultsByMonthYearDatabaseResult = (PlusVotingResultUser &
Pick<PlusVotingResult, "score" | "wasSuggested" | "passedVoting" | "tier">)[];
const resultsByMonthYearStm = sql.prepare(resultsByMonthYearSql);
type PlusVotingResultUser = Pick<
User,
"id" | "discordAvatar" | "discordDiscriminator" | "discordName" | "discordId"
> &
Pick<PlusVotingResult, "wasSuggested">;
export interface PlusVotingResultByMonthYear {
results: {
tier: number;
passed: PlusVotingResultUser[];
failed: PlusVotingResultUser[];
}[];
scores: (Pick<
PlusVotingResult,
"score" | "tier" | "wasSuggested" | "passedVoting"
> & { userId: User["id"] })[];
}
export function resultsByMontYear(
args: MonthYear,
): PlusVotingResultByMonthYear {
const results = resultsByMonthYearStm.all(
args,
) as PlusVotingResultsByMonthYearDatabaseResult;
return {
results: groupPlusVotingResults(results),
scores: scoresFromPlusVotingResults(results),
};
}
function groupPlusVotingResults(
rows: PlusVotingResultsByMonthYearDatabaseResult,
): PlusVotingResultByMonthYear["results"] {
const grouped: Record<
number,
{ passed: PlusVotingResultUser[]; failed: PlusVotingResultUser[] }
> = {};
for (const row of rows) {
const playersOfTier = grouped[row.tier] ?? {
passed: [],
failed: [],
};
grouped[row.tier] = playersOfTier;
playersOfTier[row.passedVoting ? "passed" : "failed"].push({
id: row.id,
discordAvatar: row.discordAvatar,
discordDiscriminator: row.discordDiscriminator,
discordName: row.discordName,
discordId: row.discordId,
wasSuggested: row.wasSuggested,
});
}
return Object.entries(grouped)
.map(([tier, { passed, failed }]) => ({
tier: Number(tier),
passed,
failed,
}))
.sort((a, b) => a.tier - b.tier);
}
function scoresFromPlusVotingResults(
rows: PlusVotingResultsByMonthYearDatabaseResult,
) {
return rows
.map((row) => ({
userId: row.id,
tier: row.tier,
score: row.score,
wasSuggested: row.wasSuggested,
passedVoting: row.passedVoting,
}))
.sort((a, b) => a.tier - b.tier);
}
const plusServerMembersStm = sql.prepare(plusServerMembersSql);
export type UsersForVoting = {
user: Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
| "bio"
>;
suggestions?: FindVisibleForUserSuggestedUserInfo["suggestions"];
}[];
export function usersForVoting(
loggedInUser?: Pick<UserWithPlusTier, "id" | "plusTier">,
) {
if (!loggedInUser || !loggedInUser.plusTier) return;
const { month, year } = rangeToMonthYear(nextNonCompletedVoting(new Date()));
const members = plusServerMembersStm.all({
plusTier: loggedInUser.plusTier,
}) as Unpacked<UsersForVoting>["user"][];
const allSuggestedTiers = db.plusSuggestions.findVisibleForUser({
plusTier: loggedInUser.plusTier,
month,
year,
includeBio: true,
});
invariant(allSuggestedTiers);
const suggestedUsers = allSuggestedTiers[loggedInUser.plusTier] ?? [];
const result: UsersForVoting = [];
for (const member of members) {
result.push({
user: {
id: member.id,
discordId: member.discordId,
discordName: member.discordName,
discordDiscriminator: member.discordDiscriminator,
discordAvatar: member.discordAvatar,
bio: member.bio,
},
});
}
for (const { suggestedUser, suggestions } of suggestedUsers) {
result.push({
user: {
id: suggestedUser.id,
discordId: suggestedUser.discordId,
discordName: suggestedUser.discordName,
discordDiscriminator: suggestedUser.discordDiscriminator,
discordAvatar: suggestedUser.discordAvatar,
bio: suggestedUser.bio,
},
suggestions,
});
}
return shuffle(result.filter(({ user }) => user.id !== loggedInUser.id));
}
const hasVotedStm = sql.prepare(hasVotedSql);
export function hasVoted({
month,
year,
user,
}: MonthYear & { user?: Pick<User, "id"> }) {
if (!user) return false;
return Boolean(
hasVotedStm.get({
userId: user.id,
month,
year,
}),
);
}

View File

@ -1,18 +0,0 @@
select
"PlusVotingResult"."wasSuggested",
"PlusVotingResult"."passedVoting",
"PlusVotingResult"."tier",
"PlusVotingResult"."score",
"User"."id",
"User"."discordAvatar",
"User"."discordDiscriminator",
"User"."discordName",
"User"."discordId"
from
"PlusVotingResult"
join "User" on "PlusVotingResult"."votedId" = "User".id
where
"PlusVotingResult"."month" = @month
and "PlusVotingResult"."year" = @year
order by
"User"."discordName" collate nocase asc

View File

@ -1,8 +0,0 @@
update
"User"
set
"patronTier" = @patronTier,
"patronSince" = @patronSince,
"patronTill" = null
where
"discordId" = @discordId

View File

@ -1,10 +0,0 @@
insert into
"UserResultHighlight" (
"userId",
"teamId"
)
values
(
@userId,
@teamId
)

View File

@ -1,7 +0,0 @@
update
"TournamentResult"
set
"isHighlight" = 1
where
"userId" = @userId
and "tournamentTeamId" = @tournamentTeamId

View File

@ -1,4 +0,0 @@
insert into
"UserWeapon" ("userId", "weaponSplId", "order", "isFavorite")
values
(@userId, @weaponSplId, @order, @isFavorite);

View File

@ -1,8 +0,0 @@
update
"User"
set
"patronTier" = null,
"patronSince" = null,
"patronTill" = null
where
"patronTill" < @now;

View File

@ -1,4 +0,0 @@
delete from
"UserResultHighlight"
where
"userId" = @userId

View File

@ -1,6 +0,0 @@
update
"TournamentResult"
set
"isHighlight" = 0
where
"userId" = @userId

View File

@ -1,4 +0,0 @@
delete from
"User"
where
id = @id returning *

View File

@ -1,4 +0,0 @@
delete from
"UserWeapon"
where
"userId" = @userId;

View File

@ -1,9 +0,0 @@
insert into
"PlusTier" ("userId", "tier")
select
"userId",
"tier"
from
"FreshPlusTier"
where
"tier" is not null

View File

@ -1,9 +0,0 @@
select
"User"."id",
"User"."discordId",
"User"."discordName",
"User"."discordDiscriminator",
"PlusTier"."tier" as "plusTier"
from
"User"
left join "PlusTier" on "PlusTier"."userId" = "User"."id"

View File

@ -1,13 +0,0 @@
select
"id",
"discordId",
"discordName",
"discordDiscriminator",
"patronTier"
from
"User"
where
"patronTier" is not null
order by
"patronTier" desc,
"patronSince" asc

View File

@ -1,8 +0,0 @@
select
"User"."discordId",
"PlusTier"."tier" as "plusTier"
from
"User"
left join "PlusTier" on "PlusTier"."userId" = "User"."id"
where
"PlusTier"."tier" is not null

View File

@ -1,28 +0,0 @@
select
"User".*,
"Team"."name" as "teamName",
"Team"."customUrl" as "teamCustomUrl",
"Team"."id" as "teamId",
"UserSubmittedImage"."url" as "teamAvatarUrl",
"PlusTier"."tier" as "plusTier",
json_group_array(
json_object(
'weaponSplId',
"UserWeapon"."weaponSplId",
'isFavorite',
"UserWeapon"."isFavorite"
)
) as "weapons"
from
"User"
left join "PlusTier" on "PlusTier"."userId" = "User"."id"
left join "UserWeapon" on "UserWeapon"."userId" = "User"."id"
left join "TeamMember" on "TeamMember"."userId" = "User"."id"
left join "Team" on "Team"."id" = "TeamMember"."teamId"
left join "UserSubmittedImage" on "Team"."avatarImgId" = "UserSubmittedImage"."id"
where
"User"."discordId" = @identifier
or "User"."id" = @identifier
or "User"."customUrl" = @identifier
order by
"UserWeapon"."order" asc

View File

@ -1,8 +0,0 @@
update
User
set
"patronTier" = @patronTier,
"patronSince" = @patronSince,
"patronTill" = @patronTill
where
"id" = @id;

View File

@ -1,6 +0,0 @@
update
"SplatoonPlayer"
set
"userId" = @userId
where
"id" = @playerId

View File

@ -1,6 +0,0 @@
update
"User"
set
"isVideoAdder" = 1
where
"id" = @id

View File

@ -1,349 +0,0 @@
import { sql } from "../../sql";
import type {
CalendarEventResultTeam,
SplatoonPlayer,
TournamentTeam,
User,
UserWeapon,
UserWithPlusTier,
} from "../../types";
import { parseDBJsonArray } from "~/utils/sql";
import { dateToDatabaseTimestamp } from "~/utils/dates";
import addPatronDataSql from "./addPatronData.sql";
import addResultHighlightSql from "./addResultHighlight.sql";
import addTournamentResultHighlightSql from "./addTournamentResultHighlight.sql";
import deleteAllPatronDataSql from "./deleteAllPatronData.sql";
import deleteAllResultHighlightsSql from "./deleteAllResultHighlights.sql";
import deleteAllTournamentResultHighlightsSql from "./deleteAllTournamentResultHighlights.sql";
import deleteByIdSql from "./deleteById.sql";
import findAllSql from "./findAll.sql";
import findAllPatronsSql from "./findAllPatrons.sql";
import findAllPlusMembersSql from "./findAllPlusMembers.sql";
import findByIdentifierSql from "./findByIdentifier.sql";
import searchSql from "./search.sql";
import searchExactSql from "./searchExact.sql";
import updateByDiscordIdSql from "./updateByDiscordId.sql";
import updateDiscordIdSql from "./updateDiscordId.sql";
import updateProfileSql from "./updateProfile.sql";
import upsertSql from "./upsert.sql";
import upsertLiteSql from "./upsertLite.sql";
import addUserWeaponSql from "./addUserWeapon.sql";
import deleteUserWeaponsSql from "./deleteUserWeapons.sql";
import wipePlusTiersSql from "./wipePlusTiers.sql";
import fillPlusTiersSql from "./fillPlusTiers.sql";
import forcePatronSql from "./forcePatron.sql";
import makeVideoAdderSql from "./makeVideoAdder.sql";
import linkPlayerSql from "./linkPlayer.sql";
import unlinkPlayerSql from "./unlinkPlayer.sql";
import { syncXPBadges } from "~/features/badges";
const upsertStm = sql.prepare(upsertSql);
export function upsert(
input: Pick<
User,
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
| "twitch"
| "twitter"
| "youtubeId"
| "discordUniqueName"
>,
) {
return upsertStm.get(input) as User;
}
const upsertLiteStm = sql.prepare(upsertLiteSql);
export function upsertLite(
input: Pick<
User,
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
| "discordUniqueName"
>,
) {
return upsertLiteStm.get(input) as User;
}
const updateProfileStm = sql.prepare(updateProfileSql);
const addUserWeaponStm = sql.prepare(addUserWeaponSql);
const deleteUserWeaponsStm = sql.prepare(deleteUserWeaponsSql);
export const updateProfile = sql.transaction(
({
weapons,
...rest
}: Pick<
User,
| "country"
| "id"
| "bio"
| "customUrl"
| "motionSens"
| "stickSens"
| "inGameName"
| "css"
| "favoriteBadgeId"
| "showDiscordUniqueName"
| "commissionText"
| "commissionsOpen"
> & { weapons: Pick<UserWeapon, "weaponSplId" | "isFavorite">[] }) => {
deleteUserWeaponsStm.run({ userId: rest.id });
for (const [i, weapon] of weapons.entries()) {
addUserWeaponStm.run({
userId: rest.id,
weaponSplId: weapon.weaponSplId,
isFavorite: weapon.isFavorite,
order: i + 1,
});
}
return updateProfileStm.get(rest) as User;
},
);
const updateByDiscordIdStm = sql.prepare(updateByDiscordIdSql);
export const updateMany = sql.transaction(
(
argsArr: Array<
Pick<
User,
"discordAvatar" | "discordName" | "discordUniqueName" | "discordId"
>
>,
) => {
for (const updateArgs of argsArr) {
updateByDiscordIdStm.run(updateArgs);
}
},
);
const deleteAllPatronDataStm = sql.prepare(deleteAllPatronDataSql);
const addPatronDataStm = sql.prepare(addPatronDataSql);
export type UpdatePatronDataArgs = Array<
Pick<User, "discordId" | "patronTier" | "patronSince">
>;
export const updatePatronData = sql.transaction(
(argsArr: UpdatePatronDataArgs) => {
deleteAllPatronDataStm.run({ now: dateToDatabaseTimestamp(new Date()) });
for (const args of argsArr) {
addPatronDataStm.run(args);
}
},
);
const deleteByIdStm = sql.prepare(deleteByIdSql);
const updateDiscordIdStm = sql.prepare(updateDiscordIdSql);
export const migrate = sql.transaction(
(args: { newUserId: User["id"]; oldUserId: User["id"] }) => {
const deletedUser = deleteByIdStm.get({ id: args.newUserId }) as User;
updateDiscordIdStm.run({
id: args.oldUserId,
discordId: deletedUser.discordId,
});
},
);
const findByIdentifierStm = sql.prepare(findByIdentifierSql);
export function findByIdentifier(identifier: string | number) {
const {
weapons,
teamName,
teamCustomUrl,
teamAvatarUrl,
teamId,
css,
...row
} = findByIdentifierStm.get({ identifier }) as any;
if (!row.id) return;
return {
...row,
css: css ? JSON.parse(css) : undefined,
weapons: parseDBJsonArray(weapons),
team: teamName
? {
name: teamName,
customUrl: teamCustomUrl,
avatarUrl: teamAvatarUrl,
id: teamId,
}
: undefined,
} as
| (Omit<UserWithPlusTier, "css"> & {
css: Record<string, string>;
weapons: Pick<UserWeapon, "weaponSplId" | "isFavorite">[];
team?: {
name: string;
customUrl: string;
avatarUrl?: string;
id: number;
};
})
| undefined;
}
const findAllStm = sql.prepare(findAllSql);
export function findAll() {
return findAllStm.all() as Pick<
UserWithPlusTier,
"id" | "discordId" | "discordName" | "discordDiscriminator" | "plusTier"
>[];
}
const findAllPlusMembersStm = sql.prepare(findAllPlusMembersSql);
export function findAllPlusMembers() {
return findAllPlusMembersStm.all() as Array<{
discordId: User["discordId"];
plusTier: NonNullable<UserWithPlusTier["plusTier"]>;
}>;
}
const findAllPatronsStm = sql.prepare(findAllPatronsSql);
export type FindAllPatrons = Array<
Pick<
User,
"id" | "discordId" | "discordName" | "discordDiscriminator" | "patronTier"
>
>;
export function findAllPatrons() {
return findAllPatronsStm.all() as FindAllPatrons;
}
const forcePatronStm = sql.prepare(forcePatronSql);
export function forcePatron(
user: Pick<User, "id" | "patronSince" | "patronTill" | "patronTier">,
) {
forcePatronStm.run(user);
}
const deleteAllResultHighlightsStm = sql.prepare(deleteAllResultHighlightsSql);
const deleteAllTournamentResultHighlightsStm = sql.prepare(
deleteAllTournamentResultHighlightsSql,
);
const addResultHighlightStm = sql.prepare(addResultHighlightSql);
const addTournamentResultHighlightStm = sql.prepare(
addTournamentResultHighlightSql,
);
export type UpdateResultHighlightsArgs = {
userId: User["id"];
resultTeamIds: Array<CalendarEventResultTeam["id"]>;
resultTournamentTeamIds: Array<TournamentTeam["id"]>;
};
export const updateResultHighlights = sql.transaction(
({
userId,
resultTeamIds,
resultTournamentTeamIds,
}: UpdateResultHighlightsArgs) => {
deleteAllResultHighlightsStm.run({ userId });
deleteAllTournamentResultHighlightsStm.run({ userId });
for (const teamId of resultTeamIds) {
addResultHighlightStm.run({ userId, teamId });
}
for (const tournamentTeamId of resultTournamentTeamIds) {
addTournamentResultHighlightStm.run({ userId, tournamentTeamId });
}
},
);
const searchStm = sql.prepare(searchSql);
export function search({ input, limit }: { input: string; limit: number }) {
const searchString = `%${input}%`;
return (
searchStm.all({
discordName: searchString,
inGameName: searchString,
discordUniqueName: searchString,
twitter: searchString,
limit,
}) as Array<
Pick<
UserWithPlusTier,
| "id"
| "discordId"
| "discordAvatar"
| "discordName"
| "discordDiscriminator"
| "customUrl"
| "inGameName"
| "discordUniqueName"
| "showDiscordUniqueName"
| "plusTier"
>
>
).map((user) => ({
...user,
discordUniqueName: user.showDiscordUniqueName
? user.discordUniqueName
: undefined,
}));
}
const searchExactStm = sql.prepare(searchExactSql);
export function searchExact(args: {
discordId?: User["discordId"];
customUrl?: User["customUrl"];
id?: User["id"];
}) {
return (
searchExactStm.all({
discordId: args.discordId ?? null,
customUrl: args.customUrl ?? null,
id: args.id ?? null,
}) as Array<
Pick<
UserWithPlusTier,
| "id"
| "discordId"
| "discordAvatar"
| "discordName"
| "discordDiscriminator"
| "customUrl"
| "inGameName"
| "discordUniqueName"
| "showDiscordUniqueName"
| "plusTier"
>
>
).map((user) => ({
...user,
discordUniqueName: user.showDiscordUniqueName
? user.discordUniqueName
: undefined,
}));
}
const wipePlusTiersStm = sql.prepare(wipePlusTiersSql);
const fillPlusTiersStm = sql.prepare(fillPlusTiersSql);
export const refreshPlusTiers = sql.transaction(() => {
wipePlusTiersStm.run();
fillPlusTiersStm.run();
});
const makeVideoAdderStm = sql.prepare(makeVideoAdderSql);
export function makeVideoAdder(id: User["id"]) {
return makeVideoAdderStm.run({ id });
}
const linkPlayerStm = sql.prepare(linkPlayerSql);
const unlinkPlayerStm = sql.prepare(unlinkPlayerSql);
export function linkPlayer({
userId,
playerId,
}: {
userId: User["id"];
playerId: SplatoonPlayer["id"];
}) {
unlinkPlayerStm.run({ userId });
linkPlayerStm.run({ userId, playerId });
syncXPBadges();
}

View File

@ -1,26 +0,0 @@
select
"id",
"discordName",
"discordId",
"discordAvatar",
"discordDiscriminator",
"discordUniqueName",
"showDiscordUniqueName",
"customUrl",
"inGameName",
"PlusTier"."tier" as "plusTier"
from
"User"
left join "PlusTier" on "PlusTier"."userId" = "User"."id"
where
"discordName" like @discordName
or "inGameName" like @inGameName
or "discordUniqueName" like @discordUniqueName
or "twitter" like @twitter
order by
case
when "PlusTier"."tier" is null then 4
else "PlusTier"."tier"
end asc
limit
@limit

View File

@ -1,18 +0,0 @@
select
"id",
"discordName",
"discordId",
"discordAvatar",
"discordDiscriminator",
"discordUniqueName",
"showDiscordUniqueName",
"customUrl",
"inGameName",
"PlusTier"."tier" as "plusTier"
from
"User"
left join "PlusTier" on "PlusTier"."userId" = "User"."id"
where
"discordId" = @discordId
or "customUrl" = @customUrl
or "id" = @id

View File

@ -1,6 +0,0 @@
update
"SplatoonPlayer"
set
"userId" = null
where
"userId" = @userId

View File

@ -1,8 +0,0 @@
update
"User"
set
"discordAvatar" = @discordAvatar,
"discordName" = coalesce(@discordName, "discordName"),
"discordUniqueName" = coalesce(@discordUniqueName, "discordUniqueName")
where
"discordId" = @discordId

View File

@ -1,6 +0,0 @@
update
"User"
set
"discordId" = @discordId
where
"id" = @id

View File

@ -1,16 +0,0 @@
update
"User"
set
"country" = @country,
"bio" = @bio,
"customUrl" = @customUrl,
"stickSens" = @stickSens,
"motionSens" = @motionSens,
"inGameName" = @inGameName,
"css" = @css,
"favoriteBadgeId" = @favoriteBadgeId,
"showDiscordUniqueName" = @showDiscordUniqueName,
"commissionsOpen" = @commissionsOpen,
"commissionText" = @commissionText
where
"id" = @id returning *

View File

@ -1,31 +0,0 @@
insert into
"User" (
"discordId",
"discordName",
"discordDiscriminator",
"discordAvatar",
"discordUniqueName",
"twitch",
"twitter",
"youtubeId"
)
values
(
@discordId,
@discordName,
@discordDiscriminator,
@discordAvatar,
@discordUniqueName,
@twitch,
@twitter,
@youtubeId
) on conflict("discordId") do
update
set
"discordName" = excluded."discordName",
"discordDiscriminator" = excluded."discordDiscriminator",
"discordAvatar" = excluded."discordAvatar",
"discordUniqueName" = excluded."discordUniqueName",
"twitch" = excluded."twitch",
"twitter" = excluded."twitter",
"youtubeId" = excluded."youtubeId" returning *

View File

@ -1,22 +0,0 @@
insert into
"User" (
"discordId",
"discordName",
"discordDiscriminator",
"discordAvatar",
"discordUniqueName"
)
values
(
@discordId,
@discordName,
@discordDiscriminator,
@discordAvatar,
@discordUniqueName
) on conflict("discordId") do
update
set
"discordName" = excluded."discordName",
"discordDiscriminator" = excluded."discordDiscriminator",
"discordAvatar" = excluded."discordAvatar",
"discordUniqueName" = excluded."discordUniqueName" returning *

Some files were not shown because too many files have changed in this diff Show More