fix: small fixes

This commit is contained in:
Juan Di Toro 2024-03-22 18:53:39 +01:00
parent e1ca45f1f1
commit c9de506c56
5 changed files with 331 additions and 274 deletions

View File

@ -1,51 +1,62 @@
import { useMutation } from "@tanstack/react-query"; import { useNotification } from "@refinedev/core";
import { useContext } from "react"; import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useCallback, useContext } from "react";
import { PinningProcess } from "~/data/pinning"; import { PinningProcess } from "~/data/pinning";
import { PinningContext } from "~/providers/PinningProvider"; import { PinningContext } from "~/providers/PinningProvider";
// TODO: Adapt to real API
export const usePinning = () => { export const usePinning = () => {
const queryClient = useQueryClient();
const context = useContext(PinningContext); const context = useContext(PinningContext);
const { open } = useNotification();
const { mutate } = useMutation({ const { mutate: pinMutation } = useMutation({
mutationKey: ["pin-progress"], mutationKey: ["pin-mutation"],
mutationFn: async (variables: { cid: string, type: "pin" | "unpin" }) => { mutationFn: async (variables: { cid: string }) => {
const { cid, type } = variables; const { cid } = variables;
switch (type) {
case "pin": {
const response = await PinningProcess.pin(cid); const response = await PinningProcess.pin(cid);
if (!response.success) { if (!response.success) {
open?.({ open?.({
type: "destructive", type: "error",
message: "Erorr pinning " + cid, message: `Error pinning ${cid}`,
description: response.message, description: response.message,
}); });
} }
break;
} queryClient.invalidateQueries({ queryKey: ["pin-progress"] });
case "unpin": { },
});
const { mutate: unpinMutation } = useMutation({
mutationKey: ["unpin-mutation"],
mutationFn: async (variables: { cid: string }) => {
const { cid } = variables;
const response = await PinningProcess.unpin(cid); const response = await PinningProcess.unpin(cid);
if (!response.success) { if (!response.success) {
open?.({ open?.({
type: "destructive", type: "error",
message: "Erorr removing " + cid, message: `Error removing ${cid}`,
description: response.message, description: response.message,
}); });
} }
queryClient.invalidateQueries({ queryKey: ["pin-progress"] });
break; },
}
}
context.queryClient.invalidateQueries({ queryKey: ["pin-progress"] })
}
}); });
const bulkPin = useCallback(
(cids: string[]) => {
for (const cid of cids) {
pinMutation({ cid });
}
},
[pinMutation],
);
return { return {
...context.query, ...context.query,
mutate pin: pinMutation,
unpin: unpinMutation,
bulkPin,
}; };
}; };

View File

@ -1,11 +1,11 @@
import { import {
QueryClient, type QueryClient,
UseQueryResult, type UseQueryResult,
useQuery, useQuery,
useQueryClient, useQueryClient,
} from "@tanstack/react-query"; } from "@tanstack/react-query";
import { createContext, useContext } from "react"; import { createContext, useContext } from "react";
import { PinningProcess, PinningStatus } from "~/data/pinning"; import { PinningProcess, type PinningStatus } from "~/data/pinning";
export interface IPinningData { export interface IPinningData {
cid: string; cid: string;

View File

@ -1,41 +1,47 @@
import {Links, Meta, Outlet, Scripts, ScrollRestoration,} from "@remix-run/react"; import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
} from "@remix-run/react";
import stylesheet from "./tailwind.css?url"; import stylesheet from "./tailwind.css?url";
import type {LinksFunction} from "@remix-run/node"; import type { LinksFunction } from "@remix-run/node";
// Supports weights 200-800 // Supports weights 200-800
import '@fontsource-variable/manrope'; import "@fontsource-variable/manrope";
import {Refine} from "@refinedev/core"; import { Refine } from "@refinedev/core";
import routerProvider from "@refinedev/remix-router"; import routerProvider from "@refinedev/remix-router";
import {notificationProvider} from "~/data/notification-provider"; import { notificationProvider } from "~/data/notification-provider";
import {SdkContextProvider, useSdk} from "~/components/lib/sdk-context"; import { SdkContextProvider, useSdk } from "~/components/lib/sdk-context";
import {Toaster} from "~/components/ui/toaster"; 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"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import {useMemo} from "react"; import { useMemo } from "react";
export const links: LinksFunction = () => [ export const links: LinksFunction = () => [
{rel: "stylesheet", href: stylesheet}, { rel: "stylesheet", href: stylesheet },
]; ];
const queryClient = new QueryClient(); 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">
<head> <head>
<meta charSet="utf-8"/> <meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1"/> <meta name="viewport" content="width=device-width, initial-scale=1" />
<Meta/> <Meta />
<Links/> <Links />
</head> </head>
<body className="overflow-hidden"> <body className="overflow-hidden">
{children} {children}
<Toaster/> <Toaster />
<ScrollRestoration/> <ScrollRestoration />
<Scripts/> <Scripts />
</body> </body>
</html> </html>
); );
@ -52,24 +58,21 @@ function App() {
notificationProvider={notificationProvider} notificationProvider={notificationProvider}
dataProvider={{ dataProvider={{
default: providers.default, default: providers.default,
files: providers.files files: providers.files,
}} }}
resources={resources} resources={resources}
options={{disableTelemetry: true}} options={{ disableTelemetry: true }}>
> <Outlet />
<SdkContextProvider sdk={sdk}>
<Outlet/>
</SdkContextProvider>
</Refine> </Refine>
</QueryClientProvider> </QueryClientProvider>
); );
} }
export default function Root() { export default function Root() {
const sdk = Sdk.create(import.meta.env.VITE_PORTAL_URL) const sdk = Sdk.create(import.meta.env.VITE_PORTAL_URL);
return ( return (
<SdkContextProvider sdk={sdk}> <SdkContextProvider sdk={sdk}>
<App/> <App />
</SdkContextProvider> </SdkContextProvider>
); );
} }

