Support displaying the latest Steam games

This adds some types and support on the homepage for displaying Steam games on the homepage. We'll fix the design later.
This commit is contained in:
Justin Edmund 2024-07-31 14:49:25 -07:00
parent 09c4b756b7
commit 2dc1e2a538
4 changed files with 148 additions and 42 deletions

View file

@ -0,0 +1,94 @@
<script lang="ts">
import { spring } from 'svelte/motion'
interface GameProps {
game?: SerializableGameInfo
}
let { game = undefined }: GameProps = $props()
let isHovering = $state(false)
const scale = spring(1, {
stiffness: 0.2,
damping: 0.145
})
$effect(() => {
if (isHovering) {
scale.set(1.1)
} else {
scale.set(1)
}
})
</script>
<div class="game">
{#if game}
<a
href={`https://store.steampowered.com/app/${game.id}`}
target="_blank"
rel="noopener noreferrer"
onmouseenter={() => (isHovering = true)}
onmouseleave={() => (isHovering = false)}
>
<img src={game.coverURL} alt={game.name} style="transform: scale({$scale})" />
<div class="info">
<span class="game-name">
{game.name}
</span>
<p class="game-playtime">
{game.playtime} minutes played
</p>
</div>
</a>
{:else}
<p>No album provided</p>
{/if}
</div>
<style lang="scss">
.game {
flex-basis: 100%;
a {
display: flex;
flex-direction: column;
gap: $unit * 1.5;
text-decoration: none;
transition: gap 0.125s ease-in-out;
img {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: $unit;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
width: 100%;
height: auto;
}
.info {
display: flex;
flex-direction: column;
gap: $unit-fourth;
p {
padding: 0;
margin: 0;
}
.game-name {
font-size: $font-size;
font-weight: $font-weight-med;
color: $accent-color;
}
.game-playtime {
font-size: $font-size-small;
font-weight: $font-weight-med;
color: $grey-40;
}
}
}
}
</style>

6
src/lib/types/steam.ts Normal file
View file

@ -0,0 +1,6 @@
type SerializableGameInfo = {
id: number
name: string
playtime: number
coverURL: string
}

View file

@ -1,6 +1,7 @@
<script lang="ts">
import Album from '$components/Album.svelte'
import Avatar from '$components/Avatar.svelte'
import Game from '$components/Game.svelte'
import MentionList from '$components/MentionList.svelte'
import Page from '$components/Page.svelte'
import ProjectList from '$components/ProjectList.svelte'
@ -10,7 +11,7 @@
export let data: PageData
$: ({ albums, error } = data)
$: ({ albums, games, error } = data)
</script>
<Page>
@ -67,6 +68,18 @@
<p>Loading albums...</p>
{/if}
</section>
<section class="latest-games">
{#if games.length > 0}
<ul>
{#each games.slice(0, 3) as game}
<Game {game} />
{/each}
</ul>
{:else}
<p>Loading games...</p>
{/if}
</section>
</Page>
<footer>
@ -106,42 +119,13 @@
flex-direction: row;
gap: $unit-4x;
width: 100%;
}
li {
display: flex;
flex-basis: 100%;
flex-direction: column;
gap: $unit;
.info {
display: flex;
flex-direction: column;
gap: $unit-fourth;
p {
padding: 0;
margin: 0;
}
.album-name {
font-size: $font-size;
font-weight: $font-weight-med;
}
.artist-name {
font-size: $font-size-small;
font-weight: $font-weight-med;
color: $grey-40;
}
}
img {
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: $unit;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
width: 100%;
}
}
.latest-games ul {
display: flex;
flex-direction: row;
gap: $unit-4x;
width: 100%;
}
footer {

View file

@ -1,14 +1,36 @@
import type { PageLoad } from './$types'
import type { Album } from '$lib/types/lastfm'
import type { GameInfoExtended, UserPlaytime } from 'steamapi'
export const load: PageLoad = async ({ fetch }) => {
try {
const response = await fetch('/api/lastfm')
if (!response.ok) throw new Error(`Failed to fetch albums: ${response.status}`)
const data: { albums: Album[] } = await response.json()
return { albums: data.albums }
const [albums, games] = await Promise.all([fetchRecentAlbums(fetch), fetchRecentGames(fetch)])
console.log(games[0])
return {
albums,
games
}
} catch (err) {
console.error('Error fetching albums:', err)
return { albums: [], error: err instanceof Error ? err.message : 'An unknown error occurred' }
console.error('Error fetching data:', err)
return {
albums: [],
games: [],
error: err instanceof Error ? err.message : 'An unknown error occurred'
}
}
}
async function fetchRecentAlbums(fetch: typeof window.fetch): Promise<Album[]> {
const response = await fetch('/api/lastfm')
if (!response.ok) throw new Error(`Failed to fetch albums: ${response.status}`)
const musicData: { albums: Album[] } = await response.json()
return musicData.albums
}
async function fetchRecentGames(fetch: typeof window.fetch): Promise<SerializableGameInfo[]> {
const response = await fetch('/api/steam')
if (!response.ok) {
throw new Error(`Failed to fetch recent game: ${response.status}`)
}
return await response.json()
}