feat: implement character overrides and update daily character retrieval logic
This commit is contained in:
30
drizzle/0001_nostalgic_hercules.sql
Normal file
30
drizzle/0001_nostalgic_hercules.sql
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
PRAGMA foreign_keys=OFF;--> statement-breakpoint
|
||||||
|
CREATE TABLE `__new_characterOverride` (
|
||||||
|
`characterId` text PRIMARY KEY NOT NULL,
|
||||||
|
`name` text,
|
||||||
|
`gender` text,
|
||||||
|
`age` integer,
|
||||||
|
`affiliations` text,
|
||||||
|
`devilFruitId` text,
|
||||||
|
`hakiObservation` integer,
|
||||||
|
`hakiArmament` integer,
|
||||||
|
`hakiConqueror` integer,
|
||||||
|
`bounty` integer,
|
||||||
|
`height` real,
|
||||||
|
`origin` text,
|
||||||
|
`firstAppearance` integer,
|
||||||
|
`pictureUrl` text,
|
||||||
|
`epithets` text,
|
||||||
|
`status` text,
|
||||||
|
`arcId` text,
|
||||||
|
`url` text,
|
||||||
|
`notes` text,
|
||||||
|
FOREIGN KEY (`characterId`) REFERENCES `character`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`devilFruitId`) REFERENCES `devilFruit`(`id`) ON UPDATE no action ON DELETE no action,
|
||||||
|
FOREIGN KEY (`arcId`) REFERENCES `arc`(`id`) ON UPDATE no action ON DELETE no action
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
INSERT INTO `__new_characterOverride`("characterId", "name", "gender", "age", "affiliations", "devilFruitId", "hakiObservation", "hakiArmament", "hakiConqueror", "bounty", "height", "origin", "firstAppearance", "pictureUrl", "epithets", "status", "arcId", "url", "notes") SELECT "characterId", "name", "gender", "age", "affiliations", "devilFruitId", "hakiObservation", "hakiArmament", "hakiConqueror", "bounty", "height", "origin", "firstAppearance", "pictureUrl", "epithets", "status", "arcId", "url", "notes" FROM `characterOverride`;--> statement-breakpoint
|
||||||
|
DROP TABLE `characterOverride`;--> statement-breakpoint
|
||||||
|
ALTER TABLE `__new_characterOverride` RENAME TO `characterOverride`;--> statement-breakpoint
|
||||||
|
PRAGMA foreign_keys=ON;
|
||||||
1076
drizzle/meta/0001_snapshot.json
Normal file
1076
drizzle/meta/0001_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,13 @@
|
|||||||
"when": 1772325597983,
|
"when": 1772325597983,
|
||||||
"tag": "0000_graceful_master_mold",
|
"tag": "0000_graceful_master_mold",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 1,
|
||||||
|
"version": "6",
|
||||||
|
"when": 1772383366179,
|
||||||
|
"tag": "0001_nostalgic_hercules",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { db } from '$lib/server/db';
|
import { db } from '$lib/server/db';
|
||||||
import { arc, character, characterHistory, devilFruit } from '$lib/server/db/schema';
|
import { arc, character, characterHistory, characterOverride, devilFruit } from '$lib/server/db/schema';
|
||||||
import { desc, eq } from 'drizzle-orm';
|
import { desc, eq, inArray } from 'drizzle-orm';
|
||||||
|
|
||||||
const characterWithRelationsSelect = {
|
const characterWithRelationsSelect = {
|
||||||
id: character.id,
|
id: character.id,
|
||||||
@@ -32,6 +32,113 @@ export type CharacterWithRelations = typeof character.$inferSelect & {
|
|||||||
arcName: string | null;
|
arcName: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type CharacterOverrideRow = typeof characterOverride.$inferSelect;
|
||||||
|
|
||||||
|
type RelationMaps = {
|
||||||
|
arcNameById: Map<string, string | null>;
|
||||||
|
devilFruitById: Map<string, { name: string | null; type: string | null }>;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isNotNullish<T>(value: T | null | undefined): value is T {
|
||||||
|
return value !== null && value !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeCharacterWithOverride(
|
||||||
|
baseCharacter: CharacterWithRelations,
|
||||||
|
overrideRow?: CharacterOverrideRow,
|
||||||
|
relationMaps?: RelationMaps
|
||||||
|
): CharacterWithRelations {
|
||||||
|
if (!overrideRow) {
|
||||||
|
return baseCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mergedCharacter = { ...baseCharacter } as CharacterWithRelations;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(overrideRow)) {
|
||||||
|
if (key === 'characterId' || key === 'notes') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotNullish(value)) {
|
||||||
|
(mergedCharacter as Record<string, unknown>)[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relationMaps) {
|
||||||
|
if (mergedCharacter.arcId) {
|
||||||
|
mergedCharacter.arcName = relationMaps.arcNameById.get(mergedCharacter.arcId) ?? null;
|
||||||
|
} else {
|
||||||
|
mergedCharacter.arcName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mergedCharacter.devilFruitId) {
|
||||||
|
const devilFruitData = relationMaps.devilFruitById.get(mergedCharacter.devilFruitId);
|
||||||
|
mergedCharacter.devilFruitName = devilFruitData?.name ?? null;
|
||||||
|
mergedCharacter.devilFruitType = devilFruitData?.type ?? null;
|
||||||
|
} else {
|
||||||
|
mergedCharacter.devilFruitName = null;
|
||||||
|
mergedCharacter.devilFruitType = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mergedCharacter;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyCharacterOverrides(
|
||||||
|
characters: CharacterWithRelations[]
|
||||||
|
): Promise<CharacterWithRelations[]> {
|
||||||
|
if (characters.length === 0) {
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const characterIds = characters.map((currentCharacter) => currentCharacter.id);
|
||||||
|
const overrideRows = await db
|
||||||
|
.select()
|
||||||
|
.from(characterOverride)
|
||||||
|
.where(inArray(characterOverride.characterId, characterIds));
|
||||||
|
|
||||||
|
if (overrideRows.length === 0) {
|
||||||
|
return characters;
|
||||||
|
}
|
||||||
|
|
||||||
|
const overrideByCharacterId = new Map<string, CharacterOverrideRow>(
|
||||||
|
overrideRows.map((overrideRow) => [overrideRow.characterId, overrideRow])
|
||||||
|
);
|
||||||
|
|
||||||
|
const shouldRefreshRelations = overrideRows.some(
|
||||||
|
(overrideRow) => isNotNullish(overrideRow.arcId) || isNotNullish(overrideRow.devilFruitId)
|
||||||
|
);
|
||||||
|
|
||||||
|
let relationMaps: RelationMaps | undefined;
|
||||||
|
|
||||||
|
if (shouldRefreshRelations) {
|
||||||
|
const [allArcs, allDevilFruits] = await Promise.all([
|
||||||
|
db.select({ id: arc.id, name: arc.name }).from(arc),
|
||||||
|
db
|
||||||
|
.select({ id: devilFruit.id, name: devilFruit.name, type: devilFruit.type })
|
||||||
|
.from(devilFruit)
|
||||||
|
]);
|
||||||
|
|
||||||
|
relationMaps = {
|
||||||
|
arcNameById: new Map(allArcs.map((currentArc) => [currentArc.id, currentArc.name])),
|
||||||
|
devilFruitById: new Map(
|
||||||
|
allDevilFruits.map((currentDevilFruit) => [
|
||||||
|
currentDevilFruit.id,
|
||||||
|
{ name: currentDevilFruit.name, type: currentDevilFruit.type }
|
||||||
|
])
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return characters.map((currentCharacter) =>
|
||||||
|
mergeCharacterWithOverride(
|
||||||
|
currentCharacter,
|
||||||
|
overrideByCharacterId.get(currentCharacter.id),
|
||||||
|
relationMaps
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function getDateKey(date: Date): string {
|
function getDateKey(date: Date): string {
|
||||||
return date.toISOString().split('T')[0];
|
return date.toISOString().split('T')[0];
|
||||||
}
|
}
|
||||||
@@ -50,13 +157,15 @@ function pickDailyCharacter(characters: CharacterWithRelations[], date: Date): C
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function getDailyModeCharacters(): Promise<CharacterWithRelations[]> {
|
export async function getDailyModeCharacters(): Promise<CharacterWithRelations[]> {
|
||||||
return db
|
const characters = (await db
|
||||||
.select(characterWithRelationsSelect)
|
.select(characterWithRelationsSelect)
|
||||||
.from(character)
|
.from(character)
|
||||||
.leftJoin(arc, eq(character.arcId, arc.id))
|
.leftJoin(arc, eq(character.arcId, arc.id))
|
||||||
.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
|
.leftJoin(devilFruit, eq(character.devilFruitId, devilFruit.id))
|
||||||
.where(eq(character.isInDailyMode, true))
|
.where(eq(character.isInDailyMode, true))
|
||||||
.all() as Promise<CharacterWithRelations[]>;
|
.all()) as CharacterWithRelations[];
|
||||||
|
|
||||||
|
return applyCharacterOverrides(characters);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCharacterById(characterId: string): Promise<CharacterWithRelations | null> {
|
export async function getCharacterById(characterId: string): Promise<CharacterWithRelations | null> {
|
||||||
@@ -68,7 +177,12 @@ export async function getCharacterById(characterId: string): Promise<CharacterWi
|
|||||||
.where(eq(character.id, characterId))
|
.where(eq(character.id, characterId))
|
||||||
.limit(1);
|
.limit(1);
|
||||||
|
|
||||||
return (found ?? null) as CharacterWithRelations | null;
|
if (!found) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [overriddenCharacter] = await applyCharacterOverrides([found as CharacterWithRelations]);
|
||||||
|
return overriddenCharacter ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOrCreateTodayCharacter(
|
export async function getOrCreateTodayCharacter(
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export const characterOverride = sqliteTable('characterOverride', {
|
|||||||
bounty: integer('bounty'),
|
bounty: integer('bounty'),
|
||||||
height: real('height'),
|
height: real('height'),
|
||||||
origin: text('origin'),
|
origin: text('origin'),
|
||||||
firstAppearance: integer('firstAppearance').notNull(),
|
firstAppearance: integer('firstAppearance'),
|
||||||
pictureUrl: text('pictureUrl'),
|
pictureUrl: text('pictureUrl'),
|
||||||
epithets: text('epithets', { mode: 'json' }).$type<string[]>(),
|
epithets: text('epithets', { mode: 'json' }).$type<string[]>(),
|
||||||
status: text('status'),
|
status: text('status'),
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
<div class="absolute inset-0 bg-gradient-to-br from-slate-950/85 via-slate-900/60 to-slate-950/80"></div>
|
<div class="absolute inset-0 bg-gradient-to-br from-slate-950/85 via-slate-900/60 to-slate-950/80"></div>
|
||||||
<div class="absolute inset-0 mix-blend-screen opacity-20 bg-[radial-gradient(circle_at_top,rgba(255,215,84,0.35),transparent_55%)]"></div>
|
<div class="absolute inset-0 mix-blend-screen opacity-20 bg-[radial-gradient(circle_at_top,rgba(255,215,84,0.35),transparent_55%)]"></div>
|
||||||
|
|
||||||
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col items-center justify-between px-6 py-10">
|
<div class="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col items-center justify-center px-6 py-10">
|
||||||
<div class="flex w-full flex-1 flex-col items-center justify-between gap-12">
|
<div class="flex w-full flex-col items-center gap-8">
|
||||||
<div class="text-center">
|
<div class="text-center mb-12">
|
||||||
<h1 class="text-4xl font-black uppercase tracking-[0.3em] text-amber-50 sm:text-6xl">
|
<h1 class="text-4xl font-black uppercase tracking-[0.3em] text-amber-50 sm:text-6xl">
|
||||||
OnePieceDle
|
OnePieceDle
|
||||||
</h1>
|
</h1>
|
||||||
|
|||||||
Reference in New Issue
Block a user