test page

This commit is contained in:
code002lover 2025-11-29 12:00:58 +01:00
parent c83f0a1c34
commit 5d3f64f558
10 changed files with 1195 additions and 133 deletions

681
backend/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ edition = "2024"
[dependencies] [dependencies]
prost = "0.14.1" prost = "0.14.1"
prost-types = "0.14.1" prost-types = "0.14.1"
rocket = "0.5.1" rocket = { git = "https://github.com/rwf2/Rocket", rev = "504efef179622df82ba1dbd37f2e0d9ed2b7c9e4" }
bytes = "1" bytes = "1"
rocket_prost_responder_derive = { path = "rocket_prost_responder_derive" } rocket_prost_responder_derive = { path = "rocket_prost_responder_derive" }

View File

@ -1,3 +1,5 @@
use std::process::Command;
extern crate prost_build; extern crate prost_build;
fn main() -> Result<(), std::io::Error> { fn main() -> Result<(), std::io::Error> {
@ -11,5 +13,14 @@ fn main() -> Result<(), std::io::Error> {
); );
cfg.compile_protos(&["../protobuf/items.proto"], &["../protobuf"])?; cfg.compile_protos(&["../protobuf/items.proto"], &["../protobuf"])?;
Command::new("pnpm")
.arg("run")
.arg("build")
.current_dir("../frontend")
.spawn()
.unwrap()
.wait()
.unwrap();
Ok(()) Ok(())
} }

View File

@ -1,3 +1,11 @@
use rocket::{
Request,
fs::{
FileServer,
rewrite::{File, Rewrite},
},
};
#[macro_use] #[macro_use]
extern crate rocket; extern crate rocket;
@ -11,8 +19,22 @@ fn get_user(user_list: &rocket::State<Vec<items::Person>>, name: String) -> Opti
} }
#[get("/")] #[get("/")]
fn index(user_list: &rocket::State<Vec<items::Person>>) -> String { fn get_users(user_list: &rocket::State<Vec<items::Person>>) -> items::PersonList {
format!("Hello, user amount: {}", user_list.len()) items::PersonList {
person: user_list
.inner()
.to_vec()
.iter_mut()
.map(|x| {
x.opinion.clear();
x.clone()
})
.collect(),
}
}
fn redir_missing<'r>(p: Option<Rewrite<'r>>, _req: &Request<'_>) -> Option<Rewrite<'r>> {
Some(p.unwrap_or_else(|| Rewrite::File(File::checked("../frontend/dist/index.html"))))
} }
#[launch] #[launch]
@ -36,5 +58,9 @@ fn rocket() -> _ {
rocket::build() rocket::build()
.manage(user_list) .manage(user_list)
.mount("/", routes![index, get_user]) .mount("/api", routes![get_users, get_user])
.mount(
"/",
FileServer::new("../frontend/dist").rewrite(redir_missing),
)
} }

443
frontend/items.ts Normal file
View File

