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