View File

@ -41,7 +41,7 @@ import { Input } from "~/components/ui/input";
import { UsageCard } from "~/components/usage-card"; import { UsageCard } from "~/components/usage-card";
import QRImg from "~/images/QR.png"; import QRImg from "~/images/QR.png";
import type {UpdatePasswordFormRequest} from "~/data/auth-provider"; import type { UpdatePasswordFormRequest } from "~/data/auth-provider";
export default function MyAccount() { export default function MyAccount() {
const { data: identity } = useGetIdentity<{ email: string }>(); const { data: identity } = useGetIdentity<{ email: string }>();
@ -85,9 +85,13 @@ export default function MyAccount() {
<ManagementCard> <ManagementCard>
<ManagementCardAvatar <ManagementCardAvatar
button={ button={
<DialogTrigger asChild className="absolute bottom-0 right-0 z-50"> <DialogTrigger
asChild
className="absolute bottom-0 right-0 z-50">
<Button <Button
onClick={() => setModal({ ...openModal, changeAvatar: true })} onClick={() =>
setModal({ ...openModal, changeAvatar: true })
}
variant="outline" variant="outline"
className=" flex items-center w-10 h-10 p-0 border-white rounded-full justiyf-center hover:bg-secondary-2"> className=" flex items-center w-10 h-10 p-0 border-white rounded-full justiyf-center hover:bg-secondary-2">
<EditIcon /> <EditIcon />
@ -105,7 +109,9 @@ export default function MyAccount() {
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
className="h-12 gap-x-2" className="h-12 gap-x-2"
onClick={() => setModal({ ...openModal, changeEmail: true })}> onClick={() =>
setModal({ ...openModal, changeEmail: true })
}>
<AddIcon /> <AddIcon />
Change Email Address Change Email Address
</Button> </Button>
@ -147,7 +153,9 @@ export default function MyAccount() {
</ManagementCardFooter> </ManagementCardFooter>
</ManagementCard> </ManagementCard>
<ManagementCard> <ManagementCard>
<ManagementCardTitle>Two-Factor Authentication</ManagementCardTitle> <ManagementCardTitle>
Two-Factor Authentication
</ManagementCardTitle>
<ManagementCardContent> <ManagementCardContent>
Improve security by enabling 2FA. Improve security by enabling 2FA.
</ManagementCardContent> </ManagementCardContent>
@ -313,7 +321,8 @@ const ChangePasswordSchema = z
}); });
const ChangePasswordForm = () => { const ChangePasswordForm = () => {
const { mutate: updatePassword } = useUpdatePassword<UpdatePasswordFormRequest>(); const { mutate: updatePassword } =
useUpdatePassword<UpdatePasswordFormRequest>();
const [form, fields] = useForm({ const [form, fields] = useForm({
id: "login", id: "login",
constraint: getZodConstraint(ChangePasswordSchema), constraint: getZodConstraint(ChangePasswordSchema),

View File

@ -14,6 +14,10 @@ import {
DialogTrigger, DialogTrigger,
} from "~/components/ui/dialog"; } from "~/components/ui/dialog";
import { Field } from "~/components/forms"; import { Field } from "~/components/forms";
import { z } from "zod";
import { getFormProps, useForm } from "@conform-to/react";
import { getZodConstraint, parseWithZod } from "@conform-to/zod";
import { usePinning } from "~/hooks/usePinning";
export default function FileManager() { export default function FileManager() {
return ( return (
@ -72,25 +76,55 @@ export default function FileManager() {
dataProviderName="files" dataProviderName="files"
/> />
<DialogContent> <DialogContent>
<DialogHeader> <PinFilesForm />
<DialogTitle>Pin Content</DialogTitle>
</DialogHeader>
<form action="" className="w-full flex flex-col gap-y-4">
<Field
inputProps={{
name: "cids",
placeholder: "Comma separated CIDs",
}}
labelProps={{ htmlFor: "cids", children: "Content to Pin" }}
/>
<Button type="submit" className="w-full">
Pin Content
</Button>
</form>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</GeneralLayout> </GeneralLayout>
</Authenticated> </Authenticated>
); );
} }
const PinFilesSchema = z.object({
cids: z.string().transform((value) => value.split(",")),
});
const PinFilesForm = () => {
const { bulkPin } = usePinning();
const [form, fields] = useForm({
id: "pin-files",
constraint: getZodConstraint(PinFilesSchema),
onValidate({ formData }) {
return parseWithZod(formData, { schema: PinFilesSchema });
},
shouldValidate: "onSubmit",
onSubmit(e, { submission }) {
if (submission?.status === "success") {
const value = submission.value;
bulkPin(value.cids);
}
},
});
return (
<>
<DialogHeader>
<DialogTitle>Pin Content</DialogTitle>
</DialogHeader>
<form {...getFormProps(form)} className="w-full flex flex-col gap-y-4">
<Field
inputProps={{
name: fields.cids.name,
placeholder: "Comma separated CIDs",
}}
labelProps={{ htmlFor: "cids", children: "Content to Pin" }}
errors={fields.cids.errors}
/>
<Button type="submit" className="w-full">
Pin Content
</Button>
</form>
</>
);
};