refactor: improve type definitions and enhance state management in profile and daily components
This commit is contained in:
@@ -1,23 +1,96 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import YesterdayCharacter from '$lib/components/YesterdayCharacter.svelte';
|
||||
import HintsPanel from '$lib/components/HintsPanel.svelte';
|
||||
import CharacterSearchInput from '$lib/components/CharacterSearchInput.svelte';
|
||||
import GuessHistoryTable from '$lib/components/GuessHistoryTable.svelte';
|
||||
import WinPanel from '$lib/components/WinPanel.svelte';
|
||||
import type { CharacterWithRelations } from '$lib/server/daily-character.js';
|
||||
|
||||
export let data;
|
||||
|
||||
let selectedCharacters: any[] = [];
|
||||
let selectedCharacters: CharacterWithRelations[] = [];
|
||||
let isLoaded = false;
|
||||
let isGeckoMoriaWin = false;
|
||||
|
||||
let wasOriginAvailable = false;
|
||||
let wasFruitAvailable = false;
|
||||
let wasAffiliationAvailable = false;
|
||||
let showOriginUnlock = false;
|
||||
let showFruitUnlock = false;
|
||||
let showAffiliationUnlock = false;
|
||||
let originUnlockTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let fruitUnlockTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
let affiliationUnlockTimeout: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
function clearUnlockTimeout(timeout: ReturnType<typeof setTimeout> | null) {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
function pulseUnlock(type: 'origin' | 'fruit' | 'affiliation') {
|
||||
if (type === 'origin') {
|
||||
clearUnlockTimeout(originUnlockTimeout);
|
||||
showOriginUnlock = true;
|
||||
originUnlockTimeout = setTimeout(() => {
|
||||
showOriginUnlock = false;
|
||||
originUnlockTimeout = null;
|
||||
}, 600);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type === 'fruit') {
|
||||
clearUnlockTimeout(fruitUnlockTimeout);
|
||||
showFruitUnlock = true;
|
||||
fruitUnlockTimeout = setTimeout(() => {
|
||||
showFruitUnlock = false;
|
||||
fruitUnlockTimeout = null;
|
||||
}, 600);
|
||||
return;
|
||||
}
|
||||
|
||||
clearUnlockTimeout(affiliationUnlockTimeout);
|
||||
showAffiliationUnlock = true;
|
||||
affiliationUnlockTimeout = setTimeout(() => {
|
||||
showAffiliationUnlock = false;
|
||||
affiliationUnlockTimeout = null;
|
||||
}, 600);
|
||||
}
|
||||
|
||||
function syncHintAvailability(previousGuessCount: number, nextGuessCount: number, animateUnlocks = false) {
|
||||
const nextOriginAvailable = nextGuessCount >= 5;
|
||||
const nextFruitAvailable = nextGuessCount >= 10;
|
||||
const nextAffiliationAvailable = nextGuessCount >= 15;
|
||||
|
||||
if (animateUnlocks && nextOriginAvailable && previousGuessCount < 5) {
|
||||
pulseUnlock('origin');
|
||||
}
|
||||
|
||||
if (animateUnlocks && nextFruitAvailable && previousGuessCount < 10) {
|
||||
pulseUnlock('fruit');
|
||||
}
|
||||
|
||||
if (animateUnlocks && nextAffiliationAvailable && previousGuessCount < 15) {
|
||||
pulseUnlock('affiliation');
|
||||
}
|
||||
|
||||
if (!nextOriginAvailable) {
|
||||
showOriginUnlock = false;
|
||||
clearUnlockTimeout(originUnlockTimeout);
|
||||
originUnlockTimeout = null;
|
||||
}
|
||||
|
||||
if (!nextFruitAvailable) {
|
||||
showFruitUnlock = false;
|
||||
clearUnlockTimeout(fruitUnlockTimeout);
|
||||
fruitUnlockTimeout = null;
|
||||
}
|
||||
|
||||
if (!nextAffiliationAvailable) {
|
||||
showAffiliationUnlock = false;
|
||||
clearUnlockTimeout(affiliationUnlockTimeout);
|
||||
affiliationUnlockTimeout = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Load from localStorage on mount
|
||||
onMount(() => {
|
||||
@@ -37,8 +110,8 @@
|
||||
// Reconstruct character objects from IDs
|
||||
if (Array.isArray(storedIds)) {
|
||||
selectedCharacters = storedIds
|
||||
.map((id: string) => data.characters.find((c: any) => c.id === id))
|
||||
.filter((c: any) => c !== undefined);
|
||||
.map((id: string) => data.characters.find((c: CharacterWithRelations) => c.id === id))
|
||||
.filter((c: CharacterWithRelations | undefined): c is CharacterWithRelations => !!c);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Failed to parse stored history', e);
|
||||
@@ -50,10 +123,18 @@
|
||||
if (dailyCurrentCharacterId) {
|
||||
localStorage.setItem('dailyCurrentCharacterId', dailyCurrentCharacterId);
|
||||
}
|
||||
|
||||
syncHintAvailability(0, selectedCharacters.length);
|
||||
|
||||
isLoaded = true;
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
clearUnlockTimeout(originUnlockTimeout);
|
||||
clearUnlockTimeout(fruitUnlockTimeout);
|
||||
clearUnlockTimeout(affiliationUnlockTimeout);
|
||||
});
|
||||
|
||||
// Save to localStorage whenever selectedCharacters changes (only store IDs)
|
||||
$: if (isLoaded && selectedCharacters) {
|
||||
const ids = selectedCharacters.map(char => char.id);
|
||||
@@ -66,39 +147,15 @@
|
||||
$: columnVisibility = data.columnVisibility || {};
|
||||
$: hasWon = selectedCharacters.some(char => char.id === dailyCharacter.id);
|
||||
|
||||
// Hint availability tracking for unlock animations
|
||||
$: isOriginAvailable = selectedCharacters.length >= 5;
|
||||
$: isFruitAvailable = selectedCharacters.length >= 10;
|
||||
$: isAffiliationAvailable = selectedCharacters.length >= 15;
|
||||
|
||||
// Track hint unlocks
|
||||
$: if (isLoaded) {
|
||||
if (isOriginAvailable && !wasOriginAvailable) {
|
||||
showOriginUnlock = true;
|
||||
setTimeout(() => showOriginUnlock = false, 600);
|
||||
}
|
||||
wasOriginAvailable = isOriginAvailable;
|
||||
|
||||
if (isFruitAvailable && !wasFruitAvailable) {
|
||||
showFruitUnlock = true;
|
||||
setTimeout(() => showFruitUnlock = false, 600);
|
||||
}
|
||||
wasFruitAvailable = isFruitAvailable;
|
||||
|
||||
if (isAffiliationAvailable && !wasAffiliationAvailable) {
|
||||
showAffiliationUnlock = true;
|
||||
setTimeout(() => showAffiliationUnlock = false, 600);
|
||||
}
|
||||
wasAffiliationAvailable = isAffiliationAvailable;
|
||||
}
|
||||
|
||||
function handleCharacterSelect(event: CustomEvent) {
|
||||
const character = event.detail;
|
||||
selectCharacter(character);
|
||||
}
|
||||
|
||||
function selectCharacter(character: any) {
|
||||
function selectCharacter(character: CharacterWithRelations) {
|
||||
const previousGuessCount = selectedCharacters.length;
|
||||
selectedCharacters = [character, ...selectedCharacters];
|
||||
syncHintAvailability(previousGuessCount, selectedCharacters.length, isLoaded);
|
||||
|
||||
// Check if player won
|
||||
if (character.id === dailyCharacter.id) {
|
||||
@@ -122,7 +179,9 @@
|
||||
}
|
||||
|
||||
function resetHistory() {
|
||||
const previousGuessCount = selectedCharacters.length;
|
||||
selectedCharacters = [];
|
||||
syncHintAvailability(previousGuessCount, 0);
|
||||
localStorage.removeItem('dailyCharacterHistory');
|
||||
}
|
||||
</script>
|
||||
@@ -198,7 +257,7 @@
|
||||
<main
|
||||
class="relative min-h-screen overflow-hidden bg-slate-950 text-slate-100 {isGeckoMoriaWin ? 'moria-screen-chaos' : ''}"
|
||||
>
|
||||
<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-linear-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="relative mx-auto flex min-h-screen w-full max-w-6xl flex-col px-6 py-8 sm:py-10">
|
||||
@@ -257,7 +316,7 @@
|
||||
<section class="mt-6 rounded-3xl border border-white/10 bg-white/5 p-6 shadow-[0_24px_60px_rgba(0,0,0,0.45)] backdrop-blur">
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.28em] text-amber-100 text-center">Tes amis aujourd'hui</p>
|
||||
<div class="mt-4 space-y-2">
|
||||
{#each data.friendsTodayResults as friendResult}
|
||||
{#each data.friendsTodayResults as friendResult (friendResult.userId)}
|
||||
<div class="flex items-center justify-between rounded-lg border border-white/10 bg-slate-950/50 px-4 py-2">
|
||||
<div class="flex items-center gap-3">
|
||||
{#if friendResult.image}
|
||||
|
||||
Reference in New Issue
Block a user