diff --git a/backend/src/main.rs b/backend/src/main.rs index 69fae17..0f283e5 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -92,6 +92,14 @@ fn get_game( games.iter().find(|g| g.title == title).cloned() } +#[get("/games")] +fn get_games(_token: auth::Token, game_list: &rocket::State>>) -> items::GameList { + let games = game_list.lock().unwrap(); + items::GameList { + games: games.clone(), + } +} + #[post("/game", data = "")] fn add_game( _token: auth::Token, @@ -203,7 +211,14 @@ async fn main() -> Result<(), std::io::Error> { .manage(Mutex::new(game_list)) .mount( "/api", - routes![get_users, get_user, get_game, add_opinion, add_game], + routes![ + get_users, + get_user, + get_game, + get_games, + add_opinion, + add_game + ], ) .mount( "/auth", diff --git a/frontend/items.ts b/frontend/items.ts index 3ab6bed..3789ccb 100644 --- a/frontend/items.ts +++ b/frontend/items.ts @@ -66,6 +66,10 @@ export interface PersonList { person: Person[]; } +export interface GameList { + games: Game[]; +} + /** Authentication messages */ export interface LoginRequest { username: string; @@ -101,6 +105,9 @@ export interface GameRequest { title: string; } +export interface GetGamesRequest { +} + export interface AddOpinionRequest { gameTitle: string; wouldPlay: boolean; @@ -474,6 +481,64 @@ export const PersonList: MessageFns = { }, }; +function createBaseGameList(): GameList { + return { games: [] }; +} + +export const GameList: MessageFns = { + encode(message: GameList, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + for (const v of message.games) { + Game.encode(v!, writer.uint32(10).fork()).join(); + } + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): GameList { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGameList(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: { + if (tag !== 10) { + break; + } + + message.games.push(Game.decode(reader, reader.uint32())); + continue; + } + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(object: any): GameList { + return { games: globalThis.Array.isArray(object?.games) ? object.games.map((e: any) => Game.fromJSON(e)) : [] }; + }, + + toJSON(message: GameList): unknown { + const obj: any = {}; + if (message.games?.length) { + obj.games = message.games.map((e) => Game.toJSON(e)); + } + return obj; + }, + + create, I>>(base?: I): GameList { + return GameList.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): GameList { + const message = createBaseGameList(); + message.games = object.games?.map((e) => Game.fromPartial(e)) || []; + return message; + }, +}; + function createBaseLoginRequest(): LoginRequest { return { username: "", password: "" }; } @@ -984,6 +1049,49 @@ export const GameRequest: MessageFns = { }, }; +function createBaseGetGamesRequest(): GetGamesRequest { + return {}; +} + +export const GetGamesRequest: MessageFns = { + encode(_: GetGamesRequest, writer: BinaryWriter = new BinaryWriter()): BinaryWriter { + return writer; + }, + + decode(input: BinaryReader | Uint8Array, length?: number): GetGamesRequest { + const reader = input instanceof BinaryReader ? input : new BinaryReader(input); + const end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseGetGamesRequest(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skip(tag & 7); + } + return message; + }, + + fromJSON(_: any): GetGamesRequest { + return {}; + }, + + toJSON(_: GetGamesRequest): unknown { + const obj: any = {}; + return obj; + }, + + create, I>>(base?: I): GetGamesRequest { + return GetGamesRequest.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): GetGamesRequest { + const message = createBaseGetGamesRequest(); + return message; + }, +}; + function createBaseAddOpinionRequest(): AddOpinionRequest { return { gameTitle: "", wouldPlay: false }; } @@ -1099,6 +1207,8 @@ export class AuthServiceClientImpl implements AuthService { export interface MainService { GetGame(request: GameRequest): Promise; + GetGames(request: GetGamesRequest): Promise; + AddGame(request: Game): Promise; AddOpinion(request: AddOpinionRequest): Promise; } @@ -1110,6 +1220,8 @@ export class MainServiceClientImpl implements MainService { this.service = opts?.service || MainServiceServiceName; this.rpc = rpc; this.GetGame = this.GetGame.bind(this); + this.GetGames = this.GetGames.bind(this); + this.AddGame = this.AddGame.bind(this); this.AddOpinion = this.AddOpinion.bind(this); } GetGame(request: GameRequest): Promise { @@ -1118,6 +1230,18 @@ export class MainServiceClientImpl implements MainService { return promise.then((data) => Game.decode(new BinaryReader(data))); } + GetGames(request: GetGamesRequest): Promise { + const data = GetGamesRequest.encode(request).finish(); + const promise = this.rpc.request(this.service, "GetGames", data); + return promise.then((data) => GameList.decode(new BinaryReader(data))); + } + + AddGame(request: Game): Promise { + const data = Game.encode(request).finish(); + const promise = this.rpc.request(this.service, "AddGame", data); + return promise.then((data) => Game.decode(new BinaryReader(data))); + } + AddOpinion(request: AddOpinionRequest): Promise { const data = AddOpinionRequest.encode(request).finish(); const promise = this.rpc.request(this.service, "AddOpinion", data); diff --git a/frontend/src/GameList.tsx b/frontend/src/GameList.tsx index a5bd135..91748fa 100644 --- a/frontend/src/GameList.tsx +++ b/frontend/src/GameList.tsx @@ -1,8 +1,9 @@ -import { useState } from "react"; -import { Game, Source } from "../items"; +import { useState, useEffect } from "react"; +import { Game, Source, GameList as GameListProto } from "../items"; import { apiFetch } from "./api"; export function GameList() { + const [games, setGames] = useState([]); const [title, setTitle] = useState(""); const [source, setSource] = useState(Source.STEAM); const [multiplayer, setMultiplayer] = useState(false); @@ -12,6 +13,24 @@ export function GameList() { const [remoteId, setRemoteId] = useState(0); const [message, setMessage] = useState(""); + const fetchGames = () => { + apiFetch("/api/games") + .then((res) => res.arrayBuffer()) + .then((buffer) => { + try { + const list = GameListProto.decode(new Uint8Array(buffer)); + setGames(list.games); + } catch (e) { + console.error("Failed to decode games:", e); + } + }) + .catch(console.error); + }; + + useEffect(() => { + fetchGames(); + }, []); + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const game = { @@ -37,6 +56,7 @@ export function GameList() { if (res.ok) { setMessage("Game added successfully!"); setTitle(""); + fetchGames(); // Reset other fields if needed } else { setMessage("Failed to add game."); @@ -121,6 +141,17 @@ export function GameList() { + +
+

