feat(noita): stores

This commit is contained in:
2026-05-24 09:29:30 +02:00
parent 9fd0122a67
commit 821e453bc0
13 changed files with 240 additions and 176 deletions
+8 -130
View File
@@ -1,7 +1,9 @@
<script setup lang="ts">
import { ref, computed, onMounted } from "vue";
import { storeToRefs } from "pinia";
import dayjs from "dayjs";
import RankBadge from "@/components/RankBadge.vue";
import { useNoitaStore, type Objective } from "@/stores/noita";
import {
createColumnHelper,
useVueTable,
@@ -12,32 +14,11 @@ import {
type SortingState,
} from "@tanstack/vue-table";
interface Objective {
objectiv_id: string;
display_string: string;
first_seen_at: string | null;
count: number;
max_count: number;
seed: string | null;
points_per_objectiv: number;
total_points: number;
}
const userInfo = ref({
username: "Player",
rank: null as number | null,
score: 0,
runsSubmitted: 0,
deathsCount: 0,
isStaff: false,
});
const noitaStore = useNoitaStore();
const { userInfo, objectives, leaderboard, isLoadingLeaderboard, isUploading } = storeToRefs(noitaStore);
const uploadedFiles = ref<File[]>([]);
const isUploading = ref(false);
const isDragover = ref(false);
const objectives = ref<Objective[]>([]);
const isLoadingLeaderboard = ref(false);
const leaderboard = ref<any[]>([]);
const columnHelper = createColumnHelper<Objective>();
const sorting = ref<SortingState>([]);
@@ -153,40 +134,13 @@ const handleDrop = (event: DragEvent) => {
const submitRun = async () => {
if (uploadedFiles.value.length === 0) return;
isUploading.value = true;
try {
for (const file of uploadedFiles.value) {
const formData = new FormData();
formData.append("file", file);
const response = await fetch("/api/noita/submit", {
method: "POST",
body: formData,
});
if (!response.ok) {
const error = await response.json();
alert(`Error submitting ${file.name}: ${error.detail || "Unknown error"}`);
return;
}
const result = await response.json();
console.log("Submission successful:", result);
}
await noitaStore.submitRun(uploadedFiles.value);
uploadedFiles.value = [];
alert("Run submitted successfully!");
// Refresh objectives, score, and rank after successful submission
await Promise.all([
fetchUserResults(),
fetchLeaderboard(),
]);
} catch (error) {
console.error("Error submitting run:", error);
alert("Error submitting run. Please try again.");
} finally {
isUploading.value = false;
}
};
@@ -194,94 +148,18 @@ const goHome = () => {
window.location.href = "/";
};
const fetchUserResults = async () => {
try {
const response = await fetch("/api/noita/results");
if (!response.ok) throw new Error("Failed to fetch results");
const results = await response.json();
userInfo.value.score = results.total_score;
userInfo.value.deathsCount = results.deaths_count;
userInfo.value.runsSubmitted = results.objectives.length;
objectives.value = results.objectives;
} catch (error) {
console.error("Error fetching results:", error);
}
};
const fetchLeaderboard = async () => {
isLoadingLeaderboard.value = true;
try {
const response = await fetch("/api/noita/leaderboard");
if (!response.ok) throw new Error("Failed to fetch leaderboard");
const data = await response.json();
leaderboard.value = data.leaderboard;
// Find current user's rank
const userRank = leaderboard.value.find(
(entry: any) => entry.username === userInfo.value.username
);
if (userRank) {
userInfo.value.rank = userRank.rank;
userInfo.value.score = userRank.total_score;
userInfo.value.deathsCount = userRank.deaths_count;
}
} catch (error) {
console.error("Error fetching leaderboard:", error);
} finally {
isLoadingLeaderboard.value = false;
}
};
const clearCache = async () => {
try {
const response = await fetch("/api/cache/clear", {
method: "POST",
});
if (response.ok) {
alert("Cache cleared successfully!");
// Refresh data after clearing cache
await Promise.all([
fetchUserResults(),
fetchLeaderboard(),
]);
} else {
const error = await response.json();
alert(`Error clearing cache: ${error.detail || "Unknown error"}`);
}
await noitaStore.clearCache();
alert("Cache cleared successfully!");
} catch (error) {
console.error("Error clearing cache:", error);
alert("Error clearing cache. Please try again.");
}
};
const loadUserData = async () => {
// Get user info first
try {
const response = await fetch("/api/user");
if (response.ok) {
const user = await response.json();
if (user.is_authenticated) {
userInfo.value.username = user.username;
userInfo.value.isStaff = user.is_staff || false;
}
}
} catch (error) {
console.error("Error fetching user info:", error);
}
// Fetch results and leaderboard
await Promise.all([
fetchUserResults(),
fetchLeaderboard(),
]);
};
onMounted(() => {
loadUserData();
noitaStore.loadUserData();
});
</script>
+2
View File
@@ -1,8 +1,10 @@
import { createApp } from 'vue'
import Noita from '@/Noita.vue'
import { pinia } from '@/stores'
import '@/style.css'
const selector = "#app"
const mountData = document.querySelector<HTMLElement>(selector)
const app = createApp(Noita, { ...mountData?.dataset })
app.use(pinia)
app.mount(selector)
+184
View File
@@ -0,0 +1,184 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
export interface Objective {
objectiv_id: string
display_string: string
first_seen_at: string | null
count: number
max_count: number
seed: string | null
points_per_objectiv: number
total_points: number
}
interface UserInfo {
username: string
rank: number | null
score: number
runsSubmitted: number
deathsCount: number
isStaff: boolean
}
interface LeaderboardEntry {
rank: number
username: string
total_score: number
objectives_count: number
deaths_count: number
is_staff: boolean
}
export const useNoitaStore = defineStore('noita', () => {
// State
const userInfo = ref<UserInfo>({
username: 'Player',
rank: null,
score: 0,
runsSubmitted: 0,
deathsCount: 0,
isStaff: false,
})
const objectives = ref<Objective[]>([])
const leaderboard = ref<LeaderboardEntry[]>([])
const isLoadingLeaderboard = ref(false)
const isUploading = ref(false)
const error = ref<string>('')
// Actions
const fetchUserResults = async () => {
try {
const response = await fetch('/api/noita/results')
if (!response.ok) throw new Error('Failed to fetch results')
const results = await response.json()
userInfo.value.score = results.total_score
userInfo.value.deathsCount = results.deaths_count
userInfo.value.runsSubmitted = results.objectives.length
objectives.value = results.objectives
} catch (err) {
error.value = 'Failed to fetch user results'
console.error('Error fetching results:', err)
}
}
const fetchLeaderboard = async () => {
isLoadingLeaderboard.value = true
try {
const response = await fetch('/api/noita/leaderboard')
if (!response.ok) throw new Error('Failed to fetch leaderboard')
const data = await response.json()
leaderboard.value = data.leaderboard
// Find current user's rank
const userRank = leaderboard.value.find(
(entry) => entry.username === userInfo.value.username
)
if (userRank) {
userInfo.value.rank = userRank.rank
userInfo.value.score = userRank.total_score
userInfo.value.deathsCount = userRank.deaths_count
}
} catch (err) {
error.value = 'Failed to fetch leaderboard'
console.error('Error fetching leaderboard:', err)
} finally {
isLoadingLeaderboard.value = false
}
}
const loadUserData = async () => {
try {
const response = await fetch('/api/user')
if (response.ok) {
const user = await response.json()
if (user.is_authenticated) {
userInfo.value.username = user.username
userInfo.value.isStaff = user.is_staff || false
}
}
} catch (err) {
console.error('Error fetching user info:', err)
}
await Promise.all([fetchUserResults(), fetchLeaderboard()])
}
const submitRun = async (files: File[]) => {
if (files.length === 0) return
isUploading.value = true
try {
for (const file of files) {
const formData = new FormData()
formData.append('file', file)
const response = await fetch('/api/noita/submit', {
method: 'POST',
body: formData,
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.detail || 'Unknown error')
}
const result = await response.json()
console.log('Submission successful:', result)
}
// Refresh objectives, score, and rank after successful submission
await Promise.all([fetchUserResults(), fetchLeaderboard()])
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error'
error.value = `Error submitting run: ${errorMessage}`
throw err
} finally {
isUploading.value = false
}
}
const clearCache = async () => {
try {
const response = await fetch('/api/cache/clear', {
method: 'POST',
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.detail || 'Unknown error')
}
await Promise.all([fetchUserResults(), fetchLeaderboard()])
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Unknown error'
error.value = `Error clearing cache: ${errorMessage}`
throw err
}
}
const refreshData = async () => {
await Promise.all([fetchUserResults(), fetchLeaderboard()])
}
return {
// State
userInfo,
objectives,
leaderboard,
isLoadingLeaderboard,
isUploading,
error,
// Actions
fetchUserResults,
fetchLeaderboard,
loadUserData,
submitRun,
clearCache,
refreshData,
}
})