Add QueryClientProvider for react-query support

Integrate react-query by adding `QueryClientProvider` to the application. This involves updating dependencies, wrapping existing providers with `QueryProvider`, and refactoring the `account-info` module to utilize new hooks and APIs.
This commit is contained in:
Mathis H (Avnyr) 2024-11-14 16:04:14 +01:00
parent a75a87f683
commit ea7ab60e5c
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
5 changed files with 143 additions and 98 deletions

View File

@ -41,6 +41,7 @@
"@radix-ui/react-toggle": "^1.1.0", "@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0", "@radix-ui/react-toggle-group": "^1.1.0",
"@radix-ui/react-tooltip": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.4",
"@tanstack/react-query": "^5.60.2",
"@tanstack/react-table": "^8.20.5", "@tanstack/react-table": "^8.20.5",
"axios": "^1.7.7", "axios": "^1.7.7",
"class-variance-authority": "^0.7.0", "class-variance-authority": "^0.7.0",

18
pnpm-lock.yaml generated
View File

@ -95,6 +95,9 @@ importers:
'@radix-ui/react-tooltip': '@radix-ui/react-tooltip':
specifier: ^1.1.4 specifier: ^1.1.4
version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@tanstack/react-query':
specifier: ^5.60.2
version: 5.60.2(react@18.3.1)
'@tanstack/react-table': '@tanstack/react-table':
specifier: ^8.20.5 specifier: ^8.20.5
version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -1246,6 +1249,14 @@ packages:
'@swc/helpers@0.5.5': '@swc/helpers@0.5.5':
resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==}
'@tanstack/query-core@5.59.20':
resolution: {integrity: sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==}
'@tanstack/react-query@5.60.2':
resolution: {integrity: sha512-JhpJNxIAPuE0YCpP1Py4zAsgx+zY0V531McRMtQbwVlJF8+mlZwcOPrzGmPV248K8IP+mPbsfxXToVNMNwjUcw==}
peerDependencies:
react: ^18 || ^19
'@tanstack/react-table@8.20.5': '@tanstack/react-table@8.20.5':
resolution: {integrity: sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==} resolution: {integrity: sha512-WEHopKw3znbUZ61s9i0+i9g8drmDo6asTWbrQh8Us63DAk/M0FkmIqERew6P71HI75ksZ2Pxyuf4vvKh9rAkiA==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -3850,6 +3861,13 @@ snapshots:
'@swc/counter': 0.1.3 '@swc/counter': 0.1.3
tslib: 2.6.3 tslib: 2.6.3
'@tanstack/query-core@5.59.20': {}
'@tanstack/react-query@5.60.2(react@18.3.1)':
dependencies:
'@tanstack/query-core': 5.59.20
react: 18.3.1
'@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@tanstack/react-table@8.20.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@tanstack/table-core': 8.20.5 '@tanstack/table-core': 8.20.5

View File

@ -1,4 +1,5 @@
"use client"; "use client";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
Dialog, Dialog,
@ -9,20 +10,89 @@ import {
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import type { IUserData, IUserWallet } from "@/interfaces/userdata.interface"; import type { IUserData, IUserWallet } from "@/interfaces/userdata.interface";
import { CopyButton } from "@/components/ui/copy-button"; import { CopyButton } from "@/components/ui/copy-button";
import { import type {
type ICryptoInUserWalletInfo, ICryptoInUserWalletInfo,
ICryptoInWalletInfo,
} from "@/interfaces/crypto.interface"; } from "@/interfaces/crypto.interface";
import { doDisconnect, getWallet } from "@/services/account.handler";
import { Bitcoin, Fingerprint, Key, Landmark, Unplug, User, Wallet } from "lucide-react"; import { Bitcoin, Fingerprint, Key, Landmark, Unplug, User, Wallet } from "lucide-react";
import Link from "next/link"; import Link from "next/link";
import type React from "react"; import type React from "react";
import { useEffect, useState } from "react"; import { useEffect, useState, useCallback, useMemo } from "react";
import { EReturnState, type IStandardisedReturn } from "@/interfaces/general.interface";
import type { IApiUserAssetsRes } from "@/interfaces/api.interface";
import ApiRequest from "@/services/apiRequest";
export function doDisconnect() {
if (typeof window !== "undefined") {
window.localStorage.removeItem("sub");
//Redirect to homepage
window.location.href = "/";
return true;
}
console.log("Whut ? Why trying to remove an item from the localStorage when running in SSR ?");
return false;
}
async function getWallet(): Promise<IStandardisedReturn<IApiUserAssetsRes>> {
try {
const ReqRes =
await ApiRequest.authenticated.get.json<IStandardisedReturn<IApiUserAssetsRes>>(
"user/my-assets"
);
console.log(ReqRes.data);
if (ReqRes.status !== 200) {
return {
state: EReturnState.clientError,
};
}
return {
state: EReturnState.done,
resolved: ReqRes.data,
};
} catch (err) {
return {
state: EReturnState.serverError,
};
}
}
function useWallet(userData: IUserData, setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>) {
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
if (!isLoaded) {
getWallet().then((res) => {
const wallet: IUserWallet = {
uat: Date.now(),
update_interval: 30000,
owned_cryptos: res.resolved?.UserHasCrypto?.map((el): ICryptoInUserWalletInfo => ({
id: el.Crypto.id,
name: el.Crypto.name,
value: el.Crypto.value,
image: el.Crypto.image,
quantity: el.Crypto.quantity,
owned_amount: el.amount,
created_at: el.Crypto.created_at,
updated_at: el.Crypto.updated_at,
})) || [],
};
delete res.resolved?.UserHasCrypto;
setUserData((prev) => ({
...prev,
...res.resolved,
wallet,
}) as unknown as IUserData);
console.log(userData);
setIsLoaded(true);
});
}
}, [isLoaded, userData, setUserData]);
return isLoaded;
}
export function AccountInfo({ export function AccountInfo({
userData, userData,
@ -33,59 +103,43 @@ export function AccountInfo({
setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>; setUserData: React.Dispatch<React.SetStateAction<IUserData | undefined>>;
isDisconnected: boolean; isDisconnected: boolean;
}) { }) {
const [isLoaded, setIsLoaded] = useState(false); const isLoaded = useWallet(userData, setUserData);
useEffect(() => { const walletInfo = useMemo(() => (
if (!isLoaded) { <div className={"flex flex-col md:flex-row gap-2 justify-center md:justify-evenly items-start md:items-center w-full"}>
getWallet().then((res) => { <div className={"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"}>
const wallet: IUserWallet = { <Landmark />
uat: Date.now(), <p className={"rounded bg-accent text-accent-foreground p-1"}>{userData.dollarAvailables} $</p>
update_interval: 30_000, </div>
owned_cryptos: <div className={"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"}>
res.resolved?.UserHasCrypto?.map((el): ICryptoInUserWalletInfo => { <Bitcoin />
return { <p className={"rounded bg-accent text-accent-foreground p-1"}>{`You currently have ${userData.wallet.owned_cryptos.length} crypto(s)`}</p>
id: el.Crypto.id, </div>
name: el.Crypto.name, </div>
value: el.Crypto.value, ), [userData.dollarAvailables, userData.wallet.owned_cryptos.length]);
image: el.Crypto.image,
quantity: el.Crypto.quantity, const userInfo = useMemo(() => (
owned_amount: el.amount, <div className={"flex flex-col gap-3 justify-center items-start mx-auto mt-4"}>
created_at: el.Crypto.created_at, <div className={"flex flex-row text-nowrap flex-nowrap gap-1 text-primary"}>
updated_at: el.Crypto.updated_at, <Fingerprint />
}; <h2>Your identity</h2>
}) || [], </div>
}; <div className={"font-light text-xs md:text-sm flex flex-row items-center justify-start gap-1 bg-accent p-2 rounded"}>
<p>{userData.id}</p>
<CopyButton value={userData.id} />
</div>
</div>
), [userData.id]);
delete res.resolved?.UserHasCrypto;
//@ts-ignore
setUserData({
...userData,
...res.resolved,
wallet: wallet,
});
console.log(userData);
setIsLoaded(true);
});
}
}, [isLoaded, userData, setUserData]);
if (isDisconnected) { if (isDisconnected) {
return ( return (
<div className={"flex flex-col justify-center items-center h-10 p-2 text-xs mt-2"}> <div className={"flex flex-col justify-center items-center h-10 p-2 text-xs mt-2"}>
<div <div className={"flex flex-row justify-center items-center gap-1 text-destructive to-red-900 animate-pulse"}>
className={
"flex flex-row justify-center items-center gap-1 text-destructive to-red-900 animate-pulse"
}
>
<Unplug className={"w-4"} /> <Unplug className={"w-4"} />
<p>Disconnected</p> <p>Disconnected</p>
</div> </div>
<div> <div>
<Link <Link href={"/auth"} className={"hover:text-primary flex justify-evenly items-center gap-1 p-1 text-nowrap"}>
href={"/auth"}
className={
"hover:text-primary flex justify-evenly items-center gap-1 p-1 text-nowrap"
}
>
<Key className={"w-3"} /> Link account <Key className={"w-3"} /> Link account
</Link> </Link>
</div> </div>
@ -107,50 +161,8 @@ export function AccountInfo({
<DialogDescription>{userData.pseudo}</DialogDescription> <DialogDescription>{userData.pseudo}</DialogDescription>
</DialogHeader> </DialogHeader>
<div className={"flex flex-col items-center justify-center w-full"}> <div className={"flex flex-col items-center justify-center w-full"}>
<div className={"flex flex-col justify-evenly items-center gap-2"}> {walletInfo}
<div {userInfo}
className={
"flex flex-col md:flex-row gap-2 justify-center md:justify-evenly items-start md:items-center w-full"
}
>
<div
className={
"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"
}
>
<Landmark />
<p className={"rounded bg-accent text-accent-foreground p-1"}>
{userData.dollarAvailables} $
</p>
</div>
<div
className={
"flex gap-1 justify-start md:justify-center items-center mx-auto w-full md:w-fit"
}
>
<Bitcoin />
<p className={"rounded bg-accent text-accent-foreground p-1"}>
{`You currently have ${userData.wallet.owned_cryptos.length} crypto(s)`}
</p>
</div>
</div>
<div
className={"flex flex-col gap-3 justify-center items-start mx-auto mt-4"}
>
<div className={"flex flex-row text-nowrap flex-nowrap gap-1 text-primary"}>
<Fingerprint />
<h2>Your identity</h2>
</div>
<div
className={
"font-light text-xs md:text-sm flex flex-row items-center justify-start gap-1 bg-accent p-2 rounded"
}
>
<p>{userData.id}</p>
<CopyButton value={userData.id} />
</div>
</div>
</div>
</div> </div>
<DialogFooter> <DialogFooter>
<Button variant={"secondary"} className={"gap-2 px-2"} asChild> <Button variant={"secondary"} className={"gap-2 px-2"} asChild>
@ -159,11 +171,7 @@ export function AccountInfo({
<p>My wallet</p> <p>My wallet</p>
</Link> </Link>
</Button> </Button>
<Button <Button variant={"destructive"} className={"gap-2 px-2 mb-2"} onClick={() => doDisconnect()}>
variant={"destructive"}
className={"gap-2 px-2 mb-2"}
onClick={() => doDisconnect()}
>
<Unplug /> <Unplug />
<p>Disconnect</p> <p>Disconnect</p>
</Button> </Button>

View File

@ -4,11 +4,16 @@ import { Header } from "@/components/header";
import { ThemeProvider } from "@/components/providers/theme-provider"; import { ThemeProvider } from "@/components/providers/theme-provider";
import { UserDataProvider } from "@/components/providers/userdata-provider"; import { UserDataProvider } from "@/components/providers/userdata-provider";
import type React from "react"; import type React from "react";
import {QueryProvider} from "@/components/providers/query-provider";
export function Providers({ children }: { children: React.ReactNode }) { export function Providers({ children }: { children: React.ReactNode }) {
return ( return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem> <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<UserDataProvider>{children}</UserDataProvider> <QueryProvider>
<UserDataProvider>
{children}
</UserDataProvider>
</QueryProvider>
</ThemeProvider> </ThemeProvider>
); );
} }

View File

@ -0,0 +1,13 @@
"use client"
import type {ReactNode} from "react";
import {QueryClient, QueryClientProvider} from "@tanstack/react-query";
interface QueryProviderProps {
children: ReactNode;
}
export function QueryProvider({ children }: QueryProviderProps) {
return (<QueryClientProvider client={new QueryClient()}>
{ children }
</QueryClientProvider>)
}