Existing Games

+
    + {games.map((game) => ( +
  • + {game.title} ({game.source === Source.STEAM ? "Steam" : "Roblox"}) +
  • + ))} +
+
); } diff --git a/frontend/src/PersonDetails.tsx b/frontend/src/PersonDetails.tsx index 2e5a1cc..f826019 100644 --- a/frontend/src/PersonDetails.tsx +++ b/frontend/src/PersonDetails.tsx @@ -1,14 +1,32 @@ import { useState, useEffect } from "react"; import { useParams } from "react-router-dom"; -import { Person, AddOpinionRequest } from "../items"; +import { Person, AddOpinionRequest, Game, GameList } from "../items"; import { apiFetch } from "./api"; export const PersonDetails = () => { const { name } = useParams<{ name: string }>(); const [person, setPerson] = useState(null); + const [games, setGames] = useState([]); const [gameTitle, setGameTitle] = useState(""); const [wouldPlay, setWouldPlay] = useState(false); + useEffect(() => { + apiFetch("/api/games") + .then((res) => res.arrayBuffer()) + .then((buffer) => { + try { + const list = GameList.decode(new Uint8Array(buffer)); + setGames(list.games); + if (list.games.length > 0) { + setGameTitle(list.games[0].title); + } + } catch (e) { + console.error("Failed to decode games:", e); + } + }) + .catch(console.error); + }, []); + useEffect(() => { if (name) { apiFetch(`/api/${name}`) @@ -76,12 +94,16 @@ export const PersonDetails = () => { >

Add Opinion

- setGameTitle(e.target.value)} - /> + > + {games.map((g) => ( + + ))} +