feat(noita): stores
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user