react query
This commit is contained in:
parent
2a94a47a96
commit
25a3adea2a
25
tank-frontend/package-lock.json
generated
25
tank-frontend/package-lock.json
generated
|
@ -8,6 +8,7 @@
|
||||||
"name": "tank-frontend",
|
"name": "tank-frontend",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.29.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-use-websocket": "^4.8.1"
|
"react-use-websocket": "^4.8.1"
|
||||||
|
@ -1188,6 +1189,30 @@
|
||||||
"win32"
|
"win32"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"node_modules/@tanstack/query-core": {
|
||||||
|
"version": "5.29.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.29.0.tgz",
|
||||||
|
"integrity": "sha512-WgPTRs58hm9CMzEr5jpISe8HXa3qKQ8CxewdYZeVnA54JrPY9B1CZiwsCoLpLkf0dGRZq+LcX5OiJb0bEsOFww==",
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tanstack/react-query": {
|
||||||
|
"version": "5.29.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.29.2.tgz",
|
||||||
|
"integrity": "sha512-nyuWILR4u7H5moLGSiifLh8kIqQDLNOHGuSz0rcp+J75fNc8aQLyr5+I2JCHU3n+nJrTTW1ssgAD8HiKD7IFBQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@tanstack/query-core": "5.29.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/tannerlinsley"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "^18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/babel__core": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tanstack/react-query": "^5.29.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-use-websocket": "^4.8.1"
|
"react-use-websocket": "^4.8.1"
|
||||||
|
|
|
@ -44,8 +44,8 @@ export default function App() {
|
||||||
<ClientScreen logout={logout} theme={theme} playerId={nameId.id}/>
|
<ClientScreen logout={logout} theme={theme} playerId={nameId.id}/>
|
||||||
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
|
{nameId.name === '' && <JoinForm setNameId={setNameId} clientId={nameId.id}/>}
|
||||||
<Row className='GadgetRows'>
|
<Row className='GadgetRows'>
|
||||||
{isLoggedIn && <Controls playerId={nameId.id} logout={logout}/>}
|
{isLoggedIn && <Controls playerId={nameId.id}/>}
|
||||||
{isLoggedIn && <PlayerInfo playerId={nameId.id} logout={logout}/>}
|
{isLoggedIn && <PlayerInfo playerId={nameId.id}/>}
|
||||||
<Scoreboard/>
|
<Scoreboard/>
|
||||||
</Row>
|
</Row>
|
||||||
</Column>;
|
</Column>;
|
||||||
|
|
|
@ -1,19 +1,17 @@
|
||||||
import './Controls.css';
|
import './Controls.css';
|
||||||
import useWebSocket, {ReadyState} from 'react-use-websocket';
|
import useWebSocket, {ReadyState} from 'react-use-websocket';
|
||||||
import {useEffect} from 'react';
|
import {useEffect} from 'react';
|
||||||
|
import {Guid} from "./Guid.ts";
|
||||||
|
|
||||||
export default function Controls({playerId, logout}: {
|
export default function Controls({playerId}: { playerId: Guid }) {
|
||||||
playerId: string,
|
|
||||||
logout: () => void
|
|
||||||
}) {
|
|
||||||
const url = new URL('controls', import.meta.env.VITE_TANK_WS);
|
const url = new URL('controls', import.meta.env.VITE_TANK_WS);
|
||||||
url.searchParams.set('playerId', playerId);
|
url.searchParams.set('playerId', playerId);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
getWebSocket,
|
getWebSocket,
|
||||||
readyState
|
readyState
|
||||||
} = useWebSocket(url.toString(), {
|
} = useWebSocket(url.toString(), {
|
||||||
onError: logout,
|
|
||||||
shouldReconnect: () => true,
|
shouldReconnect: () => true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,40 @@
|
||||||
import {useEffect, useState} from 'react';
|
import {useQuery} from '@tanstack/react-query'
|
||||||
import {Player, getPlayer} from './serverCalls';
|
import {Player} from './serverCalls';
|
||||||
import {Guid} from "./Guid.ts";
|
import {Guid} from "./Guid.ts";
|
||||||
import Column from "./components/Column.tsx";
|
import Column from "./components/Column.tsx";
|
||||||
|
|
||||||
export default function PlayerInfo({playerId, logout}: {
|
export default function PlayerInfo({playerId}: { playerId: Guid }) {
|
||||||
playerId: Guid,
|
const query = useQuery({
|
||||||
logout: () => void
|
queryKey: ['player'],
|
||||||
}) {
|
refetchInterval: 1000,
|
||||||
const [player, setPlayer] = useState<Player | null>();
|
queryFn: async () => {
|
||||||
|
const url = new URL('/player', import.meta.env.VITE_TANK_API);
|
||||||
|
url.searchParams.set('id', playerId);
|
||||||
|
|
||||||
useEffect(() => {
|
const response = await fetch(url, {method: 'GET'});
|
||||||
const refresh = () => {
|
if (!response.ok)
|
||||||
getPlayer(playerId).then(response => {
|
throw new Error(`response failed with code ${response.status} (${response.status})${await response.text()}`)
|
||||||
if (response.successResult)
|
return await response.json() as Player;
|
||||||
setPlayer(response.successResult);
|
}
|
||||||
else
|
});
|
||||||
logout();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const timer = setInterval(refresh, 5000);
|
|
||||||
return () => clearInterval(timer);
|
|
||||||
}, [playerId]);
|
|
||||||
|
|
||||||
return <Column className='PlayerInfo'>
|
return <Column className='PlayerInfo'>
|
||||||
<h3>
|
<h3>
|
||||||
{player ? `Playing as ${player?.name}` : 'loading...'}
|
{query.isPending && 'loading...'}
|
||||||
|
{query.isSuccess && `Playing as ${query.data.name}`}
|
||||||
</h3>
|
</h3>
|
||||||
<table>
|
{query.isError && <p>{query.error.message}</p>}
|
||||||
|
{query.isSuccess && <table>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>kills:</td>
|
<td>kills:</td>
|
||||||
<td>{player?.scores.kills}</td>
|
<td>{query.data?.scores?.kills ?? '?'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>deaths:</td>
|
<td>deaths:</td>
|
||||||
<td>{player?.scores.deaths}</td>
|
<td>{query.data?.scores?.deaths ?? '?'}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>}
|
||||||
</Column>;
|
</Column>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
import {getScores, Player} from "./serverCalls.tsx";
|
import {Player} from "./serverCalls.tsx";
|
||||||
import {useEffect, useState} from "react";
|
|
||||||
import DataTable from "./components/DataTable.tsx";
|
import DataTable from "./components/DataTable.tsx";
|
||||||
|
import {useQuery} from "@tanstack/react-query";
|
||||||
|
|
||||||
function numberSorter(a: number, b: number) {
|
function numberSorter(a: number, b: number) {
|
||||||
return b - a;
|
return b - a;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Scoreboard({}: {}) {
|
export default function Scoreboard({}: {}) {
|
||||||
const [players, setPlayers] = useState<Player[]>([]);
|
const query = useQuery({
|
||||||
|
queryKey: ['scores'],
|
||||||
|
refetchInterval: 1000,
|
||||||
|
queryFn: async () => {
|
||||||
|
const url = new URL('/scores', import.meta.env.VITE_TANK_API);
|
||||||
|
const response = await fetch(url, {method: 'GET'});
|
||||||
|
if (!response.ok)
|
||||||
|
throw new Error(`response failed with code ${response.status} (${response.status})${await response.text()}`)
|
||||||
|
return await response.json() as Player[];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
if (query.isError)
|
||||||
const refresh = () => {
|
return <p>{query.error.message}</p>;
|
||||||
getScores().then(value => {
|
if (query.isPending)
|
||||||
if (value.successResult)
|
return <p>loading...</p>;
|
||||||
setPlayers(value.successResult);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const timer = setInterval(refresh, 5000);
|
|
||||||
return () => clearInterval(timer);
|
|
||||||
}, [players]);
|
|
||||||
|
|
||||||
return <DataTable
|
return <DataTable
|
||||||
data={players}
|
data={query.data}
|
||||||
columns={[
|
columns={[
|
||||||
{field: 'name'},
|
{field: 'name'},
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,14 @@ import React from 'react';
|
||||||
import {createRoot} from 'react-dom/client';
|
import {createRoot} from 'react-dom/client';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
|
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
|
||||||
|
|
||||||
|
const queryClient = new QueryClient()
|
||||||
|
|
||||||
createRoot(document.getElementById('root')!).render(
|
createRoot(document.getElementById('root')!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App/>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<App/>
|
||||||
|
</QueryClientProvider>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
);
|
);
|
||||||
|
|
|
@ -44,15 +44,3 @@ export function postPlayer({name, id}: NameId) {
|
||||||
|
|
||||||
return fetchTyped<NameId>({url, method: 'POST'});
|
return fetchTyped<NameId>({url, method: 'POST'});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPlayer(id: Guid) {
|
|
||||||
const url = new URL('/player', import.meta.env.VITE_TANK_API);
|
|
||||||
url.searchParams.set('id', id);
|
|
||||||
|
|
||||||
return fetchTyped<Player>({url, method: 'GET'});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getScores() {
|
|
||||||
const url = new URL('/scores', import.meta.env.VITE_TANK_API);
|
|
||||||
return fetchTyped<Player[]>({url, method: 'GET'});
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue