Add Steam integration showing online status and recent games
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
Some checks failed
Deploy with Docker Compose / deploy (push) Has been cancelled
Fetches player summary and recently played games from Steam API with 5-minute server-side caching. Displays in the home sidebar with online indicator and game artwork. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,7 @@ export const useHomeDataStore = defineStore("homeData", () => {
|
||||
const spotifyRecent = ref([]);
|
||||
const rowingSessions = ref([]);
|
||||
const gitFeed = ref(null);
|
||||
const steamStatus = ref(null);
|
||||
const radioLive = ref(false);
|
||||
|
||||
async function fetchAll() {
|
||||
@@ -27,6 +28,7 @@ export const useHomeDataStore = defineStore("homeData", () => {
|
||||
spotifyRecent { track { name album { name images { url } } artists { name } } playedAt }
|
||||
rowingSessions { id date time distance timePer500m calories }
|
||||
giteaFeed { avatarUrl repoUrl repoName opType commitMessage createdAt }
|
||||
steamStatus { online recentGames { appId name playtime2Weeks playtimeForever headerImageUrl } }
|
||||
me { id username admin }
|
||||
}
|
||||
`),
|
||||
@@ -38,6 +40,7 @@ export const useHomeDataStore = defineStore("homeData", () => {
|
||||
spotifyRecent.value = data.spotifyRecent || [];
|
||||
rowingSessions.value = data.rowingSessions;
|
||||
gitFeed.value = data.giteaFeed || null;
|
||||
steamStatus.value = data.steamStatus || null;
|
||||
me.value = data.me || null;
|
||||
loaded.value = true;
|
||||
} catch (err) {
|
||||
@@ -67,6 +70,7 @@ export const useHomeDataStore = defineStore("homeData", () => {
|
||||
spotifyRecent,
|
||||
rowingSessions,
|
||||
gitFeed,
|
||||
steamStatus,
|
||||
radioLive,
|
||||
fetchAll,
|
||||
fetchRadioStatus,
|
||||
|
||||
42
vue/src/stores/steam.js
Normal file
42
vue/src/stores/steam.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { ref, watch } from "vue";
|
||||
import { gql } from "@/graphql";
|
||||
import { useHomeDataStore } from "@/stores/homeData";
|
||||
|
||||
export const useSteamStore = defineStore("steam", () => {
|
||||
const steamStatus = ref({ online: false, recentGames: [] });
|
||||
|
||||
const homeData = useHomeDataStore();
|
||||
watch(
|
||||
() => homeData.steamStatus,
|
||||
(newStatus) => {
|
||||
if (newStatus) {
|
||||
steamStatus.value = newStatus;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
async function fetchSteam() {
|
||||
try {
|
||||
const data = await gql(`
|
||||
query {
|
||||
steamStatus {
|
||||
online
|
||||
recentGames { appId name playtime2Weeks playtimeForever headerImageUrl }
|
||||
}
|
||||
}
|
||||
`);
|
||||
if (data.steamStatus) {
|
||||
steamStatus.value = data.steamStatus;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to fetch Steam status", err);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
steamStatus,
|
||||
fetchSteam,
|
||||
};
|
||||
});
|
||||
@@ -20,6 +20,7 @@ import Favorites from "./Favorites.vue";
|
||||
// import Gym from "./Gym.vue";
|
||||
import Gym2 from "./Gym2.vue";
|
||||
import Consumption from "./Consumption.vue";
|
||||
import Steam from "./Steam.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -30,6 +31,7 @@ import Consumption from "./Consumption.vue";
|
||||
class="flex-1 flex flex-col min-h-0 background-children border-children gap-2"
|
||||
>
|
||||
<Chat class="flex-1 min-h-0" />
|
||||
<Steam />
|
||||
</div>
|
||||
<div class="sidebar-image">
|
||||
<Miku class="border-tertiary border bg-bg_secondary h-60" />
|
||||
|
||||
66
vue/src/views/home/Steam.vue
Normal file
66
vue/src/views/home/Steam.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<script setup>
|
||||
import { onMounted, onUnmounted } from "vue";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { useSteamStore } from "@/stores/steam";
|
||||
import { useHomeDataStore } from "@/stores/homeData";
|
||||
import Header from "@/components/text/Header.vue";
|
||||
|
||||
const steamStore = useSteamStore();
|
||||
const { steamStatus } = storeToRefs(steamStore);
|
||||
const homeData = useHomeDataStore();
|
||||
const { loaded } = storeToRefs(homeData);
|
||||
|
||||
let refreshInterval;
|
||||
onMounted(() => {
|
||||
refreshInterval = setInterval(() => steamStore.fetchSteam(), 5 * 60 * 1000);
|
||||
});
|
||||
onUnmounted(() => clearInterval(refreshInterval));
|
||||
|
||||
function formatHours(minutes) {
|
||||
const hrs = (minutes / 60).toFixed(1);
|
||||
return `${hrs}h`;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col min-h-0 overflow-hidden">
|
||||
<Header class="text-left">
|
||||
<span class="flex items-center gap-2">
|
||||
Steam
|
||||
<span
|
||||
class="inline-block w-2 h-2 rounded-full"
|
||||
:class="steamStatus.online ? 'bg-green-500' : 'bg-gray-400'"
|
||||
:title="steamStatus.online ? 'Online' : 'Offline'"
|
||||
/>
|
||||
</span>
|
||||
</Header>
|
||||
|
||||
<div v-if="!loaded" class="p-2 text-sm">Loading...</div>
|
||||
|
||||
<div
|
||||
v-else-if="steamStatus.recentGames.length"
|
||||
class="flex-1 overflow-y-auto flex flex-col gap-2 p-1"
|
||||
>
|
||||
<div
|
||||
v-for="game in steamStatus.recentGames"
|
||||
:key="game.appId"
|
||||
class="flex flex-col"
|
||||
>
|
||||
<img
|
||||
:src="game.headerImageUrl"
|
||||
:alt="game.name"
|
||||
class="w-full object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
<div class="px-1 py-0.5 text-xs">
|
||||
<p class="font-bold truncate">{{ game.name }}</p>
|
||||
<p class="text-tertiary">
|
||||
{{ formatHours(game.playtime2Weeks) }} last 2 weeks
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="p-2 text-sm">No recent games.</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user