tauri-app: use fields from recorded raw league data
All checks were successful
record-daemon / Build, check and test (push) Successful in 2m6s

This commit is contained in:
2026-03-27 13:42:37 +01:00
parent d67d52fa86
commit 52f8be7caa
2 changed files with 218 additions and 134 deletions

View File

@@ -1,7 +1,7 @@
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { invoke } from "@tauri-apps/api/core";
import type { GameHistoryItem, TimestampedEvent, ItemInfo, RawEndGameStats, EndGamePlayer } from "../types/timeline";
import type { GameHistoryItem, TimestampedEvent, ItemInfo } from "../types/timeline";
import {
getGameResult,
formatDuration,
@@ -16,6 +16,15 @@ import {
formatGameStartTime,
getSummonerSpellUrl,
getItemImageUrl,
getChampionName,
getSummonerName,
getQueueType,
getQueueId,
getGameMode,
getMapName,
getFinalStats,
getSummonerSpells,
getItems,
} from "../types/timeline";
// Helper to get video timestamp in seconds from tuple format
@@ -49,58 +58,9 @@ function closeDetail() {
selectedGame.value = null;
}
// Helper to find local player from raw end game stats
function getLocalPlayer(stats: RawEndGameStats | null): EndGamePlayer | null {
if (!stats) return null;
// Try localPlayer field first (camelCase from API)
if (stats.localPlayer) {
return stats.localPlayer;
}
// Try teams
if (stats.teams) {
for (const team of stats.teams) {
if (team.players) {
for (const player of team.players) {
if (player.isLocalPlayer) {
return player;
}
}
}
}
}
// Try legacy players array
if (stats.players && stats.players.length > 0) {
return stats.players[0];
}
return null;
}
// Helper to get items array for display (6 slots + trinket)
// Items are now stored as raw item IDs in raw_end_game_stats
function getItemsArray(game: GameHistoryItem): (ItemInfo | null)[] {
const result: (ItemInfo | null)[] = [null, null, null, null, null, null, null];
const localPlayer = getLocalPlayer(game.raw_end_game_stats);
if (localPlayer && localPlayer.items) {
// Items are stored as an array of item IDs (up to 7 items: 6 main + 1 trinket)
for (let i = 0; i < Math.min(localPlayer.items.length, 7); i++) {
const itemId = localPlayer.items[i];
if (itemId && itemId > 0) {
// Slot 6 is trinket, slots 0-5 are main items
result[i] = {
itemId: itemId,
name: null,
slot: i
};
}
}
}
return result;
return getItems(game);
}
onMounted(() => {
@@ -161,13 +121,13 @@ onMounted(() => {
<div class="champion-section">
<div class="champion-image-wrapper">
<img
:src="getChampionImageUrl(game.champion)"
:alt="game.champion || 'Unknown Champion'"
:src="getChampionImageUrl(getChampionName(game))"
:alt="getChampionName(game) || 'Unknown Champion'"
class="champion-image"
@error="($event.target as HTMLImageElement).src = getChampionImageUrl(null)"
/>
<div class="champion-level" v-if="game.final_stats">
{{ Math.min(18, Math.floor(game.final_stats.game_duration / 60)) }}
<div class="champion-level" v-if="getFinalStats(game)">
{{ Math.min(18, Math.floor(getFinalStats(game)!.game_duration / 60)) }}
</div>
</div>
@@ -175,18 +135,18 @@ onMounted(() => {
<div class="summoner-spells">
<div class="spell-slot">
<img
v-if="game.summoner_spells"
:src="getSummonerSpellUrl(game.summoner_spells.spell1Id)"
:alt="game.summoner_spells.spell1Name || 'Spell 1'"
v-if="getSummonerSpells(game)"
:src="getSummonerSpellUrl(getSummonerSpells(game)!.spell1Id)"
:alt="getSummonerSpells(game)!.spell1Name || 'Spell 1'"
class="spell-image"
/>
<div v-else class="spell-placeholder"></div>
</div>
<div class="spell-slot">
<img
v-if="game.summoner_spells"
:src="getSummonerSpellUrl(game.summoner_spells.spell2Id)"
:alt="game.summoner_spells.spell2Name || 'Spell 2'"
v-if="getSummonerSpells(game)"
:src="getSummonerSpellUrl(getSummonerSpells(game)!.spell2Id)"
:alt="getSummonerSpells(game)!.spell2Name || 'Spell 2'"
class="spell-image"
/>
<div v-else class="spell-placeholder"></div>
@@ -200,7 +160,7 @@ onMounted(() => {
<!-- Left: Queue Type & Time -->
<div class="game-info-left">
<div class="queue-type">
{{ getQueueDisplayName(game.queue_type, game.queue_id) }}
{{ getQueueDisplayName(getQueueType(game), getQueueId(game)) }}
</div>
<div class="game-duration">
{{ formatDuration(game.duration_secs) }}
@@ -211,12 +171,12 @@ onMounted(() => {
</div>
<!-- Right: KDA Score -->
<div class="kda-section" v-if="game.final_stats">
<div class="kda-section" v-if="getFinalStats(game)">
<span class="kda-values">
{{ formatKDA(game.final_stats) }}
{{ formatKDA(getFinalStats(game)) }}
</span>
<span class="kda-ratio" :class="{ perfect: game.final_stats.deaths === 0 }">
{{ calculateKDA(game.final_stats) }} KDA
<span class="kda-ratio" :class="{ perfect: getFinalStats(game)!.deaths === 0 }">
{{ calculateKDA(getFinalStats(game)) }} KDA
</span>
</div>
<div class="kda-section" v-else>
@@ -227,15 +187,15 @@ onMounted(() => {
</div>
<!-- Right: Key Stats Column + Items -->
<div class="stats-section" v-if="game.final_stats">
<div class="stats-section" v-if="getFinalStats(game)">
<!-- Key Stats Column -->
<div class="key-stats-column">
<div class="key-stat">
<span class="key-stat-value">{{ formatCSPerMin(game.final_stats) }}</span>
<span class="key-stat-value">{{ formatCSPerMin(getFinalStats(game)) }}</span>
<span class="key-stat-label">CS/m</span>
</div>
<div class="key-stat">
<span class="key-stat-value">{{ formatGoldPerMin(game.final_stats) }}</span>
<span class="key-stat-value">{{ formatGoldPerMin(getFinalStats(game)) }}</span>
<span class="key-stat-label">Gold/m</span>
</div>
</div>
@@ -277,14 +237,14 @@ onMounted(() => {
<div class="modal-header">
<div class="modal-champion">
<img
:src="getChampionImageUrl(selectedGame.champion)"
:alt="selectedGame.champion || 'Unknown Champion'"
:src="getChampionImageUrl(getChampionName(selectedGame))"
:alt="getChampionName(selectedGame) || 'Unknown Champion'"
class="modal-champion-image"
/>
<div class="modal-champion-info">
<h2>{{ selectedGame.champion || 'Unknown Champion' }}</h2>
<h2>{{ getChampionName(selectedGame) || 'Unknown Champion' }}</h2>
<div class="modal-queue">
{{ getQueueDisplayName(selectedGame.queue_type, selectedGame.queue_id) }}
{{ getQueueDisplayName(getQueueType(selectedGame), getQueueId(selectedGame)) }}
</div>
</div>
</div>
@@ -306,66 +266,62 @@ onMounted(() => {
<span class="detail-label">Started:</span>
<span class="detail-value">{{ formatGameStartTime(selectedGame.start_time) }}</span>
</div>
<div class="detail-row" v-if="selectedGame.game_mode">
<div class="detail-row" v-if="getGameMode(selectedGame)">
<span class="detail-label">Game Mode:</span>
<span class="detail-value">{{ selectedGame.game_mode }}</span>
<span class="detail-value">{{ getGameMode(selectedGame) }}</span>
</div>
<div class="detail-row" v-if="selectedGame.map_name">
<div class="detail-row" v-if="getMapName(selectedGame)">
<span class="detail-label">Map:</span>
<span class="detail-value">{{ selectedGame.map_name }}</span>
<span class="detail-value">{{ getMapName(selectedGame) }}</span>
</div>
<div class="detail-row" v-if="selectedGame.summoner_name">
<div class="detail-row" v-if="getSummonerName(selectedGame)">
<span class="detail-label">Summoner:</span>
<span class="detail-value">{{ selectedGame.summoner_name }}</span>
</div>
<div class="detail-row" v-if="selectedGame.team">
<span class="detail-label">Team:</span>
<span class="detail-value">{{ selectedGame.team === 100 ? 'Blue' : 'Red' }}</span>
<span class="detail-value">{{ getSummonerName(selectedGame) }}</span>
</div>
</div>
</div>
<!-- Stats -->
<div class="modal-section" v-if="selectedGame.final_stats">
<div class="modal-section" v-if="getFinalStats(selectedGame)">
<h3>Performance</h3>
<div class="stats-highlight">
<div class="stat-highlight-item">
<span class="stat-highlight-value">{{ selectedGame.final_stats.kills }}</span>
<span class="stat-highlight-value">{{ getFinalStats(selectedGame)!.kills }}</span>
<span class="stat-highlight-label">Kills</span>
</div>
<div class="stat-highlight-item">
<span class="stat-highlight-value">{{ selectedGame.final_stats.deaths }}</span>
<span class="stat-highlight-value">{{ getFinalStats(selectedGame)!.deaths }}</span>
<span class="stat-highlight-label">Deaths</span>
</div>
<div class="stat-highlight-item">
<span class="stat-highlight-value">{{ selectedGame.final_stats.assists }}</span>
<span class="stat-highlight-value">{{ getFinalStats(selectedGame)!.assists }}</span>
<span class="stat-highlight-label">Assists</span>
</div>
</div>
<div class="detail-grid">
<div class="detail-row">
<span class="detail-label">KDA Ratio:</span>
<span class="detail-value">{{ calculateKDA(selectedGame.final_stats) }}</span>
<span class="detail-value">{{ calculateKDA(getFinalStats(selectedGame)) }}</span>
</div>
<div class="detail-row">
<span class="detail-label">Creep Score:</span>
<span class="detail-value">{{ selectedGame.final_stats.creep_score }} ({{ formatCSPerMin(selectedGame.final_stats) }}/min)</span>
<span class="detail-value">{{ getFinalStats(selectedGame)!.creep_score }} ({{ formatCSPerMin(getFinalStats(selectedGame)) }}/min)</span>
</div>
<div class="detail-row">
<span class="detail-label">Gold Earned:</span>
<span class="detail-value">{{ formatNumber(selectedGame.final_stats.gold_earned) }} ({{ formatGoldPerMin(selectedGame.final_stats) }}/min)</span>
<span class="detail-value">{{ formatNumber(getFinalStats(selectedGame)!.gold_earned) }} ({{ formatGoldPerMin(getFinalStats(selectedGame)) }}/min)</span>
</div>
<div class="detail-row">
<span class="detail-label">Damage Dealt:</span>
<span class="detail-value">{{ formatNumber(selectedGame.final_stats.damage_dealt) }}</span>
<span class="detail-value">{{ formatNumber(getFinalStats(selectedGame)!.damage_dealt) }}</span>
</div>
<div class="detail-row">
<span class="detail-label">Damage Taken:</span>
<span class="detail-value">{{ formatNumber(selectedGame.final_stats.damage_taken) }}</span>
<span class="detail-value">{{ formatNumber(getFinalStats(selectedGame)!.damage_taken) }}</span>
</div>
<div class="detail-row">
<span class="detail-label">Vision Score:</span>
<span class="detail-value">{{ selectedGame.final_stats.vision_score.toFixed(1) }}</span>
<span class="detail-value">{{ getFinalStats(selectedGame)!.vision_score.toFixed(1) }}</span>
</div>
</div>
</div>