Polish inputs and make re-usable components
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
export let loading = false;
|
||||
export let hasResults = false;
|
||||
export let oncompare: (() => void) | undefined = undefined;
|
||||
export let currentPlayer: BeatLeaderPlayerProfile | null = null;
|
||||
|
||||
let initialized = false;
|
||||
|
||||
@@ -17,13 +18,36 @@
|
||||
let playerAProfile: BeatLeaderPlayerProfile | null = null;
|
||||
let playerBProfile: BeatLeaderPlayerProfile | null = null;
|
||||
|
||||
// Preview profiles (loaded as user types)
|
||||
let previewAProfile: BeatLeaderPlayerProfile | null = null;
|
||||
let previewBProfile: BeatLeaderPlayerProfile | null = null;
|
||||
let loadingPreviewA = false;
|
||||
let loadingPreviewB = false;
|
||||
|
||||
// Load from URL params on mount
|
||||
onMount(() => {
|
||||
if (browser) {
|
||||
const sp = new URLSearchParams(location.search);
|
||||
playerA = sp.get('a') ?? playerA;
|
||||
playerB = sp.get('b') ?? playerB;
|
||||
const urlA = sp.get('a');
|
||||
const urlB = sp.get('b');
|
||||
|
||||
// Prefill playerA with current player if not in URL
|
||||
if (!urlA && !playerA && currentPlayer?.id) {
|
||||
playerA = currentPlayer.id;
|
||||
} else {
|
||||
playerA = urlA ?? playerA;
|
||||
}
|
||||
|
||||
playerB = urlB ?? playerB;
|
||||
initialized = true;
|
||||
|
||||
// Load initial previews if IDs are already present
|
||||
if (playerA.trim()) {
|
||||
void loadPreviewProfile('A', playerA.trim());
|
||||
}
|
||||
if (playerB.trim()) {
|
||||
void loadPreviewProfile('B', playerB.trim());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -67,6 +91,88 @@
|
||||
playerBProfile = pb;
|
||||
}
|
||||
|
||||
// Debounced preview loading
|
||||
let previewDebounceTimerA: ReturnType<typeof setTimeout> | null = null;
|
||||
let previewDebounceTimerB: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
async function loadPreviewProfile(player: 'A' | 'B', id: string): Promise<void> {
|
||||
if (!id || id.trim().length < 3) {
|
||||
if (player === 'A') previewAProfile = null;
|
||||
else previewBProfile = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const trimmed = id.trim();
|
||||
if (player === 'A') {
|
||||
loadingPreviewA = true;
|
||||
} else {
|
||||
loadingPreviewB = true;
|
||||
}
|
||||
|
||||
try {
|
||||
const profile = await fetchPlayerProfile(trimmed);
|
||||
// Only update if this is still the current player ID
|
||||
const currentId = player === 'A' ? playerA.trim() : playerB.trim();
|
||||
if (currentId === trimmed) {
|
||||
if (player === 'A') {
|
||||
previewAProfile = profile;
|
||||
} else {
|
||||
previewBProfile = profile;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Silently fail - don't show errors for preview loading
|
||||
const currentId = player === 'A' ? playerA.trim() : playerB.trim();
|
||||
if (currentId === trimmed) {
|
||||
if (player === 'A') {
|
||||
previewAProfile = null;
|
||||
} else {
|
||||
previewBProfile = null;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (player === 'A') {
|
||||
loadingPreviewA = false;
|
||||
} else {
|
||||
loadingPreviewB = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for changes to playerA and debounce preview loading
|
||||
$: if (initialized && browser) {
|
||||
const trimmed = playerA.trim();
|
||||
if (previewDebounceTimerA) clearTimeout(previewDebounceTimerA);
|
||||
|
||||
if (trimmed && trimmed.length >= 3) {
|
||||
previewDebounceTimerA = setTimeout(() => {
|
||||
void loadPreviewProfile('A', trimmed);
|
||||
}, 800);
|
||||
} else {
|
||||
previewAProfile = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Watch for changes to playerB and debounce preview loading
|
||||
$: if (initialized && browser) {
|
||||
const trimmed = playerB.trim();
|
||||
if (previewDebounceTimerB) clearTimeout(previewDebounceTimerB);
|
||||
|
||||
if (trimmed && trimmed.length >= 3) {
|
||||
previewDebounceTimerB = setTimeout(() => {
|
||||
void loadPreviewProfile('B', trimmed);
|
||||
}, 800);
|
||||
} else {
|
||||
previewBProfile = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Computed: which profile to display (post-compare takes priority over preview)
|
||||
$: displayProfileA = hasCompared ? playerAProfile : previewAProfile;
|
||||
$: displayProfileB = hasCompared ? playerBProfile : previewBProfile;
|
||||
$: showCardA = !!(displayProfileA || (loadingPreviewA && playerA.trim().length >= 3));
|
||||
$: showCardB = !!(displayProfileB || (loadingPreviewB && playerB.trim().length >= 3));
|
||||
|
||||
async function handleSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
hasCompared = true;
|
||||
@@ -90,31 +196,33 @@
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
{#if $$slots['player-a-card']}
|
||||
<slot name="player-a-card" />
|
||||
{:else if showCardA}
|
||||
<div class="player-card-wrapper">
|
||||
{#if displayProfileA}
|
||||
<PlayerCard
|
||||
name={displayProfileA.name ?? 'Player A'}
|
||||
avatar={displayProfileA.avatar ?? null}
|
||||
country={displayProfileA.country ?? null}
|
||||
rank={displayProfileA.rank ?? null}
|
||||
showRank={typeof displayProfileA.rank === 'number'}
|
||||
width="100%"
|
||||
avatarSize={56}
|
||||
techPp={displayProfileA.techPp}
|
||||
accPp={displayProfileA.accPp}
|
||||
passPp={displayProfileA.passPp}
|
||||
playerId={displayProfileA.id ?? null}
|
||||
gradientId="compare-player-a"
|
||||
/>
|
||||
{:else if loadingPreviewA}
|
||||
<div class="loading-profile">Loading player...</div>
|
||||
{:else if hasCompared}
|
||||
<div class="empty-profile">Player A profile not found</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $$slots['player-a-card']}
|
||||
<slot name="player-a-card" />
|
||||
{:else if hasCompared}
|
||||
<div class="player-card-wrapper">
|
||||
{#if playerAProfile}
|
||||
<PlayerCard
|
||||
name={playerAProfile.name ?? 'Player A'}
|
||||
avatar={playerAProfile.avatar ?? null}
|
||||
country={playerAProfile.country ?? null}
|
||||
rank={playerAProfile.rank ?? null}
|
||||
showRank={typeof playerAProfile.rank === 'number'}
|
||||
width="100%"
|
||||
avatarSize={56}
|
||||
techPp={playerAProfile.techPp}
|
||||
accPp={playerAProfile.accPp}
|
||||
passPp={playerAProfile.passPp}
|
||||
playerId={playerAProfile.id ?? null}
|
||||
gradientId="compare-player-a"
|
||||
/>
|
||||
{:else}
|
||||
<div class="empty-profile">Player A profile not found</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="player-column">
|
||||
@@ -127,31 +235,33 @@
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
{#if $$slots['player-b-card']}
|
||||
<slot name="player-b-card" />
|
||||
{:else if showCardB}
|
||||
<div class="player-card-wrapper">
|
||||
{#if displayProfileB}
|
||||
<PlayerCard
|
||||
name={displayProfileB.name ?? 'Player B'}
|
||||
avatar={displayProfileB.avatar ?? null}
|
||||
country={displayProfileB.country ?? null}
|
||||
rank={displayProfileB.rank ?? null}
|
||||
showRank={typeof displayProfileB.rank === 'number'}
|
||||
width="100%"
|
||||
avatarSize={56}
|
||||
techPp={displayProfileB.techPp}
|
||||
accPp={displayProfileB.accPp}
|
||||
passPp={displayProfileB.passPp}
|
||||
playerId={displayProfileB.id ?? null}
|
||||
gradientId="compare-player-b"
|
||||
/>
|
||||
{:else if loadingPreviewB}
|
||||
<div class="loading-profile">Loading player...</div>
|
||||
{:else if hasCompared}
|
||||
<div class="empty-profile">Player B profile not found</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $$slots['player-b-card']}
|
||||
<slot name="player-b-card" />
|
||||
{:else if hasCompared}
|
||||
<div class="player-card-wrapper">
|
||||
{#if playerBProfile}
|
||||
<PlayerCard
|
||||
name={playerBProfile.name ?? 'Player B'}
|
||||
avatar={playerBProfile.avatar ?? null}
|
||||
country={playerBProfile.country ?? null}
|
||||
rank={playerBProfile.rank ?? null}
|
||||
showRank={typeof playerBProfile.rank === 'number'}
|
||||
width="100%"
|
||||
avatarSize={56}
|
||||
techPp={playerBProfile.techPp}
|
||||
accPp={playerBProfile.accPp}
|
||||
passPp={playerBProfile.passPp}
|
||||
playerId={playerBProfile.id ?? null}
|
||||
gradientId="compare-player-b"
|
||||
/>
|
||||
{:else}
|
||||
<div class="empty-profile">Player B profile not found</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -224,5 +334,23 @@
|
||||
box-shadow: 0 0 20px rgba(34, 211, 238, 0.35), 0 0 0 2px rgba(34, 211, 238, 0.1);
|
||||
color: rgba(255, 255, 255, 1);
|
||||
}
|
||||
|
||||
.player-card-wrapper {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.empty-profile {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
color: rgba(148, 163, 184, 0.7);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.loading-profile {
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
color: rgba(148, 163, 184, 0.7);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user