fix: Removed PinningProvider and used react-query directly

This commit is contained in:
Tania Gutierrez 2024-03-21 23:15:56 -04:00
parent 39a8789f95
commit 8b8fa967d0
Signed by: riobuenoDevelops
GPG Key ID: 53133EB28EB7E801
8 changed files with 125 additions and 169 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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
} }
} }

View File

@ -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;

View File

@ -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
}; };
}; };

View File

@ -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>
); );
} }

View File

@ -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

View File

@ -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",