From c2914348817c2c89762fe8e050a3cb60a9709993 Mon Sep 17 00:00:00 2001 From: Juan Di Toro Date: Tue, 19 Mar 2024 15:40:12 +0100 Subject: [PATCH 01/50] fix: wrong login schema --- app/routes/login.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 0e495cf..90cee68 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -96,7 +96,7 @@ export default function Login() { const LoginSchema = z.object({ email: z.string().email(), password: z.string(), - rememberMe: z.boolean(), + rememberMe: z.boolean().optional(), }); const LoginForm = () => { From 6bffca05240352d834268c76fd02b0ae55244839 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:10:21 -0400 Subject: [PATCH 02/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0a92fb1..7a50c58 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240319140708", + "@lumeweb/portal-sdk": "0.0.0-20240320165911", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 081df029e0414a8ddeeb1d480fefd0b3655ad55c Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:10:57 -0400 Subject: [PATCH 03/50] refactor: de-duplicate code into handleResponse helper and add support for success notifications --- app/data/auth-provider.ts | 70 +++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index 0f207b0..9b94720 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -4,12 +4,15 @@ import type { AuthActionResponse, CheckResponse, IdentityResponse, - OnErrorResponse + OnErrorResponse, + SuccessNotificationResponse // @ts-ignore } from "@refinedev/core/dist/interfaces/bindings/auth" import {Sdk} from "@lumeweb/portal-sdk"; import type {AccountInfoResponse} from "@lumeweb/portal-sdk"; +; + export type AuthFormRequest = { email: string; password: string; @@ -31,7 +34,7 @@ export type Identity = { email: string; } -export interface UpdatePasswordFormRequest extends UpdatePasswordFormTypes{ +export interface UpdatePasswordFormRequest extends UpdatePasswordFormTypes { currentPassword: string; } @@ -43,6 +46,39 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { } }; + type ResponseResult = { + ret: boolean | Error; + successNotification?: SuccessNotificationResponse; + redirectToSuccess?: string; + redirectToError?: string; + successCb?: () => void; + } + + const handleResponse = (result: ResponseResult): AuthActionResponse => { + if (result.ret) { + if (result.ret instanceof Error) { + return { + success: false, + error: result.ret, + redirectTo: result.redirectToError + } + } + + result.successCb?.(); + + return { + success: true, + successNotification: result.successNotification, + redirectTo: result.redirectToSuccess, + } + } + + return { + success: false, + redirectTo: result.redirectToError + } + } + return { async login(params: AuthFormRequest): Promise { const ret = await sdk.account().login({ @@ -68,17 +104,13 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { async logout(params: any): Promise { let ret = await sdk.account().logout(); - return {success: ret, redirectTo: "/login"}; + return handleResponse({ret, redirectToSuccess: "/login"}); }, async check(params?: any): Promise { const ret = await sdk.account().ping(); - if (ret) { - maybeSetupAuth(); - } - - return {authenticated: ret, redirectTo: ret ? undefined : "/login"}; + return handleResponse({ret, redirectToError: "/login", successCb: maybeSetupAuth}); }, async onError(error: any): Promise { @@ -92,7 +124,7 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { first_name: params.firstName, last_name: params.lastName, }); - return {success: ret, redirectTo: ret ? "/dashboard" : undefined}; + return handleResponse({ret, redirectToSuccess: "/login"}); }, async forgotPassword(params: any): Promise { @@ -103,22 +135,12 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { maybeSetupAuth(); const ret = await sdk.account().updatePassword(params.currentPassword, params.password as string); - if (ret) { - if (ret instanceof Error) { - return { - success: false, - error: ret - } + return handleResponse({ + ret, successNotification: { + message: "Password Updated", + description: "Your password has been updated successfully.", } - - return { - success: true - } - } else { - return { - success: false - } - } + }); }, async getPermissions(params?: Record): Promise { From 33decc2e2d034a7f89d1b36dcb89204ae4e14b46 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:19:53 -0400 Subject: [PATCH 04/50] Revert "fix: route redirection issue" This reverts commit 9a8a7ee97817621f7a4378b25ef9c8b048b81f99. --- app/routes/_index.tsx | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 150bb68..666f9ca 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,19 +1,24 @@ -import { useGo, useIsAuthenticated } from "@refinedev/core"; +import {useGo, useIsAuthenticated} from "@refinedev/core"; +import {useEffect} from "react"; export default function Index() { - const { isLoading, data } = useIsAuthenticated(); + const {isLoading, data} = useIsAuthenticated(); - const go = useGo(); + const go = useGo(); - if (isLoading) { - return <>Checking Login Status; - } + useEffect(() => { + if (!isLoading) { + if (data?.authenticated) { + go({to: "/dashboard", type: "replace"}); + } else { + go({to: "/login", type: "replace"}); + } + } + }, [isLoading, data]); - if (data?.authenticated) { - go({ to: "/dashboard", type: "replace" }); - } else { - go({ to: "/login", type: "replace" }); - } + if (isLoading) { + return <>Checking Login Status || null; + } - return <>Redirecting; + return (<>Redirecting) || null; } From 6fbbe4975c18642857afbd252ce0f058803a64d5 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:26:41 -0400 Subject: [PATCH 05/50] refactor: switch to using Authenticated and Navigate components --- app/routes/_index.tsx | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 666f9ca..f8372ce 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -1,24 +1,12 @@ -import {useGo, useIsAuthenticated} from "@refinedev/core"; -import {useEffect} from "react"; +import {Authenticated} from "@refinedev/core"; +import {Navigate} from "@remix-run/react"; export default function Index() { - const {isLoading, data} = useIsAuthenticated(); - - const go = useGo(); - - useEffect(() => { - if (!isLoading) { - if (data?.authenticated) { - go({to: "/dashboard", type: "replace"}); - } else { - go({to: "/login", type: "replace"}); - } - } - }, [isLoading, data]); - - if (isLoading) { - return <>Checking Login Status || null; - } - - return (<>Redirecting) || null; + return ( + Checking Login Status + }> + + + ) } From 729414c45a89d0337c7f827b4c549668e236f86b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:42:21 -0400 Subject: [PATCH 06/50] refactor: add success notification on register, remove success from route form and auto login call --- app/data/auth-provider.ts | 7 ++++++- app/routes/register.tsx | 30 ++++++++---------------------- 2 files changed, 14 insertions(+), 23 deletions(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index 9b94720..05b1021 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -124,7 +124,12 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { first_name: params.firstName, last_name: params.lastName, }); - return handleResponse({ret, redirectToSuccess: "/login"}); + return handleResponse({ + ret, redirectToSuccess: "/login", successNotification: { + message: "Registration Successful", + description: "You have successfully registered. Please check your email to verify your account.", + } + }); }, async forgotPassword(params: any): Promise { diff --git a/app/routes/register.tsx b/app/routes/register.tsx index 8690270..fa15ba9 100644 --- a/app/routes/register.tsx +++ b/app/routes/register.tsx @@ -47,11 +47,11 @@ export default function Register() { const login = useLogin(); const { open } = useNotification(); const [form, fields] = useForm({ - id: "register", - constraint: getZodConstraint(RegisterSchema), - onValidate({ formData }) { - return parseWithZod(formData, { schema: RegisterSchema }); - }, + id: "register", + constraint: getZodConstraint(RegisterSchema), + onValidate({formData}) { + return parseWithZod(formData, {schema: RegisterSchema}); + }, onSubmit(e) { e.preventDefault(); @@ -61,23 +61,9 @@ export default function Register() { password: data.password.toString(), firstName: data.firstName.toString(), lastName: data.lastName.toString(), - }, { - onSuccess: () => { - open?.({ - type: "success", - message: "Verify your Email", - description: "An Email was sent to your email address. Please verify your email address to activate your account.", - key: "register-success" - }) - login.mutate({ - email: data.email.toString(), - password: data.password.toString(), - rememberMe: false, - }) - } - }) - } - }); + }) + } + }); return (
From b3f004472371dfd598deb91fe848a8f5e2a2b721 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 13:43:51 -0400 Subject: [PATCH 07/50] refactor: use handleResponse on login --- app/data/auth-provider.ts | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index 05b1021..25acbc6 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -86,20 +86,15 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { password: params.password, }); - let redirectTo: string | undefined; + return handleResponse({ + ret, redirectToSuccess: "/dashboard", redirectToError: "/login", successCb: () => { + sdk.setAuthToken(sdk.account().jwtToken); + }, successNotification: { + message: "Login Successful", + description: "You have successfully logged in." - if (ret) { - redirectTo = params.redirectTo; - if (!redirectTo) { - redirectTo = ret ? "/dashboard" : "/login"; } - sdk.setAuthToken(sdk.account().jwtToken); - } - - return { - success: ret, - redirectTo, - }; + }); }, async logout(params: any): Promise { From bec75e63eef7adf9a4f0ae4270d4b0fb2543e8ca Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 15:26:24 -0400 Subject: [PATCH 08/50] refactor: fix handling of check by creating a handleCheckResponse wrapper to translate success to authenticated --- app/data/auth-provider.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index 25acbc6..d6d38eb 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -54,6 +54,10 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { successCb?: () => void; } + interface CheckResponseResult extends ResponseResult { + authenticated?: boolean; + } + const handleResponse = (result: ResponseResult): AuthActionResponse => { if (result.ret) { if (result.ret instanceof Error) { @@ -79,6 +83,17 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { } } + const handleCheckResponse = (result: CheckResponseResult): CheckResponse => { + const response = handleResponse(result); + const success = response.success; + delete response.success; + + return { + ...response, + authenticated: success + } + } + return { async login(params: AuthFormRequest): Promise { const ret = await sdk.account().login({ @@ -105,7 +120,7 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { async check(params?: any): Promise { const ret = await sdk.account().ping(); - return handleResponse({ret, redirectToError: "/login", successCb: maybeSetupAuth}); + return handleCheckResponse({ret, redirectToError: "/login", successCb: maybeSetupAuth}); }, async onError(error: any): Promise { From 64e2216a3645fee133a81df78dd3a498dd441bea Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 15:42:35 -0400 Subject: [PATCH 09/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7a50c58..8ba6d3e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240320165911", + "@lumeweb/portal-sdk": "0.0.0-20240320194338", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 81d52d25243eae79671348a562915433d6396a0b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 16:08:40 -0400 Subject: [PATCH 10/50] refactor: ret should now satisfy HttpError --- app/data/account-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/data/account-provider.ts b/app/data/account-provider.ts index 45cb5ac..23276b1 100644 --- a/app/data/account-provider.ts +++ b/app/data/account-provider.ts @@ -30,7 +30,7 @@ export const accountProvider: SdkProvider = { if (ret) { if (ret instanceof Error) { - return Promise.reject(ret) + return Promise.reject(ret satisfies HttpError) } } else { return Promise.reject(); From 6ce3b02dc621d34957ccacd08bd4fd79c13968f7 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 16:21:37 -0400 Subject: [PATCH 11/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ba6d3e..3cf9bcd 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240320194338", + "@lumeweb/portal-sdk": "0.0.0-20240320201908", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 9332598627c60e9902bffcf7e5820d31f47b244b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 16:21:56 -0400 Subject: [PATCH 12/50] refactor: pass ret as a HttpError --- app/data/auth-provider.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index d6d38eb..3f696ed 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -1,4 +1,4 @@ -import type {AuthProvider, UpdatePasswordFormTypes} from "@refinedev/core" +import type {AuthProvider, HttpError, UpdatePasswordFormTypes} from "@refinedev/core" import type { AuthActionResponse, @@ -8,10 +8,9 @@ import type { SuccessNotificationResponse // @ts-ignore } from "@refinedev/core/dist/interfaces/bindings/auth" -import {Sdk} from "@lumeweb/portal-sdk"; +import {Sdk, AccountError} from "@lumeweb/portal-sdk"; import type {AccountInfoResponse} from "@lumeweb/portal-sdk"; -; export type AuthFormRequest = { email: string; @@ -60,10 +59,10 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { const handleResponse = (result: ResponseResult): AuthActionResponse => { if (result.ret) { - if (result.ret instanceof Error) { + if (result.ret instanceof AccountError) { return { success: false, - error: result.ret, + error: result.ret satisfies HttpError, redirectTo: result.redirectToError } } From bba7ce59d3abe936cba846b9c5224f75f0bb4f17 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Wed, 20 Mar 2024 16:22:06 -0400 Subject: [PATCH 13/50] refactor: nop on onError --- app/data/auth-provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/data/auth-provider.ts b/app/data/auth-provider.ts index 3f696ed..b3a56fe 100644 --- a/app/data/auth-provider.ts +++ b/app/data/auth-provider.ts @@ -123,7 +123,7 @@ export const createPortalAuthProvider = (sdk: Sdk): AuthProvider => { }, async onError(error: any): Promise { - return {logout: true}; + return {}; }, async register(params: RegisterFormRequest): Promise { From 2d0609c95c7a4e05616b99372daa5918839a9279 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 08:49:05 -0400 Subject: [PATCH 14/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3cf9bcd..82d7b42 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240320201908", + "@lumeweb/portal-sdk": "0.0.0-20240321124505", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 82b4f9f4fc8a4bf70d8853721f1a25ba4ade90c4 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 08:49:29 -0400 Subject: [PATCH 15/50] fix: need to pass currentPassword --- app/routes/account.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/routes/account.tsx b/app/routes/account.tsx index dedf176..6b6318c 100644 --- a/app/routes/account.tsx +++ b/app/routes/account.tsx @@ -313,7 +313,7 @@ const ChangePasswordSchema = z }); const ChangePasswordForm = () => { - const { mutate: updatePassword } = useUpdatePassword<{ password: string }>(); + const { mutate: updatePassword } = useUpdatePassword(); const [form, fields] = useForm({ id: "login", constraint: getZodConstraint(ChangePasswordSchema), @@ -327,6 +327,7 @@ const ChangePasswordForm = () => { const data = Object.fromEntries(new FormData(e.currentTarget).entries()); updatePassword({ + currentPassword: data.currentPassword.toString(), password: data.newPassword.toString(), }); }, From 214eb1458336d627ee1b99b9a8c6a4777d496475 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 10:28:38 -0400 Subject: [PATCH 16/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 82d7b42..197d098 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321124505", + "@lumeweb/portal-sdk": "0.0.0-20240321142755", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 8fecd31e4322a66438325c15272124fee817870b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 10:50:44 -0400 Subject: [PATCH 17/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 197d098..2f5c8be 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321142755", + "@lumeweb/portal-sdk": "0.0.0-20240321144958", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 02b712793e7660633fa6943ed95cc8d18875d64d Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 10:58:18 -0400 Subject: [PATCH 18/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f5c8be..cf47938 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321144958", + "@lumeweb/portal-sdk": "0.0.0-20240321145732", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From adf306b0741c89b39c230849186f85ffc1554e22 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 11:03:55 -0400 Subject: [PATCH 19/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cf47938..8f10455 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321145732", + "@lumeweb/portal-sdk": "0.0.0-20240321150330", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 583a95e68a82f7a8b479894bbf966d87942a00d6 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 11:39:40 -0400 Subject: [PATCH 20/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f10455..7e46bc2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321150330", + "@lumeweb/portal-sdk": "0.0.0-20240321153723", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 51e8cfef76cd2ac2ce332f7fc0a9ae978cb53c34 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 11:42:26 -0400 Subject: [PATCH 21/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7e46bc2..fd85f30 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321153723", + "@lumeweb/portal-sdk": "0.0.0-20240321153859", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From d4ad64bf88f6353bc6cbc20ef61aae89ae467282 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 12:13:02 -0400 Subject: [PATCH 22/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd85f30..66d3214 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321153859", + "@lumeweb/portal-sdk": "0.0.0-20240321161237", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From d326881f9b1bd0792cdd9aec0626552a8994ed85 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 12:15:55 -0400 Subject: [PATCH 23/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66d3214..aeb170f 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321161237", + "@lumeweb/portal-sdk": "0.0.0-20240321161526", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 7bd2ecdc829c907b3989d94e053f05c22b7ef68b Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 12:21:52 -0400 Subject: [PATCH 24/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aeb170f..61188a2 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321161526", + "@lumeweb/portal-sdk": "0.0.0-20240321162119", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From be31f0db046777096b74c3da648d3ab9f7c7c786 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 12:41:05 -0400 Subject: [PATCH 25/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 61188a2..a774eee 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321162119", + "@lumeweb/portal-sdk": "0.0.0-20240321163942", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 335e982ab0e7ad743e33a4e1212e7ee24ac7ee7d Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 12:56:25 -0400 Subject: [PATCH 26/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a774eee..8a48986 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321163942", + "@lumeweb/portal-sdk": "0.0.0-20240321165606", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 184b3d9a0e3b514545b2c38af4341a4c392e4ce0 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 13:08:52 -0400 Subject: [PATCH 27/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8a48986..8627f0a 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321165606", + "@lumeweb/portal-sdk": "0.0.0-20240321170958", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From b9652ab261def57c79db60dba82e8926f54aa6bd Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 13:16:42 -0400 Subject: [PATCH 28/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8627f0a..d0bad7d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321170958", + "@lumeweb/portal-sdk": "0.0.0-20240321171622", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 39ac3467fc1a0ea9a6a88a2288837f6219ae39f9 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 13:21:22 -0400 Subject: [PATCH 29/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d0bad7d..77eade4 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321171622", + "@lumeweb/portal-sdk": "0.0.0-20240321172104", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From d7243596222f033fe86ea1ec16da408c0070b6e0 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 13:24:13 -0400 Subject: [PATCH 30/50] feat: initial very basic file listing --- app/data/file-provider.ts | 161 +++++++++++++++++++++++++++++--------- 1 file changed, 124 insertions(+), 37 deletions(-) diff --git a/app/data/file-provider.ts b/app/data/file-provider.ts index dc6528a..8719b6f 100644 --- a/app/data/file-provider.ts +++ b/app/data/file-provider.ts @@ -1,39 +1,126 @@ -import type { DataProvider } from "@refinedev/core"; -import { SdkProvider } from "~/data/sdk-provider.js"; +import {SdkProvider} from "~/data/sdk-provider.js"; +import {S5Client} from "@lumeweb/s5-js"; +import {PROTOCOL_S5} from "@lumeweb/portal-sdk"; +import {Multihash} from "@lumeweb/libs5/lib/multihash.js"; +import {AxiosProgressEvent} from "axios"; +import {CancelablePromise} from "@lumeweb/s5-js/lib/axios.js"; +import {MetadataResult} from "@lumeweb/s5-js/lib/options/download.js"; +import {metadataMagicByte, Unpacker, CID, METADATA_TYPES, CID_TYPES} from "@lumeweb/libs5"; -export const fileProvider = { - getList: () => { - console.log("Not implemented"); - return Promise.resolve({ - 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: () => "", +async function getIsManifest(s5: S5Client, hash: string): Promise { + + let type: number | null; + try { + const resp = s5.downloadData(hash, { + onDownloadProgress: (progressEvent: AxiosProgressEvent) => { + if (progressEvent.loaded >= 10) { + resp.cancel(); + } + }, + }) as CancelablePromise; + + const data = await resp; + const unpacker = Unpacker.fromPacked(Buffer.from(data)); + try { + const magic = unpacker.unpackInt(); + + if (magic !== metadataMagicByte) { + return false; + } + + type = unpacker.unpackInt(); + + if (!type || !Object.values(METADATA_TYPES).includes(type)) { + return false; + } + } catch (e) { + return false; + } + + } catch (e) { + return false; + } + + switch (type) { + case METADATA_TYPES.DIRECTORY: + return CID_TYPES.DIRECTORY; + case METADATA_TYPES.WEBAPP: + return CID_TYPES.METADATA_WEBAPP; + case METADATA_TYPES.MEDIA: + return CID_TYPES.METADATA_MEDIA; + case METADATA_TYPES.USER_IDENTITY: + return CID_TYPES.USER_IDENTITY; + } + + return 0; +} + +export interface FileItem { + cid: string; + type: string; + mimeType: string; +} + +export const fileProvider: SdkProvider = { + sdk: undefined, + async getList() { + const items: FileItem[] = []; + try { + const s5 = fileProvider.sdk?.protocols().get(PROTOCOL_S5)!.getSdk()!; + const pinList = await s5.accountPins(); + for (const pin of pinList!.pins) { + const manifest = await getIsManifest(s5, pin.hash) as number; + + if (manifest) { + items.push({ + cid: CID.fromHash(pin.hash, pin.size, manifest).toString(), + type: "manifest", + mimeType: "application/octet-stream", + }); + } else { + items.push({ + cid: CID.fromHash(pin.hash, pin.size, CID_TYPES.RAW).toString(), + type: "raw", + mimeType: pin.mime_type, + }); + } + } + } catch (e) { + return Promise.reject(e); + } + + return { + data: items, + total: items.length, + }; + }, + 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() { + return ""; + }, } satisfies SdkProvider; From 3162d4dcd6b9db0e57848d7dee91ad70ec381d29 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 14:30:11 -0400 Subject: [PATCH 31/50] fix: pass all providers --- app/root.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/root.tsx b/app/root.tsx index 5f17966..26300e4 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -45,7 +45,7 @@ export default function App() { authProvider={providers.auth} routerProvider={routerProvider} notificationProvider={notificationProvider} - dataProvider={providers.default} + dataProvider={providers} resources={resources} options={{disableTelemetry: true}} > From a7ef0b4773011dbb647f96ca95a4826cf4cb6782 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 14:34:55 -0400 Subject: [PATCH 32/50] fix: update resource name --- app/components/data-table.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/components/data-table.tsx b/app/components/data-table.tsx index 79f9b5b..7059bde 100644 --- a/app/components/data-table.tsx +++ b/app/components/data-table.tsx @@ -27,7 +27,7 @@ export function DataTable({ const table = useTable({ columns, refineCoreProps: { - resource: "files" + resource: "file" } }) From 61b70ffa2edc4bff8ff385a58749b90cd8f32aa9 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 15:04:19 -0400 Subject: [PATCH 33/50] fix: use Authenticated component --- app/routes/file-manager/index.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/app/routes/file-manager/index.tsx b/app/routes/file-manager/index.tsx index 96a5777..31e3a3a 100644 --- a/app/routes/file-manager/index.tsx +++ b/app/routes/file-manager/index.tsx @@ -1,14 +1,16 @@ -import { GeneralLayout } from "~/components/general-layout"; -import { FileCard, FileCardList, FileTypes } from "~/components/file-card"; -import { DataTable } from "~/components/data-table"; -import { columns } from "./columns"; -import { Input } from "~/components/ui/input"; -import { Button } from "~/components/ui/button"; -import { AddIcon } from "~/components/icons"; +import {GeneralLayout} from "~/components/general-layout"; +import {FileCard, FileCardList, FileTypes} from "~/components/file-card"; +import {DataTable} from "~/components/data-table"; +import {columns} from "./columns"; +import {Input} from "~/components/ui/input"; +import {Button} from "~/components/ui/button"; +import {AddIcon} from "~/components/icons"; +import {Authenticated} from "@refinedev/core"; export default function FileManager() { return ( - + +

File Manager

+
); } From b42a04ebc31ecfe264af56975285c91ec676990e Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 15:17:45 -0400 Subject: [PATCH 34/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77eade4..1acecee 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321172104", + "@lumeweb/portal-sdk": "0.0.0-20240321191714", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 3a5cd0ae32d053da36f5b86578ddc37d35d95dde Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 15:32:39 -0400 Subject: [PATCH 35/50] refactor: split app to App and Root and store the sdk context in the root for app to use --- app/root.tsx | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/app/root.tsx b/app/root.tsx index 26300e4..6d099b6 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -7,15 +7,16 @@ import type {LinksFunction} from "@remix-run/node"; import '@fontsource-variable/manrope'; import {Refine} from "@refinedev/core"; import routerProvider from "@refinedev/remix-router"; -import { notificationProvider } from "~/data/notification-provider"; -import {SdkContextProvider} from "~/components/lib/sdk-context"; -import { Toaster } from "~/components/ui/toaster"; +import {notificationProvider} from "~/data/notification-provider"; +import {SdkContextProvider, useSdk} from "~/components/lib/sdk-context"; +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 {useMemo} from "react"; export const links: LinksFunction = () => [ - { rel: "stylesheet", href: stylesheet }, + {rel: "stylesheet", href: stylesheet}, ]; export function Layout({children}: { children: React.ReactNode }) { @@ -29,7 +30,7 @@ export function Layout({children}: { children: React.ReactNode }) { {children} - + @@ -37,9 +38,9 @@ export function Layout({children}: { children: React.ReactNode }) { ); } -export default function App() { - const sdk = Sdk.create(import.meta.env.VITE_PORTAL_URL) - const providers = getProviders(sdk); +function App() { + const sdk = useSdk(); + const providers = useMemo(() => getProviders(sdk as Sdk), [sdk]); return ( - - - + ); } +export default function Root() { + const sdk = Sdk.create(import.meta.env.VITE_PORTAL_URL) + return ( + + + + ); +} + export function HydrateFallback() { return

Loading...

; } From 6cdf8e40b9283e4dad0b205c73c526008a8a3464 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:13:35 -0400 Subject: [PATCH 36/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1acecee..474cd40 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321191714", + "@lumeweb/portal-sdk": "0.0.0-20240321201246", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From f7a760051f641effc4b0b3fcefe4e976fffdb239 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:15:04 -0400 Subject: [PATCH 37/50] refactor: use our own abort controller --- app/data/file-provider.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/data/file-provider.ts b/app/data/file-provider.ts index 8719b6f..448f441 100644 --- a/app/data/file-provider.ts +++ b/app/data/file-provider.ts @@ -11,13 +11,17 @@ async function getIsManifest(s5: S5Client, hash: string): Promise { if (progressEvent.loaded >= 10) { - resp.cancel(); + abort.abort(); } }, - }) as CancelablePromise; + httpConfig: { + signal: abort.signal, + }, + }); const data = await resp; const unpacker = Unpacker.fromPacked(Buffer.from(data)); From 0c304b20dbd6ba699a5d735db01c034b2eb26004 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:20:37 -0400 Subject: [PATCH 38/50] refactor: use new CID with the multihash --- app/data/file-provider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/data/file-provider.ts b/app/data/file-provider.ts index 448f441..136d462 100644 --- a/app/data/file-provider.ts +++ b/app/data/file-provider.ts @@ -76,14 +76,15 @@ export const fileProvider: SdkProvider = { const manifest = await getIsManifest(s5, pin.hash) as number; if (manifest) { + const mHash = Multihash.fromBase64Url(pin.hash); items.push({ - cid: CID.fromHash(pin.hash, pin.size, manifest).toString(), + cid: new CID(manifest, mHash, pin.size).toString(), type: "manifest", mimeType: "application/octet-stream", }); } else { items.push({ - cid: CID.fromHash(pin.hash, pin.size, CID_TYPES.RAW).toString(), + cid: new CID(CID_TYPES.RAW, Multihash.fromBase64Url(pin.hash), pin.size).toString(), type: "raw", mimeType: pin.mime_type, }); From 20d8905a673d7b667616e56288a1cc0975172390 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:32:23 -0400 Subject: [PATCH 39/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 474cd40..caa7b2c 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321201246", + "@lumeweb/portal-sdk": "0.0.0-20240321203143", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From 7ea4af13465d4ea631874de000c44c9157ec64e1 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:37:25 -0400 Subject: [PATCH 40/50] dep: update portal-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index caa7b2c..90ad8ef 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "@conform-to/react": "^1.0.2", "@conform-to/zod": "^1.0.2", "@fontsource-variable/manrope": "^5.0.19", - "@lumeweb/portal-sdk": "0.0.0-20240321203143", + "@lumeweb/portal-sdk": "0.0.0-20240321203634", "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", From ea395df49495f26e7a05717e72984f263613bd66 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:45:06 -0400 Subject: [PATCH 41/50] refactor: add pinned property for date --- app/data/file-provider.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/data/file-provider.ts b/app/data/file-provider.ts index 136d462..a9b3b1e 100644 --- a/app/data/file-provider.ts +++ b/app/data/file-provider.ts @@ -3,9 +3,7 @@ import {S5Client} from "@lumeweb/s5-js"; import {PROTOCOL_S5} from "@lumeweb/portal-sdk"; import {Multihash} from "@lumeweb/libs5/lib/multihash.js"; import {AxiosProgressEvent} from "axios"; -import {CancelablePromise} from "@lumeweb/s5-js/lib/axios.js"; -import {MetadataResult} from "@lumeweb/s5-js/lib/options/download.js"; -import {metadataMagicByte, Unpacker, CID, METADATA_TYPES, CID_TYPES} from "@lumeweb/libs5"; +import {CID, CID_TYPES, METADATA_TYPES, metadataMagicByte, Unpacker} from "@lumeweb/libs5"; async function getIsManifest(s5: S5Client, hash: string): Promise { @@ -63,6 +61,7 @@ export interface FileItem { cid: string; type: string; mimeType: string; + pinned: string; } export const fileProvider: SdkProvider = { @@ -81,12 +80,14 @@ export const fileProvider: SdkProvider = { cid: new CID(manifest, mHash, pin.size).toString(), type: "manifest", mimeType: "application/octet-stream", + pinned: pin.pinned_at, }); } else { items.push({ cid: new CID(CID_TYPES.RAW, Multihash.fromBase64Url(pin.hash), pin.size).toString(), type: "raw", mimeType: pin.mime_type, + pinned: pin.pinned_at, }); } } From 23cc02b26e626b81fd6f8e3e6b7cac9abdb3d264 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:45:20 -0400 Subject: [PATCH 42/50] dep: add date-fns --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 90ad8ef..8acf6d0 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@visx/visx": "^3.10.2", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "date-fns": "^3.6.0", "react": "^18.2.0", "react-dom": "^18.2.0", "tailwind-merge": "^2.2.1", From 6252979d28417ad0c134787af65dafaf3d95eeb9 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:46:02 -0400 Subject: [PATCH 43/50] refactor: update columns --- app/routes/file-manager/columns.tsx | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/app/routes/file-manager/columns.tsx b/app/routes/file-manager/columns.tsx index b4a1f2b..4812ccb 100644 --- a/app/routes/file-manager/columns.tsx +++ b/app/routes/file-manager/columns.tsx @@ -4,15 +4,8 @@ import { FileIcon, MoreIcon } from "~/components/icons"; import { Checkbox } from "~/components/ui/checkbox"; import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "~/components/ui/dropdown-menu"; import { cn } from "~/utils"; - -// This type is used to define the shape of our data. -// You can use a Zod schema here if you want. -export type File = { - name: string; - cid: string; - size: string; - createdOn: string; -}; +import {FileItem} from "~/data/file-provider.js"; +import {format} from "date-fns/fp"; declare module '@tanstack/table-core' { interface TableMeta { @@ -20,13 +13,13 @@ declare module '@tanstack/table-core' { } } -export const columns: ColumnDef[] = [ +export const columns: ColumnDef[] = [ { id: "select", size: 20, header: ({ table }) => ( [] = [ header: "Size", }, { - accessorKey: "createdOn", + accessorKey: "pinnedOn", size: 200, - header: "Created On", + header: "Pinned On", cell: ({ row }) => (
- {row.getValue("createdOn")} + {format(row.getValue("pinned")) as unknown as string} [] = [
) } -]; \ No newline at end of file +]; From 4619f9709ce56a6a2b018a313e8cdcc1794c7107 Mon Sep 17 00:00:00 2001 From: Derrick Hammer Date: Thu, 21 Mar 2024 16:49:14 -0400 Subject: [PATCH 44/50] fix: size is missing --- app/data/file-provider.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/data/file-provider.ts b/app/data/file-provider.ts index a9b3b1e..4206821 100644 --- a/app/data/file-provider.ts +++ b/app/data/file-provider.ts @@ -60,6 +60,7 @@ async function getIsManifest(s5: S5Client, hash: string): Promise Date: Thu, 21 Mar 2024 17:00:30 -0400 Subject: [PATCH 45/50] fix: wrong accessorKey --- app/routes/file-manager/columns.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/routes/file-manager/columns.tsx b/app/routes/file-manager/columns.tsx index 4812ccb..5bd1aa0 100644 --- a/app/routes/file-manager/columns.tsx +++ b/app/routes/file-manager/columns.tsx @@ -57,7 +57,7 @@ export const columns: ColumnDef[] = [ header: "Size", }, { - accessorKey: "pinnedOn", + accessorKey: "pinned", size: 200, header: "Pinned On", cell: ({ row }) => ( From 9d9aa4e9c9a4e4a1683a897b9b9966065ed4fa4a Mon Sep 17 00:00:00 2001 From: Juan Di Toro Date: Fri, 22 Mar 2024 12:31:31 +0100 Subject: [PATCH 46/50] feat: add pinning modal --- app/components/forms.tsx | 2 +- app/routes/file-manager/index.tsx | 143 ++++++++++++++++++------------ 2 files changed, 85 insertions(+), 60 deletions(-) diff --git a/app/components/forms.tsx b/app/components/forms.tsx index faaa14d..814adae 100644 --- a/app/components/forms.tsx +++ b/app/components/forms.tsx @@ -58,7 +58,7 @@ export const FieldCheckbox = ({ const input = useInputControl({ key, name: inputProps.name, - formId: inputProps.form, + formId: inputProps.form, initialValue: defaultChecked ? checkedValue : undefined }) const fallbackId = useId() diff --git a/app/routes/file-manager/index.tsx b/app/routes/file-manager/index.tsx index 31e3a3a..19bb1b4 100644 --- a/app/routes/file-manager/index.tsx +++ b/app/routes/file-manager/index.tsx @@ -1,64 +1,89 @@ -import {GeneralLayout} from "~/components/general-layout"; -import {FileCard, FileCardList, FileTypes} from "~/components/file-card"; -import {DataTable} from "~/components/data-table"; -import {columns} from "./columns"; -import {Input} from "~/components/ui/input"; -import {Button} from "~/components/ui/button"; -import {AddIcon} from "~/components/icons"; -import {Authenticated} from "@refinedev/core"; +import { GeneralLayout } from "~/components/general-layout"; +import { FileCard, FileCardList, FileTypes } from "~/components/file-card"; +import { DataTable } from "~/components/data-table"; +import { columns } from "./columns"; +import { Input } from "~/components/ui/input"; +import { Button } from "~/components/ui/button"; +import { AddIcon } from "~/components/icons"; +import { Authenticated } from "@refinedev/core"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "~/components/ui/dialog"; +import { Field } from "~/components/forms"; export default function FileManager() { return ( - - -

File Manager

- - - - - - -

Files

-
- } - placeholder="Search files by name or CID" - className="border-ring font-medium w-full grow h-12 flex-1 bg-primary-2/10" - /> - - -
- -
-
+ + + +

File Manager

+ + + + + + +

Files

+
+ } + placeholder="Search files by name or CID" + className="border-ring font-medium w-full grow h-12 flex-1 bg-primary-2/10" + /> + + + + +
+ +
+ + Pinning Contnet + + +
+ + + + +
+
+
); } From e401889b04933bc7beff20bee63fb0e2f6d3f9eb Mon Sep 17 00:00:00 2001 From: Juan Di Toro Date: Fri, 22 Mar 2024 13:47:50 +0100 Subject: [PATCH 47/50] fix: UX changes regarding showing dates on table and actions pannel --- app/components/data-table.tsx | 4 +- app/components/general-layout.tsx | 4 +- app/components/lib/uppy.ts | 272 +++++++++++++++------------- app/components/ui/toast.tsx | 2 +- app/components/ui/toaster.tsx | 2 +- app/root.tsx | 2 +- app/routes/file-manager/columns.tsx | 160 ++++++++-------- app/routes/file-manager/index.tsx | 39 ++-- app/routes/login.tsx | 20 +- app/tailwind.css | 4 +- 10 files changed, 266 insertions(+), 243 deletions(-) diff --git a/app/components/data-table.tsx b/app/components/data-table.tsx index 7059bde..54f9bd7 100644 --- a/app/components/data-table.tsx +++ b/app/components/data-table.tsx @@ -1,8 +1,8 @@ import { useMemo} from "react"; -import { BaseRecord } from "@refinedev/core"; +import type { BaseRecord } from "@refinedev/core"; import { useTable } from "@refinedev/react-table"; import { - ColumnDef, + type ColumnDef, flexRender, } from "@tanstack/react-table"; diff --git a/app/components/general-layout.tsx b/app/components/general-layout.tsx index 01025de..f4d79c3 100644 --- a/app/components/general-layout.tsx +++ b/app/components/general-layout.tsx @@ -29,10 +29,10 @@ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuGr import { Avatar } from "@radix-ui/react-avatar"; import { cn } from "~/utils"; import { useGetIdentity, useLogout } from "@refinedev/core"; -import { Identity } from "~/data/auth-provider"; +import type { Identity } from "~/data/auth-provider"; -export const GeneralLayout = ({ children }: React.PropsWithChildren<{}>) => { +export const GeneralLayout = ({ children }: React.PropsWithChildren) => { const location = useLocation(); const { data: identity } = useGetIdentity(); const{ mutate: logout } = useLogout() diff --git a/app/components/lib/uppy.ts b/app/components/lib/uppy.ts index 5fb54bc..e2da3d4 100644 --- a/app/components/lib/uppy.ts +++ b/app/components/lib/uppy.ts @@ -1,14 +1,21 @@ -import Uppy, {debugLogger, type State, UppyFile} from "@uppy/core" +import Uppy, { debugLogger, type State, type UppyFile } from "@uppy/core"; -import Tus from "@uppy/tus" -import toArray from "@uppy/utils/lib/toArray" +import Tus from "@uppy/tus"; +import toArray from "@uppy/utils/lib/toArray"; -import {type ChangeEvent, useCallback, useEffect, useMemo, useRef, useState} from "react" -import DropTarget, {type DropTargetOptions} from "./uppy-dropzone" -import {useSdk} from "~/components/lib/sdk-context.js"; -import UppyFileUpload from "~/components/lib/uppy-file-upload.js"; -import {PROTOCOL_S5, Sdk} from "@lumeweb/portal-sdk"; -import {S5Client, HashProgressEvent} from "@lumeweb/s5-js"; +import { + type ChangeEvent, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import DropTarget, { type DropTargetOptions } from "./uppy-dropzone"; +import { useSdk } from "~/components/lib/sdk-context"; +import UppyFileUpload from "~/components/lib/uppy-file-upload"; +import { PROTOCOL_S5, type Sdk } from "@lumeweb/portal-sdk"; +import type { S5Client, HashProgressEvent } from "@lumeweb/s5-js"; const LISTENING_EVENTS = [ "upload", @@ -16,46 +23,46 @@ const LISTENING_EVENTS = [ "upload-error", "file-added", "file-removed", - "files-added" -] as const + "files-added", +] as const; export function useUppy() { - const sdk = useSdk() + const sdk = useSdk(); - const [uploadLimit, setUploadLimit] = useState(0) + const [uploadLimit, setUploadLimit] = useState(0); - useEffect(() => { - async function getUploadLimit() { - try { - const limit = await sdk.account!().uploadLimit(); - setUploadLimit(limit); - } catch (err) { - console.log('Error occured while fetching upload limit', err); - } - } - getUploadLimit(); - }, []); + useEffect(() => { + async function getUploadLimit() { + try { + const limit = await sdk.account!().uploadLimit(); + setUploadLimit(limit); + } catch (err) { + console.log("Error occured while fetching upload limit", err); + } + } + getUploadLimit(); + }, [sdk.account]); - const inputRef = useRef(null) - const [targetRef, _setTargetRef] = useState(null) - const uppyInstance = useRef() + const inputRef = useRef(null); + const [targetRef, _setTargetRef] = useState(null); + const uppyInstance = useRef(); const setRef = useCallback( (element: HTMLElement | null) => _setTargetRef(element), - [] - ) - const [, setUppyState] = useState() + [], + ); + const [, setUppyState] = useState(); const [state, setState] = useState< "completed" | "idle" | "initializing" | "error" | "uploading" - >("initializing") + >("initializing"); const [inputProps, setInputProps] = useState< | { - ref: typeof inputRef - type: "file" - onChange: (event: ChangeEvent) => void + ref: typeof inputRef; + type: "file"; + onChange: (event: ChangeEvent) => void; } | object - >({}) + >({}); const getRootProps = useMemo( () => () => { return { @@ -63,103 +70,110 @@ export function useUppy() { onClick: () => { if (inputRef.current) { //@ts-expect-error -- dumb html - inputRef.current.value = null - inputRef.current.click() - console.log("clicked", { input: inputRef.current }) + inputRef.current.value = null; + inputRef.current.click(); + console.log("clicked", { input: inputRef.current }); } }, - role: "presentation" - } + role: "presentation", + }; }, - [setRef] - ) + [setRef], + ); const removeFile = useCallback( (id: string) => { - uppyInstance.current?.removeFile(id) + uppyInstance.current?.removeFile(id); }, // eslint-disable-next-line react-hooks/exhaustive-deps - [targetRef, uppyInstance] - ) + [targetRef, uppyInstance], + ); const cancelAll = useCallback( () => uppyInstance.current?.cancelAll({ reason: "user" }), // eslint-disable-next-line react-hooks/exhaustive-deps - [targetRef, uppyInstance] - ) + [targetRef, uppyInstance], + ); useEffect(() => { - if (!targetRef) return + if (!targetRef) return; - const tusPreprocessor = async (fileIDs: string[]) => { - for(const fileID of fileIDs) { - const file = uppyInstance.current?.getFile(fileID) as UppyFile - // @ts-ignore - if (file.uploader === "tus") { - const hashProgressCb = (event: HashProgressEvent) => { - uppyInstance.current?.emit("preprocess-progress", file, { - uploadStarted: false, - bytesUploaded: 0, - preprocess: { - mode: "determinate", - message: "Hashing file...", - value: Math.round((event.total / event.total) * 100) - } - }) - } - const options = await sdk.protocols!().get(PROTOCOL_S5).getSdk().getTusOptions(file.data as File, {}, {onHashProgress: hashProgressCb}) - uppyInstance.current?.setFileState(fileID, { - tus: options, - meta: { - ...options.metadata, - ...file.meta, - } - }) - } + const tusPreprocessor = async (fileIDs: string[]) => { + for (const fileID of fileIDs) { + const file = uppyInstance.current?.getFile(fileID) as UppyFile; + // @ts-ignore + if (file.uploader === "tus") { + const hashProgressCb = (event: HashProgressEvent) => { + uppyInstance.current?.emit("preprocess-progress", file, { + uploadStarted: false, + bytesUploaded: 0, + preprocess: { + mode: "determinate", + message: "Hashing file...", + value: Math.round((event.total / event.total) * 100), + }, + }); + }; + const options = await sdk.protocols!() + .get(PROTOCOL_S5) + .getSdk() + .getTusOptions( + file.data as File, + {}, + { onHashProgress: hashProgressCb }, + ); + uppyInstance.current?.setFileState(fileID, { + tus: options, + meta: { + ...options.metadata, + ...file.meta, + }, + }); } } + }; const uppy = new Uppy({ - logger: debugLogger, - onBeforeUpload: (files) => { - for (const file of Object.entries(files)) { - // @ts-ignore - file[1].uploader = file[1].size > uploadLimit ? "tus" : "file"; - } + logger: debugLogger, + onBeforeUpload: (files) => { + for (const file of Object.entries(files)) { + // @ts-ignore + file[1].uploader = file[1].size > uploadLimit ? "tus" : "file"; + } - return true; - }, + return true; + }, }).use(DropTarget, { - target: targetRef - } as DropTargetOptions) + target: targetRef, + } as DropTargetOptions); - uppyInstance.current = uppy + uppyInstance.current = uppy; setInputProps({ ref: inputRef, type: "file", onChange: (event) => { - const files = toArray(event.target.files) + const files = toArray(event.target.files); if (files.length > 0) { - uppyInstance.current?.log("[DragDrop] Files selected through input") - uppyInstance.current?.addFiles(files) + uppyInstance.current?.log("[DragDrop] Files selected through input"); + uppyInstance.current?.addFiles(files); } uppy.iteratePlugins((plugin) => { - uppy.removePlugin(plugin); + uppy.removePlugin(plugin); }); - uppy.use(UppyFileUpload, { sdk: sdk as Sdk }) + uppy.use(UppyFileUpload, { sdk: sdk as Sdk }); - let useTus = false; + let useTus = false; - uppyInstance.current?.getFiles().forEach((file) => { - if (file.size > uploadLimit) { - useTus = true; - } - }) - - if (useTus) { - uppy.use(Tus, { limit: 6, parallelUploads: 10 }) - uppy.addPreProcessor(tusPreprocessor) + uppyInstance.current?.getFiles().forEach((file) => { + if (file.size > uploadLimit) { + useTus = true; } + }); + + if (useTus) { + uppy.use(Tus, { limit: 6, parallelUploads: 10 }); + uppy.addPreProcessor(tusPreprocessor); + } // We clear the input after a file is selected, because otherwise // change event is not fired in Chrome and Safari when a file @@ -169,54 +183,52 @@ export function useUppy() { // Chrome will not trigger change if we drop the same file twice (Issue #768). // @ts-expect-error TS freaks out, but this is fine // eslint-disable-next-line no-param-reassign - event.target.value = null - } - }) - - + event.target.value = null; + }, + }); uppy.on("complete", (result) => { if (result.failed.length === 0) { - console.log("Upload successful üòÄ") - setState("completed") + console.log("Upload successful üòÄ"); + setState("completed"); } else { - console.warn("Upload failed üòû") - setState("error") + console.warn("Upload failed üòû"); + setState("error"); } - console.log("successful files:", result.successful) - console.log("failed files:", result.failed) - }) + console.log("successful files:", result.successful); + console.log("failed files:", result.failed); + }); const setStateCb = (event: (typeof LISTENING_EVENTS)[number]) => { switch (event) { case "upload": - setState("uploading") - break + setState("uploading"); + break; case "upload-error": - setState("error") - break + setState("error"); + break; default: - break + break; } - setUppyState(uppy.getState()) - } + setUppyState(uppy.getState()); + }; for (const event of LISTENING_EVENTS) { uppy.on(event, function cb() { - setStateCb(event) - }) + setStateCb(event); + }); } - setState("idle") - }, [targetRef, uploadLimit]) + setState("idle"); + }, [targetRef, uploadLimit]); useEffect(() => { return () => { - uppyInstance.current?.cancelAll({ reason: "unmount" }) - uppyInstance.current?.logout() - uppyInstance.current?.close() - uppyInstance.current = undefined - } - }, []) + uppyInstance.current?.cancelAll({ reason: "unmount" }); + uppyInstance.current?.logout(); + uppyInstance.current?.close(); + uppyInstance.current = undefined; + }; + }, []); return { getFiles: () => uppyInstance.current?.getFiles() ?? [], error: uppyInstance.current?.getState, @@ -227,6 +239,6 @@ export function useUppy() { getInputProps: () => inputProps, getRootProps, removeFile, - cancelAll - } + cancelAll, + }; } diff --git a/app/components/ui/toast.tsx b/app/components/ui/toast.tsx index f8f37c9..9aaa1d7 100644 --- a/app/components/ui/toast.tsx +++ b/app/components/ui/toast.tsx @@ -60,7 +60,7 @@ const ToastAction = React.forwardRef< - {toasts.map(function ({ id, title, description, action, cancelMutation, ...props }) { + {toasts.map(({ id, title, description, action, cancelMutation, ...props }) => { const undoButton = cancelMutation ? Undo : undefined return ( diff --git a/app/root.tsx b/app/root.tsx index 6d099b6..522ae69 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -28,7 +28,7 @@ export function Layout({children}: { children: React.ReactNode }) { - + {children} diff --git a/app/routes/file-manager/columns.tsx b/app/routes/file-manager/columns.tsx index 5bd1aa0..d5fd059 100644 --- a/app/routes/file-manager/columns.tsx +++ b/app/routes/file-manager/columns.tsx @@ -2,88 +2,94 @@ import { DrawingPinIcon, TrashIcon } from "@radix-ui/react-icons"; import type { ColumnDef, RowData } from "@tanstack/react-table"; import { FileIcon, MoreIcon } from "~/components/icons"; import { Checkbox } from "~/components/ui/checkbox"; -import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from "~/components/ui/dropdown-menu"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/components/ui/dropdown-menu"; import { cn } from "~/utils"; -import {FileItem} from "~/data/file-provider.js"; -import {format} from "date-fns/fp"; +import type { FileItem } from "~/data/file-provider"; -declare module '@tanstack/table-core' { +declare module "@tanstack/table-core" { interface TableMeta { - hoveredRowId: string, + hoveredRowId: string; } } export const columns: ColumnDef[] = [ - { - id: "select", - size: 20, - header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: "name", - header: "Name", - cell: ({ row }) => ( -
- - {row.getValue("name")} -
- ) - }, - { - accessorKey: "cid", - header: "CID", - }, - { - accessorKey: "size", - header: "Size", - }, - { - accessorKey: "pinned", - size: 200, - header: "Pinned On", - cell: ({ row }) => ( -
- {format(row.getValue("pinned")) as unknown as string} - - - - - - - - - Ping CID - - - - - Delete - - - - -
- ) - } + { + id: "select", + size: 20, + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "name", + header: "Name", + cell: ({ row }) => ( +
+ + {row.getValue("name")} +
+ ), + }, + { + accessorKey: "cid", + header: "CID", + }, + { + accessorKey: "size", + header: "Size", + }, + { + accessorKey: "pinned", + header: "Pinned On", + cell: ({ row }) => new Date(row.getValue("pinned")).toLocaleString(), + }, + { + accessorKey: "actions", + header: () => null, + size: 20, + cell: ({ row }) => ( +
+ + + + + + + + + Delete + + + + +
+ ), + }, ]; diff --git a/app/routes/file-manager/index.tsx b/app/routes/file-manager/index.tsx index 19bb1b4..82436b4 100644 --- a/app/routes/file-manager/index.tsx +++ b/app/routes/file-manager/index.tsx @@ -18,8 +18,8 @@ import { Field } from "~/components/forms"; export default function FileManager() { return ( - - + +

File Manager

- - - Pinning Contnet - - -
- + + + Pin Content + + + - - - - + + +
+ + ); } diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 90cee68..b87b001 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -47,7 +47,7 @@ export default function Login() { go({ to, type: "push" }); } } - }, [isAuthLoading, authData]); + }, [isAuthLoading, authData, parsed, go]); return (
@@ -109,16 +109,18 @@ const LoginForm = () => { return parseWithZod(formData, { schema: LoginSchema }); }, shouldValidate: "onSubmit", - onSubmit(e) { + onSubmit(e, { submission }) { e.preventDefault(); - const data = Object.fromEntries(new FormData(e.currentTarget).entries()); - login.mutate({ - email: data.email.toString(), - password: data.password.toString(), - rememberMe: data.rememberMe.toString() === "on", - redirectTo: parsed.params?.to, - }); + if (submission?.status === "success") { + const data = submission.value; + login.mutate({ + email: data.email, + password: data.password, + rememberMe: data.rememberMe ?? false, + redirectTo: parsed.params?.to, + }); + } }, }); diff --git a/app/tailwind.css b/app/tailwind.css index 477e95a..deb417f 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -4,8 +4,8 @@ @layer base { :root { - --background: 240, 33%, 3%; - --foreground: 0, 0%, 88%; + --background: 0 0% 3.9%; + --foreground: 0 0% 98%; --card: 0 0% 0%; --card-foreground: 0 0% 3.9%; From a61fe195ee4cea7d09b37d9d9481f28d49a365b3 Mon Sep 17 00:00:00 2001 From: Juan Di Toro Date: Fri, 22 Mar 2024 14:04:22 +0100 Subject: [PATCH 48/50] fix: closes #8 --- app/routes/login.otp.tsx | 69 +++++++++++++++++++++++++++++++ app/routes/login.tsx | 87 +++++----------------------------------- 2 files changed, 79 insertions(+), 77 deletions(-) create mode 100644 app/routes/login.otp.tsx diff --git a/app/routes/login.otp.tsx b/app/routes/login.otp.tsx new file mode 100644 index 0000000..0002c40 --- /dev/null +++ b/app/routes/login.otp.tsx @@ -0,0 +1,69 @@ +import { getFormProps, useForm } from "@conform-to/react"; +import { getZodConstraint, parseWithZod } from "@conform-to/zod"; +import { useGo, useIsAuthenticated, useParsed } from "@refinedev/core"; +import { Link } from "@remix-run/react"; +import { useEffect } from "react"; +import { z } from "zod"; +import { Field } from "~/components/forms"; +import { Button } from "~/components/ui/button"; +import type { LoginParams } from "./login"; + +const OtpSchema = z.object({ + otp: z.string().length(6, { message: "OTP must be 6 characters" }), +}); + +export default function OtpForm() { + const { isLoading: isAuthLoading, data: authData } = useIsAuthenticated(); + const go = useGo(); + const parsed = useParsed(); + // TODO: Add support for resending the OTP + const [form, fields] = useForm({ + id: "otp", + constraint: getZodConstraint(OtpSchema), + onValidate({ formData }) { + return parseWithZod(formData, { schema: OtpSchema }); + }, + shouldValidate: "onSubmit", + }); + const valid = true; + const to = parsed.params?.to ?? "/dashboard"; + + useEffect(() => { + if (!isAuthLoading) { + if (authData?.authenticated && valid) { + go({ to, type: "push" }); + } + } + }, [isAuthLoading, authData, to, go]); + + return ( +
+ +

Check your inbox

+

+ We will need the six digit confirmation code you received in your + email in order to verify your account and get started. Didn’t receive + a code?{" "} + +

+
+ + +

+ + ← Back to Login + +

+ + ); +} diff --git a/app/routes/login.tsx b/app/routes/login.tsx index b87b001..8c4a480 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,5 +1,5 @@ import type { MetaFunction } from "@remix-run/node"; -import { Link, useLocation } from "@remix-run/react"; +import { Link } from "@remix-run/react"; import { z } from "zod"; import { Button } from "~/components/ui/button"; import logoPng from "~/images/lume-logo.png?url"; @@ -11,7 +11,6 @@ import { getFormProps, useForm } from "@conform-to/react"; import { getZodConstraint, parseWithZod } from "@conform-to/zod"; import { useGo, - useIsAuthenticated, useLogin, useParsed, } from "@refinedev/core"; @@ -25,30 +24,11 @@ export const meta: MetaFunction = () => { ]; }; -type LoginParams = { +export type LoginParams = { to: string; }; export default function Login() { - const location = useLocation(); - const { isLoading: isAuthLoading, data: authData } = useIsAuthenticated(); - const hash = location.hash; - const go = useGo(); - const parsed = useParsed(); - - useEffect(() => { - if (!isAuthLoading) { - if (authData?.authenticated) { - let to = "/dashboard"; - if (parsed.params?.to) { - to = parsed.params.to; - } - - go({ to, type: "push" }); - } - } - }, [isAuthLoading, authData, parsed, go]); - return (
@@ -62,8 +42,7 @@ export default function Login() { />
- {hash === "" && } - {hash === "#otp" && } +