fix: Removed PinningProvider and used react-query directly
This commit is contained in:
parent
39a8789f95
commit
8b8fa967d0
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
|
@ -6,34 +6,35 @@ import {
|
|||
AccordionTrigger,
|
||||
} from "./ui/accordion";
|
||||
import { Progress } from "./ui/progress";
|
||||
import { usePinning } from "~/hooks/usePinnning";
|
||||
import { IPinningData } from "~/providers/PinningProvider";
|
||||
import { usePinning, useUnpinMutation } from "~/hooks/usePinnning";
|
||||
import { Tabs, TabsTrigger, TabsList, TabsContent } from "./ui/tabs";
|
||||
import { Button } from "./ui/button";
|
||||
import { Cross2Icon } from "@radix-ui/react-icons";
|
||||
import { PinningStatus } from "~/data/pinning";
|
||||
|
||||
export const PinningNetworkBanner = () => {
|
||||
const { cidList } = usePinning();
|
||||
const { data} = usePinning();
|
||||
|
||||
// TODO: Adapt to real API
|
||||
const itemsLeft = useMemo(
|
||||
() => cidList.filter((item) => item.progress < 100),
|
||||
[cidList],
|
||||
() => data?.items.filter((item: PinningStatus) => item.status.includes("inprogress")) || [],
|
||||
[data],
|
||||
);
|
||||
|
||||
const completedItems = useMemo(
|
||||
() => cidList.filter((item) => item.progress === 100),
|
||||
[cidList],
|
||||
() => data?.items.filter((item: PinningStatus) => item.status.includes("completed")) || [],
|
||||
[data],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`border border-border rounded-lg absolute w-1/3 bottom-4 right-4 ${
|
||||
!cidList.length ? "hidden" : "block"
|
||||
!data?.items.length ? "hidden" : "block"
|
||||
}`}>
|
||||
<Accordion type="single" defaultValue="item-1" collapsible>
|
||||
<AccordionItem value="item-1">
|
||||
<AccordionTrigger className="font-bold bg-primary px-4 rounded-tr-lg rounded-tl-lg">
|
||||
{`${completedItems.length}/${cidList.length} items completed`}
|
||||
{`${completedItems.length}/${data?.items.length} items completed`}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<Tabs className="w-full" defaultValue="inProgress">
|
||||
|
@ -43,8 +44,8 @@ export const PinningNetworkBanner = () => {
|
|||
</TabsList>
|
||||
<TabsContent value="inProgress">
|
||||
{itemsLeft.length ? (
|
||||
itemsLeft.map((cid) => (
|
||||
<PinCidItem key={cid.cid} item={cid} />
|
||||
itemsLeft.map((item: PinningStatus) => (
|
||||
<PinCidItem key={item.id} item={item} />
|
||||
))
|
||||
) : (
|
||||
<div className="text-primary-2 text-sm flex justify-center items-center h-10">
|
||||
|
@ -54,8 +55,8 @@ export const PinningNetworkBanner = () => {
|
|||
</TabsContent>
|
||||
<TabsContent value="completed">
|
||||
{completedItems.length ? (
|
||||
completedItems.map((cid) => (
|
||||
<PinCidItem key={cid.cid} item={cid} />
|
||||
completedItems.map((item: PinningStatus) => (
|
||||
<PinCidItem key={item.id} item={item} />
|
||||
))
|
||||
) : (
|
||||
<div className="text-muted text-sm flex justify-center items-center h-10">
|
||||
|
@ -71,29 +72,21 @@ export const PinningNetworkBanner = () => {
|
|||
);
|
||||
};
|
||||
|
||||
const PinCidItem = ({ item }: { item: IPinningData }) => {
|
||||
const { getProgress, onRemoveCidFromList } = usePinning();
|
||||
|
||||
useEffect(() => {
|
||||
if (item.progress < 100) {
|
||||
const intervalId = setInterval(() => {
|
||||
getProgress(item.cid);
|
||||
}, 1000); // Adjust the interval time (1000ms = 1 second) as needed
|
||||
|
||||
return () => clearInterval(intervalId); // Clear interval on component unmount
|
||||
}
|
||||
}, [getProgress, item]); // Add dependencies to ensure the effect runs correctly
|
||||
const PinCidItem = ({ item }: { item: PinningStatus }) => {
|
||||
const { mutate } = useUnpinMutation();
|
||||
|
||||
return (
|
||||
<div className="px-4 mb-4">
|
||||
<div className="flex justify-between items-center rounded-lg h-10 py-2 px-4 hover:bg-primary/50 group">
|
||||
<span className="font-semibold">{item.cid}</span>
|
||||
<span className="font-semibold">{item.id}</span>
|
||||
<span className="group-hover:hidden">{item.progress}%</span>
|
||||
<div className="gap-x-2 hidden group-hover:flex">
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="p-2 rounded-full h-6"
|
||||
onClick={() => onRemoveCidFromList(item.cid)}>
|
||||
onClick={() => mutate({
|
||||
cid: item.id,
|
||||
})}>
|
||||
<Cross2Icon />
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
import { SdkProvider } from "~/data/sdk-provider.js";
|
||||
import { PinningProcess } from "./pinning";
|
||||
|
||||
export const pinningProvider = {
|
||||
getList: () => {
|
||||
console.log("Not implemented");
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
};
|
||||
},
|
||||
getOne: () => {
|
||||
console.log("Not implemented");
|
||||
return Promise.resolve({
|
||||
data: {
|
||||
id: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
update: () => {
|
||||
console.log("Not implemented");
|
||||
return Promise.resolve({
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
create: () => {
|
||||
console.log("Not implemented");
|
||||
return Promise.resolve({
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
deleteOne: () => {
|
||||
console.log("Not implemented");
|
||||
return Promise.resolve({
|
||||
data: {},
|
||||
});
|
||||
},
|
||||
getApiUrl: () => "",
|
||||
custom: () => {
|
||||
|
||||
const pinCid = async (cid: string) => {
|
||||
return await PinningProcess.pin(cid);
|
||||
}
|
||||
|
||||
const unpinCid = async (cid: string) => {
|
||||
console.log("Not Implemented");
|
||||
}
|
||||
|
||||
const checkCid = async (cid: string) => {
|
||||
console.log("Not Implemented");
|
||||
}
|
||||
|
||||
const checkCidProgress = (cid: string) => {
|
||||
const progressGenerator = PinningProcess.pollProgress(cid);
|
||||
|
||||
return progressGenerator.next();
|
||||
}
|
||||
|
||||
return {
|
||||
pinCid,
|
||||
unpinCid,
|
||||
checkCid,
|
||||
checkCidProgress
|
||||
}
|
||||
},
|
||||
} satisfies SdkProvider;
|
|
@ -1,4 +1,4 @@
|
|||
interface PinningStatus {
|
||||
export interface PinningStatus {
|
||||
id: string;
|
||||
progress: number;
|
||||
status: 'inprogress' | 'completed' | 'stale';
|
||||
|
@ -30,13 +30,26 @@ export class PinningProcess {
|
|||
return { success: true, message: "Pinning process started" };
|
||||
}
|
||||
|
||||
static *pollProgress(id: string): Generator<PinningStatus | null, void, unknown> {
|
||||
let status = PinningProcess.instances.get(id);
|
||||
while (status && status.status !== 'completed') {
|
||||
yield status;
|
||||
status = PinningProcess.instances.get(id);
|
||||
static async unpin(id: string): Promise<{ success: boolean; message: string }> {
|
||||
if (!PinningProcess.instances.has(id)) {
|
||||
return { success: false, message: "ID not found or not being processed" };
|
||||
}
|
||||
yield status ?? null; // Yield the final status, could be null if ID doesn't exist
|
||||
|
||||
PinningProcess.instances.delete(id);
|
||||
return { success: true, message: "Pinning process removed" }
|
||||
}
|
||||
|
||||
static *pollAllProgress(): Generator<PinningStatus[], void, unknown> {
|
||||
let allStatuses = Array.from(PinningProcess.instances.values());
|
||||
let inProgress = allStatuses.some(status => status.status !== 'completed');
|
||||
|
||||
while (inProgress) {
|
||||
yield allStatuses;
|
||||
allStatuses = Array.from(PinningProcess.instances.values());
|
||||
inProgress = allStatuses.some(status => status.status !== 'completed');
|
||||
}
|
||||
|
||||
yield allStatuses ?? []; // Yield the final statuses
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import {Sdk} from "@lumeweb/portal-sdk";
|
|||
import {accountProvider} from "~/data/account-provider.js";
|
||||
import type {SdkProvider} from "~/data/sdk-provider.js";
|
||||
import {createPortalAuthProvider} from "~/data/auth-provider.js";
|
||||
import { pinningProvider } from "./pinning-provider";
|
||||
|
||||
interface DataProviders {
|
||||
default: SdkProvider;
|
||||
|
@ -25,7 +24,6 @@ export function getProviders(sdk: Sdk) {
|
|||
default: accountProvider,
|
||||
auth: createPortalAuthProvider(sdk),
|
||||
files: fileProvider,
|
||||
pinning: pinningProvider
|
||||
};
|
||||
|
||||
return providers;
|
||||
|
|
|
@ -1,62 +1,73 @@
|
|||
import { useCustom, useNotification } from "@refinedev/core";
|
||||
import { usePinningContext } from "~/providers/PinningProvider";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { PinningProcess } from "~/data/pinning";
|
||||
|
||||
export const usePinning = () => {
|
||||
const { open } = useNotification();
|
||||
const { data: cidList, setData } = usePinningContext();
|
||||
// TODO: Adapt to real API
|
||||
|
||||
const { data } = useCustom({
|
||||
// useCustom requires URL and METHOD params
|
||||
url: "",
|
||||
method: "get",
|
||||
dataProviderName: "pinning",
|
||||
export const usePinMutation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: async ({ cid }) => {
|
||||
const response = await PinningProcess.pin(cid);
|
||||
|
||||
if (!response.success) {
|
||||
open?.({
|
||||
type: "destructive",
|
||||
message: "Erorr pinning " + cid,
|
||||
description: response.message,
|
||||
});
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["pin-progress"] })
|
||||
}
|
||||
});
|
||||
|
||||
const onPin = async (cid: string) => {
|
||||
if (!data?.pinCid) return;
|
||||
return { mutate }
|
||||
}
|
||||
|
||||
const response = await data?.pinCid(cid);
|
||||
export const useUnpinMutation = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutate } = useMutation({
|
||||
mutationFn: async ({ cid }) => {
|
||||
const response = await PinningProcess.unpin(cid);
|
||||
|
||||
if (response.success) {
|
||||
setData((prev) => [...prev, { cid, progress: 0 }]);
|
||||
} else {
|
||||
open?.({
|
||||
type: "destrunctive",
|
||||
message: "Erorr pinning " + cid,
|
||||
description: response.message,
|
||||
});
|
||||
if (!response.success) {
|
||||
open?.({
|
||||
type: "destructive",
|
||||
message: "Erorr pinning " + cid,
|
||||
description: response.message,
|
||||
});
|
||||
}
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["pin-progress"] })
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const getProgress = (cid: string) => {
|
||||
if (!data?.checkCidProgress) return;
|
||||
return { mutate }
|
||||
}
|
||||
|
||||
const response = data?.checkCidProgress(cid);
|
||||
export const usePinning = () => {
|
||||
const { data } = useQuery({
|
||||
queryKey: ["pin-progress"],
|
||||
refetchInterval: (query) => {
|
||||
if (!query.state.data || !query.state.data.items.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!response.done) {
|
||||
setData((prev) =>
|
||||
prev.map((cidInfo) => {
|
||||
const newData = cidInfo;
|
||||
return 1000;
|
||||
},
|
||||
refetchIntervalInBackground: true,
|
||||
queryFn: () => {
|
||||
const response = PinningProcess.pollAllProgress();
|
||||
const result = response.next();
|
||||
|
||||
if (cidInfo.cid === cid) {
|
||||
newData.progress = response.value.progress;
|
||||
|
||||
return newData;
|
||||
}
|
||||
return cidInfo;
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const onRemoveCidFromList = (cid: string) => {
|
||||
setData((prev) => prev.filter((item) => item.cid !== cid));
|
||||
};
|
||||
return {
|
||||
items: result.value || [],
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
cidList,
|
||||
onPin,
|
||||
getProgress,
|
||||
onRemoveCidFromList,
|
||||
data
|
||||
};
|
||||
};
|
||||
|
|
36
app/root.tsx
36
app/root.tsx
|
@ -13,11 +13,14 @@ import { Toaster } from "~/components/ui/toaster";
|
|||
import {getProviders} from "~/data/providers.js";
|
||||
import {Sdk} from "@lumeweb/portal-sdk";
|
||||
import resources from "~/data/resources.js";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
|
||||
export const links: LinksFunction = () => [
|
||||
{ rel: "stylesheet", href: stylesheet },
|
||||
];
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export function Layout({children}: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
|
@ -42,22 +45,23 @@ export default function App() {
|
|||
const sdk = Sdk.create(import.meta.env.VITE_PORTAL_URL)
|
||||
const providers = getProviders(sdk);
|
||||
return (
|
||||
<Refine
|
||||
authProvider={providers.auth}
|
||||
routerProvider={routerProvider}
|
||||
notificationProvider={notificationProvider}
|
||||
dataProvider={{
|
||||
default: providers.default,
|
||||
files: providers.files,
|
||||
pinning: providers.pinning
|
||||
}}
|
||||
resources={resources}
|
||||
options={{disableTelemetry: true}}
|
||||
>
|
||||
<SdkContextProvider sdk={sdk}>
|
||||
<Outlet/>
|
||||
</SdkContextProvider>
|
||||
</Refine>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Refine
|
||||
authProvider={providers.auth}
|
||||
routerProvider={routerProvider}
|
||||
notificationProvider={notificationProvider}
|
||||
dataProvider={{
|
||||
default: providers.default,
|
||||
files: providers.files
|
||||
}}
|
||||
resources={resources}
|
||||
options={{disableTelemetry: true}}
|
||||
>
|
||||
<SdkContextProvider sdk={sdk}>
|
||||
<Outlet/>
|
||||
</SdkContextProvider>
|
||||
</Refine>
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
DropdownMenuTrigger,
|
||||
} from "~/components/ui/dropdown-menu";
|
||||
|
||||
import { usePinning } from "~/hooks/usePinnning";
|
||||
import { usePinMutation } from "~/hooks/usePinnning";
|
||||
import { cn } from "~/utils";
|
||||
|
||||
// This type is used to define the shape of our data.
|
||||
|
@ -25,7 +25,7 @@ export type File = {
|
|||
|
||||
const CreatedOnCell = ({ row }: { row: Row<File> }) => {
|
||||
// const { open } = useNotification();
|
||||
const { onPin } = usePinning();
|
||||
const { mutate } = usePinMutation();
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between">
|
||||
|
@ -43,7 +43,9 @@ const CreatedOnCell = ({ row }: { row: Row<File> }) => {
|
|||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
console.log(`Adding ${row.getValue("cid")} for pinning...`);
|
||||
onPin(row.getValue("cid"));
|
||||
mutate({
|
||||
cid: row.getValue("cid"),
|
||||
});
|
||||
}}>
|
||||
<DrawingPinIcon className="mr-2" />
|
||||
Pin CID
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
"@refinedev/remix-router": "https://gitpkg.now.sh/LumeWeb/refine/packages/remix?remix",
|
||||
"@remix-run/node": "^2.8.0",
|
||||
"@remix-run/react": "^2.8.0",
|
||||
"@tanstack/react-query": "^5.28.6",
|
||||
"@tanstack/react-table": "^8.13.2",
|
||||
"@uppy/core": "^3.9.3",
|
||||
"@uppy/tus": "^3.5.3",
|
||||
|
|
Loading…
Reference in New Issue