From 76281892a265179eed5198b71b105b863c0a962b Mon Sep 17 00:00:00 2001 From: code002lover Date: Mon, 12 Jan 2026 14:27:01 +0100 Subject: [PATCH] fix bugs --- backend/src/main.rs | 29 +++++++++++++++++++---------- frontend/src/App.tsx | 17 ++++++++++++++--- frontend/src/GameDetails.tsx | 2 +- frontend/src/api.ts | 2 +- frontend/src/hooks/useGameFilter.ts | 8 +++++++- 5 files changed, 42 insertions(+), 16 deletions(-) diff --git a/backend/src/main.rs b/backend/src/main.rs index b8a611f..eb72fb2 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -3,6 +3,7 @@ use rocket::fs::FileServer; use rocket::futures::lock::Mutex; use backend::auth; +use backend::auth::AdminState; use backend::items::{self, Game}; use backend::proto_utils; use backend::store::{self, User, save_state}; @@ -111,6 +112,7 @@ async fn update_game( game: proto_utils::Proto, ) -> Option { let mut games = game_list.lock().await; + let mut users = user_list.lock().await; let mut game = game.into_inner(); game.title = game.title.trim().to_string(); @@ -125,13 +127,14 @@ async fn update_game( (g.remote_id == game.remote_id && g.source == game.source) || (g.title == game.title) }) { if existing.title != game.title { + let old_title = existing.title.clone(); // Update title for every opinion - for person in user_list.lock().await.iter_mut() { + for person in users.iter_mut() { let opinion = person .person .opinion .iter_mut() - .find(|o| o.title == existing.title); + .find(|o| o.title == old_title); if let Some(opinion) = opinion { opinion.title = game.title.clone(); } @@ -149,7 +152,6 @@ async fn update_game( games.sort_unstable_by(|g1, g2| g1.title.cmp(&g2.title)); - let users = user_list.lock().await; save_state(&games, &users); r_existing @@ -189,13 +191,16 @@ async fn refresh_state( _token: auth::AdminToken, game_list: &rocket::State>>, user_list: &rocket::State>>, + admin_state: &rocket::State, ) -> items::RefreshResponse { if let Some((new_games, new_users)) = store::load_state() { let mut games = game_list.lock().await; let mut users = user_list.lock().await; + let mut admins = admin_state.admins.lock().await; *games = new_games; *users = new_users; + *admins = store::load_admins(); items::RefreshResponse { success: true, @@ -212,12 +217,12 @@ async fn refresh_state( #[post("/opinion", data = "")] async fn add_opinion( token: auth::Token, - user_list: &rocket::State>>, game_list: &rocket::State>>, + user_list: &rocket::State>>, req: proto_utils::Proto, ) -> Option { - let mut users = user_list.lock().await; let games = game_list.lock().await; + let mut users = user_list.lock().await; let mut result = None; // Validate game exists @@ -261,12 +266,12 @@ async fn add_opinion( #[patch("/opinion", data = "")] async fn remove_opinion( token: auth::Token, - user_list: &rocket::State>>, game_list: &rocket::State>>, + user_list: &rocket::State>>, req: proto_utils::Proto, ) -> Option { - let mut users = user_list.lock().await; let games = game_list.lock().await; + let mut users = user_list.lock().await; let mut result = None; if let Some(user) = users @@ -340,9 +345,13 @@ async fn get_game_thumbnail( .json::() .await .ok()? - .get("universeId")? - .as_u64() - .unwrap() + .get("universeId") + .and_then(|v| v.as_u64()) + }; + + let universe_id = match universe_id { + Some(id) => id, + None => return None.into(), }; let api_url = format!( diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index f156284..e7530fc 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState, useEffect, useCallback } from "react"; import { Person, PersonList as PersonListProto } from "../items"; import { Login } from "./Login"; import { PersonList } from "./PersonList"; @@ -62,10 +62,21 @@ function App() { } }, [theme]); - const addToast = (message: string, type: ToastType = "info") => { + useEffect(() => { + const handleUnauthorized = () => { + setToken(""); + setPeople([]); + addToast("Session expired. Please log in again.", "info"); + }; + + window.addEventListener("unauthorized", handleUnauthorized); + return () => window.removeEventListener("unauthorized", handleUnauthorized); + }, [addToast]); + + const addToast = useCallback((message: string, type: ToastType = "info") => { const id = Date.now(); setToasts((prev) => [...prev, { id, message, type }]); - }; + }, []); const removeToast = (id: number) => { setToasts((prev) => prev.filter((t) => t.id !== id)); diff --git a/frontend/src/GameDetails.tsx b/frontend/src/GameDetails.tsx index c84e237..9259693 100644 --- a/frontend/src/GameDetails.tsx +++ b/frontend/src/GameDetails.tsx @@ -67,7 +67,7 @@ export function GameDetails({ onShowToast }: Props) { }; if (loading) return ; - if (error) return window.location.reload()} />; + if (error) return navigate(0)} />; if (!game) return ; const getExternalLink = () => { diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 6ca4d88..668e121 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -22,7 +22,7 @@ export const apiFetch = async ( if (response.status == 401) { localStorage.removeItem("token"); localStorage.removeItem("isAdmin"); - window.location.href = "/"; + window.dispatchEvent(new CustomEvent("unauthorized")); } throw new Error(`Request failed with status ${response.status}`); } diff --git a/frontend/src/hooks/useGameFilter.ts b/frontend/src/hooks/useGameFilter.ts index 2e46224..06d0fbb 100644 --- a/frontend/src/hooks/useGameFilter.ts +++ b/frontend/src/hooks/useGameFilter.ts @@ -17,6 +17,12 @@ export function useGameFilter( const [fetchedTitles, setFetchedTitles] = useState([]); const metaDataRef = useRef<{ [key: string]: GameProto }>({}); + useEffect(() => { + return () => { + metaDataRef.current = {}; + }; + }, []); + const { gameToNegative, gameToPositiveOpinion } = useMemo(() => { const gameToNegative = new Map>(); const gameToPositiveOpinion = new Map>(); @@ -108,7 +114,7 @@ export function useGameFilter( const gamesMap = useMemo(() => { return new Map(Object.entries(metaDataRef.current)); - }, [fetchedTitles]); + }, []); return { filteredGames, gameToPositive: gameToPositiveOpinion, games: gamesMap }; }