@ -0,0 +1,443 @@
// Code generated by protoc-gen-ts_proto. DO NOT EDIT.
// versions:
// protoc-gen-ts_proto v2.8.3
// protoc v6.33.1
// source: items.proto
/* eslint-disable */
import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
export const protobufPackage = "items";
export enum Source {
STEAM = 0,
ROBLOX = 1,
UNRECOGNIZED = -1,
}
export function sourceFromJSON(object: any): Source {
switch (object) {
case 0:
case "STEAM":
return Source.STEAM;
case 1:
case "ROBLOX":
return Source.ROBLOX;
case -1:
case "UNRECOGNIZED":
default:
return Source.UNRECOGNIZED;
}
}
export function sourceToJSON(object: Source): string {
switch (object) {
case Source.STEAM:
return "STEAM";
case Source.ROBLOX:
return "ROBLOX";
case Source.UNRECOGNIZED:
default:
return "UNRECOGNIZED";
}
}
export interface Person {
name: string;
opinion: Opinion[];
}
export interface Opinion {
game: Game | undefined;
wouldPlay: boolean;
}
export interface Game {
title: string;
source: Source;
multiplayer: boolean;
minPlayers: number;
maxPlayers: number;
price: number;
}
export interface PersonList {
person: Person[];
}
function createBasePerson(): Person {
return { name: "", opinion: [] };
}
export const Person: MessageFns<Person> = {
encode(message: Person, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.name !== "") {
writer.uint32(10).string(message.name);
}
for (const v of message.opinion) {
Opinion.encode(v!, writer.uint32(18).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Person {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePerson();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.name = reader.string();
continue;
}
case 2: {
if (tag !== 18) {
break;
}
message.opinion.push(Opinion.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Person {
return {
name: isSet(object.name) ? globalThis.String(object.name) : "",
opinion: globalThis.Array.isArray(object?.opinion) ? object.opinion.map((e: any) => Opinion.fromJSON(e)) : [],
};
},
toJSON(message: Person): unknown {
const obj: any = {};
if (message.name !== "") {
obj.name = message.name;
}
if (message.opinion?.length) {
obj.opinion = message.opinion.map((e) => Opinion.toJSON(e));
}
return obj;
},
create<I extends Exact<DeepPartial<Person>, I>>(base?: I): Person {
return Person.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<Person>, I>>(object: I): Person {
const message = createBasePerson();
message.name = object.name ?? "";
message.opinion = object.opinion?.map((e) => Opinion.fromPartial(e)) || [];
return message;
},
};
function createBaseOpinion(): Opinion {
return { game: undefined, wouldPlay: false };
}
export const Opinion: MessageFns<Opinion> = {
encode(message: Opinion, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.game !== undefined) {
Game.encode(message.game, writer.uint32(10).fork()).join();
}
if (message.wouldPlay !== false) {
writer.uint32(16).bool(message.wouldPlay);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Opinion {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseOpinion();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.game = Game.decode(reader, reader.uint32());
continue;
}
case 2: {
if (tag !== 16) {
break;
}
message.wouldPlay = reader.bool();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Opinion {
return {
game: isSet(object.game) ? Game.fromJSON(object.game) : undefined,
wouldPlay: isSet(object.wouldPlay) ? globalThis.Boolean(object.wouldPlay) : false,
};
},
toJSON(message: Opinion): unknown {
const obj: any = {};
if (message.game !== undefined) {
obj.game = Game.toJSON(message.game);
}
if (message.wouldPlay !== false) {
obj.wouldPlay = message.wouldPlay;
}
return obj;
},
create<I extends Exact<DeepPartial<Opinion>, I>>(base?: I): Opinion {
return Opinion.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<Opinion>, I>>(object: I): Opinion {
const message = createBaseOpinion();
message.game = (object.game !== undefined && object.game !== null) ? Game.fromPartial(object.game) : undefined;
message.wouldPlay = object.wouldPlay ?? false;
return message;
},
};
function createBaseGame(): Game {
return { title: "", source: 0, multiplayer: false, minPlayers: 0, maxPlayers: 0, price: 0 };
}
export const Game: MessageFns<Game> = {
encode(message: Game, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
if (message.title !== "") {
writer.uint32(10).string(message.title);
}
if (message.source !== 0) {
writer.uint32(16).int32(message.source);
}
if (message.multiplayer !== false) {
writer.uint32(24).bool(message.multiplayer);
}
if (message.minPlayers !== 0) {
writer.uint32(32).uint32(message.minPlayers);
}
if (message.maxPlayers !== 0) {
writer.uint32(40).uint32(message.maxPlayers);
}
if (message.price !== 0) {
writer.uint32(48).uint32(message.price);
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): Game {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseGame();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.title = reader.string();
continue;
}
case 2: {
if (tag !== 16) {
break;
}
message.source = reader.int32() as any;
continue;
}
case 3: {
if (tag !== 24) {
break;
}
message.multiplayer = reader.bool();
continue;
}
case 4: {
if (tag !== 32) {
break;
}
message.minPlayers = reader.uint32();
continue;
}
case 5: {
if (tag !== 40) {
break;
}
message.maxPlayers = reader.uint32();
continue;
}
case 6: {
if (tag !== 48) {
break;
}
message.price = reader.uint32();
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): Game {
return {
title: isSet(object.title) ? globalThis.String(object.title) : "",
source: isSet(object.source) ? sourceFromJSON(object.source) : 0,
multiplayer: isSet(object.multiplayer) ? globalThis.Boolean(object.multiplayer) : false,
minPlayers: isSet(object.minPlayers) ? globalThis.Number(object.minPlayers) : 0,
maxPlayers: isSet(object.maxPlayers) ? globalThis.Number(object.maxPlayers) : 0,
price: isSet(object.price) ? globalThis.Number(object.price) : 0,
};
},
toJSON(message: Game): unknown {
const obj: any = {};
if (message.title !== "") {
obj.title = message.title;
}
if (message.source !== 0) {
obj.source = sourceToJSON(message.source);
}
if (message.multiplayer !== false) {
obj.multiplayer = message.multiplayer;
}
if (message.minPlayers !== 0) {
obj.minPlayers = Math.round(message.minPlayers);
}
if (message.maxPlayers !== 0) {
obj.maxPlayers = Math.round(message.maxPlayers);
}
if (message.price !== 0) {
obj.price = Math.round(message.price);
}
return obj;
},
create<I extends Exact<DeepPartial<Game>, I>>(base?: I): Game {
return Game.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<Game>, I>>(object: I): Game {
const message = createBaseGame();
message.title = object.title ?? "";
message.source = object.source ?? 0;
message.multiplayer = object.multiplayer ?? false;
message.minPlayers = object.minPlayers ?? 0;
message.maxPlayers = object.maxPlayers ?? 0;
message.price = object.price ?? 0;
return message;
},
};
function createBasePersonList(): PersonList {
return { person: [] };
}
export const PersonList: MessageFns<PersonList> = {
encode(message: PersonList, writer: BinaryWriter = new BinaryWriter()): BinaryWriter {
for (const v of message.person) {
Person.encode(v!, writer.uint32(10).fork()).join();
}
return writer;
},
decode(input: BinaryReader | Uint8Array, length?: number): PersonList {
const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
const end = length === undefined ? reader.len : reader.pos + length;
const message = createBasePersonList();
while (reader.pos < end) {
const tag = reader.uint32();
switch (tag >>> 3) {
case 1: {
if (tag !== 10) {
break;
}
message.person.push(Person.decode(reader, reader.uint32()));
continue;
}
}
if ((tag & 7) === 4 || tag === 0) {
break;
}
reader.skip(tag & 7);
}
return message;
},
fromJSON(object: any): PersonList {
return {
person: globalThis.Array.isArray(object?.person) ? object.person.map((e: any) => Person.fromJSON(e)) : [],
};
},
toJSON(message: PersonList): unknown {
const obj: any = {};
if (message.person?.length) {
obj.person = message.person.map((e) => Person.toJSON(e));
}
return obj;
},
create<I extends Exact<DeepPartial<PersonList>, I>>(base?: I): PersonList {
return PersonList.fromPartial(base ?? ({} as any));
},
fromPartial<I extends Exact<DeepPartial<PersonList>, I>>(object: I): PersonList {
const message = createBasePersonList();
message.person = object.person?.map((e) => Person.fromPartial(e)) || [];
return message;
},
};
type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
export type DeepPartial<T> = T extends Builtin ? T
: T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>>
: T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
: T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
: Partial<T>;
type KeysOfUnion<T> = T extends T ? keyof T : never;
export type Exact<P, I extends P> = P extends Builtin ? P
: P & { [K in keyof P]: Exact<P[K], I[K]> } & { [K in Exclude<keyof I, KeysOfUnion<P>>]: never };
function isSet(value: any): boolean {
return value !== null && value !== undefined;
}
export interface MessageFns<T> {
encode(message: T, writer?: BinaryWriter): BinaryWriter;
decode(input: BinaryReader | Uint8Array, length?: number): T;
fromJSON(object: any): T;
toJSON(message: T): unknown;
create<I extends Exact<DeepPartial<T>, I>>(base?: I): T;
fromPartial<I extends Exact<DeepPartial<T>, I>>(object: I): T;
}

View File

@ -7,9 +7,11 @@
"dev": "vite", "dev": "vite",
"build": "tsc -b && vite build", "build": "tsc -b && vite build",
"lint": "eslint .", "lint": "eslint .",
"preview": "vite preview" "preview": "vite preview",
"gen:proto": "protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=. -I ../protobuf items.proto"
}, },
"dependencies": { "dependencies": {
"@bufbuild/protobuf": "^2.10.1",
"react": "^19.2.0", "react": "^19.2.0",
"react-dom": "^19.2.0" "react-dom": "^19.2.0"
}, },
@ -23,6 +25,7 @@
"eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.24", "eslint-plugin-react-refresh": "^0.4.24",
"globals": "^16.5.0", "globals": "^16.5.0",
"ts-proto": "^2.8.3",
"typescript": "~5.9.3", "typescript": "~5.9.3",
"typescript-eslint": "^8.46.4", "typescript-eslint": "^8.46.4",
"vite": "npm:rolldown-vite@7.2.5" "vite": "npm:rolldown-vite@7.2.5"

View File

@ -11,6 +11,9 @@ importers:
.: .:
dependencies: dependencies:
'@bufbuild/protobuf':
specifier: ^2.10.1
version: 2.10.1
react: react:
specifier: ^19.2.0 specifier: ^19.2.0
version: 19.2.0 version: 19.2.0
@ -45,6 +48,9 @@ importers:
globals: globals:
specifier: ^16.5.0 specifier: ^16.5.0
version: 16.5.0 version: 16.5.0
ts-proto:
specifier: ^2.8.3
version: 2.8.3
typescript: typescript:
specifier: ~5.9.3 specifier: ~5.9.3
version: 5.9.3 version: 5.9.3
@ -140,6 +146,9 @@ packages:
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
'@bufbuild/protobuf@2.10.1':
resolution: {integrity: sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg==}
'@emnapi/core@1.7.1': '@emnapi/core@1.7.1':
resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==}
@ -460,6 +469,10 @@ packages:
caniuse-lite@1.0.30001757: caniuse-lite@1.0.30001757:
resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==}
case-anything@2.1.13:
resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==}
engines: {node: '>=12.13'}
chalk@4.1.2: chalk@4.1.2:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -496,10 +509,18 @@ packages:
deep-is@0.1.4: deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
detect-libc@1.0.3:
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
engines: {node: '>=0.10'}
hasBin: true
detect-libc@2.1.2: detect-libc@2.1.2:
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
dprint-node@1.0.8:
resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==}
electron-to-chromium@1.5.262: electron-to-chromium@1.5.262:
resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==} resolution: {integrity: sha512-NlAsMteRHek05jRUxUR0a5jpjYq9ykk6+kO0yRaMi5moe7u0fVIOeQ3Y30A8dIiWFBNUoQGi1ljb1i5VtS9WQQ==}
@ -939,6 +960,16 @@ packages:
peerDependencies: peerDependencies:
typescript: '>=4.8.4' typescript: '>=4.8.4'
ts-poet@6.12.0:
resolution: {integrity: sha512-xo+iRNMWqyvXpFTaOAvLPA5QAWO6TZrSUs5s4Odaya3epqofBu/fMLHEWl8jPmjhA0s9sgj9sNvF1BmaQlmQkA==}
ts-proto-descriptors@2.0.0:
resolution: {integrity: sha512-wHcTH3xIv11jxgkX5OyCSFfw27agpInAd6yh89hKG6zqIXnjW9SYqSER2CVQxdPj4czeOhGagNvZBEbJPy7qkw==}
ts-proto@2.8.3:
resolution: {integrity: sha512-TdXInqG+61pj/TvORqITWjvjTTsL1EZxwX49iEj89+xFAcqPT8tjChpAGQXzfcF4MJwvNiuoCEbBOKqVf3ds3g==}
hasBin: true
tslib@2.8.1: tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@ -1109,6 +1140,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1 '@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.28.5 '@babel/helper-validator-identifier': 7.28.5
'@bufbuild/protobuf@2.10.1': {}
'@emnapi/core@1.7.1': '@emnapi/core@1.7.1':
dependencies: dependencies:
'@emnapi/wasi-threads': 1.1.0 '@emnapi/wasi-threads': 1.1.0
@ -1450,6 +1483,8 @@ snapshots:
caniuse-lite@1.0.30001757: {} caniuse-lite@1.0.30001757: {}
case-anything@2.1.13: {}
chalk@4.1.2: chalk@4.1.2:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
@ -1479,8 +1514,14 @@ snapshots:
deep-is@0.1.4: {} deep-is@0.1.4: {}
detect-libc@1.0.3: {}
detect-libc@2.1.2: {} detect-libc@2.1.2: {}
dprint-node@1.0.8:
dependencies:
detect-libc: 1.0.3
electron-to-chromium@1.5.262: {} electron-to-chromium@1.5.262: {}
escalade@3.2.0: {} escalade@3.2.0: {}
@ -1848,6 +1889,21 @@ snapshots:
dependencies: dependencies:
typescript: 5.9.3 typescript: 5.9.3
ts-poet@6.12.0:
dependencies:
dprint-node: 1.0.8
ts-proto-descriptors@2.0.0:
dependencies:
'@bufbuild/protobuf': 2.10.1
ts-proto@2.8.3:
dependencies:
'@bufbuild/protobuf': 2.10.1
case-anything: 2.1.13
ts-poet: 6.12.0
ts-proto-descriptors: 2.0.0
tslib@2.8.1: tslib@2.8.1:
optional: true optional: true

View File

@ -1,33 +1,37 @@
import { useState } from 'react' import { useState, useEffect } from 'react'
import reactLogo from './assets/react.svg' import { Person, PersonList } from '../items'
import viteLogo from '/vite.svg'
import './App.css' import './App.css'
function App() { function App() {
const [count, setCount] = useState(0) const [people, setPeople] = useState<Person[]>([])
useEffect(() => {
fetch('/api')
.then((res) => res.arrayBuffer())
.then((buffer) => {
const list = PersonList.decode(new Uint8Array(buffer))
setPeople(list.person)
})
.catch((err) => console.error('Failed to fetch people:', err))
}, [])
return ( return (
<> <>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card"> <div className="card">
<button onClick={() => setCount((count) => count + 1)}> <h2>People List</h2>
count is {count} {people.map((person, index) => (
</button> <div key={index}>
<p> <h3>{person.name}</h3>
Edit <code>src/App.tsx</code> and save to test HMR <ul>
</p> {person.opinion.map((op, i) => (
<li key={i}>
{op.game?.title} - {op.wouldPlay ? 'Would Play' : 'Would Not Play'}
</li>
))}
</ul>
</div>
))}
</div> </div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</> </>
) )
} }

View File

@ -3,11 +3,16 @@
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2022", "target": "ES2022",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2022", "DOM", "DOM.Iterable"], "lib": [
"ES2022",
"DOM",
"DOM.Iterable"
],
"module": "ESNext", "module": "ESNext",
"types": ["vite/client"], "types": [
"vite/client"
],
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
@ -15,14 +20,15 @@
"moduleDetection": "force", "moduleDetection": "force",
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true, "erasableSyntaxOnly": false,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": ["src"] "include": [
"src"
]
} }

View File

@ -25,3 +25,5 @@ enum Source {
STEAM = 0; STEAM = 0;
ROBLOX = 1; ROBLOX = 1;
} }
message PersonList { repeated Person person = 1; }