sendou.ink/app/features/match-profile/MatchProfileRepository.server.ts
2026-07-29 10:52:57 +03:00

115 lines
2.9 KiB
TypeScript

import * as R from "remeda";
import { db } from "~/db/sql";
import type { DBBoolean, Tables } from "~/db/tables";
import type { UserMapModePreferences } from "~/db/tables-json";
import { actorId } from "~/features/auth/core/user.server";
import type { WeaponPoolItem } from "~/form/fields/WeaponPoolFormField";
import type { UnifiedLanguageCode } from "~/modules/i18n/config";
import { modesShort } from "~/modules/in-game-lists/modes";
import { matchProfileWeapons } from "~/utils/kysely.server";
import { toDBBoolean } from "~/utils/sql";
export function findSettingsByUserId(userId: number) {
return db
.selectFrom("User")
.select(({ eb }) => [
"User.mapModePreferences",
"User.vc",
"User.languages",
"User.noScreen",
matchProfileWeapons(eb).as("weaponPool"),
])
.where("id", "=", userId)
.executeTakeFirstOrThrow();
}
export async function updateOwnMatchProfile({
mapModePreferences,
vc,
languages,
weaponPool,
noScreen,
}: {
mapModePreferences: UserMapModePreferences;
vc: Tables["User"]["vc"];
languages: UnifiedLanguageCode[];
weaponPool: WeaponPoolItem[];
noScreen: DBBoolean;
}) {
const userId = actorId();
const current = await db
.selectFrom("User")
.select(["mapModePreferences", "noScreen"])
.where("id", "=", userId)
.executeTakeFirstOrThrow();
const mergedPool = mergeExcludedModePreferences(
mapModePreferences.pool,
current.mapModePreferences?.pool,
);
const newMapModePreferences: UserMapModePreferences = {
...mapModePreferences,
pool: mergedPool,
};
const mapModePreferencesChanged = !R.isDeepEqual(
newMapModePreferences,
current.mapModePreferences,
);
const noScreenChanged = current.noScreen !== noScreen;
await db.transaction().execute(async (trx) => {
await trx
.deleteFrom("UserWeaponPool")
.where("userId", "=", userId)
.execute();
if (weaponPool.length > 0) {
await trx
.insertInto("UserWeaponPool")
.values(
weaponPool.map((wpn, i) => ({
userId,
sortOrder: i,
weaponSplId: wpn.id,
isFavorite: toDBBoolean(wpn.isFavorite),
})),
)
.execute();
}
await trx
.updateTable("User")
.set({
mapModePreferences: JSON.stringify(newMapModePreferences),
vc,
languages: languages.length > 0 ? JSON.stringify(languages) : null,
noScreen,
})
.where("id", "=", userId)
.execute();
});
return { mapModePreferencesChanged, noScreenChanged };
}
/**
* Preserves existing preferences for modes not included in the new submission.
* So if they later want to play this mode again, the system remembers their maps.
*/
function mergeExcludedModePreferences(
newPool: UserMapModePreferences["pool"],
currentPool: UserMapModePreferences["pool"] | undefined,
) {
const modesExcluded = modesShort.filter(
(mode) => !newPool.some((mp) => mp.mode === mode),
);
const preservedPreferences = modesExcluded.flatMap(
(mode) => currentPool?.filter((mp) => mp.mode === mode) ?? [],
);
return [...newPool, ...preservedPreferences];
}