diff --git a/src/domain/node.ts b/src/domain/node.ts index 7cbde29..3b8a0d2 100644 --- a/src/domain/node.ts +++ b/src/domain/node.ts @@ -42,7 +42,7 @@ export type BaseNode = { owner: string reward: string locked: boolean - authorized: string[] + authorized?: string[] time: number score: number score_updated: boolean @@ -171,7 +171,7 @@ export type BaseUpdateNode = { name?: string description?: string reward?: string - authorized?: string | string[] + authorized?: string[] locked?: boolean registration_url?: string manager?: string @@ -346,13 +346,17 @@ export class NodeManager { return res.item_hash } - async updateCoreChannelNode(updateCCN: UpdateCCN): Promise { + async updateCoreChannelNode( + updateCCN: UpdateCCN, + ): Promise<[string, Partial]> { updateCCN = await NodeManager.updateCCNSchema.parseAsync(updateCCN) return this.updateNode(updateCCN, 'create-node') } - async updateComputeResourceNode(updateCRN: UpdateCRN): Promise { + async updateComputeResourceNode( + updateCRN: UpdateCRN, + ): Promise<[string, Partial]> { updateCRN = await NodeManager.updateCRNSchema.parseAsync(updateCRN) return this.updateNode(updateCRN, 'create-resource-node') @@ -438,10 +442,10 @@ export class NodeManager { ) } - protected async updateNode( - { hash, ...details }: UpdateAlephNode, + protected async updateNode( + { hash, ...details }: U, action: 'create-node' | 'create-resource-node', - ): Promise { + ): Promise<[string, Partial]> { if (!this.account) throw new Error('Invalid account') if (!hash) throw new Error('Invalid node hash') @@ -471,7 +475,15 @@ export class NodeManager { APIServer: apiServer, }) - return res.item_hash + return [ + res.item_hash, + { + hash, + ...details, + picture: details.picture as string, + banner: details.banner as string, + } as unknown as Partial, + ] } isCRN(node: AlephNode): node is CRN { @@ -484,7 +496,7 @@ export class NodeManager { isKYCCleared(node: CCN): boolean { if (!this.account) return false - return node.authorized?.includes(this.account.address) + return node.authorized?.includes(this.account.address) || false } isLocked(node: CCN): boolean { diff --git a/src/helpers/schemas.ts b/src/helpers/schemas.ts index 3155e49..4a697c0 100644 --- a/src/helpers/schemas.ts +++ b/src/helpers/schemas.ts @@ -69,7 +69,7 @@ export const updateBaseNodeSchema = z.object({ reward: optionalString(ethereumAddressSchema), manager: optionalString(ethereumAddressSchema), authorized: optionalString(requiredStringSchema).or( - z.array(ethereumAddressSchema), + z.array(ethereumAddressSchema).optional(), ), locked: z.boolean().optional(), registration_url: optionalString(urlSchema), diff --git a/src/hooks/form/useEditComputeResourceNodeForm.tsx b/src/hooks/form/useEditComputeResourceNodeForm.tsx index 094af09..88dd33f 100644 --- a/src/hooks/form/useEditComputeResourceNodeForm.tsx +++ b/src/hooks/form/useEditComputeResourceNodeForm.tsx @@ -9,8 +9,9 @@ import { useWatch, } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' -import { NodeManager, UpdateCRN } from '@/domain/node' +import { CRN, NodeManager, UpdateCRN } from '@/domain/node' import { useNotification } from '@aleph-front/aleph-core' +import { EntityAddAction } from '@/store/entity' export type UseEditComputeResourceNodeFormState = UpdateCRN @@ -54,11 +55,22 @@ export type UseEditComputeResourceNodeFormReturn = { handleSubmit: (e: FormEvent) => Promise } +function calculateVirtualNode(node: CRN, updated: Partial): CRN { + const virtualNode: CRN = { + ...(node || {}), + ...updated, + virtual: Date.now(), + } + + return virtualNode +} + export function useEditComputeResourceNodeForm({ defaultValues, }: UseEditComputeResourceNodeFormProps): UseEditComputeResourceNodeFormReturn { - const [appState] = useAppState() + const [appState, dispatch] = useAppState() const { account } = appState.account + const { entities: nodes } = appState.crns const noti = useNotification() @@ -68,24 +80,36 @@ export function useEditComputeResourceNodeForm({ const onSubmit = useCallback( async (state: UseEditComputeResourceNodeFormState) => { if (!manager) throw new Error('Manager not ready') + if (!account) throw new Error('Invalid account') - await manager.updateComputeResourceNode(state) - return state.hash as string + const [, partialCRN] = await manager.updateComputeResourceNode(state) + + const node = nodes?.find((node) => node.hash === partialCRN.hash) + const entity = calculateVirtualNode(node as CRN, partialCRN) + + return entity }, - [manager], + [account, manager, nodes], ) const onSuccess = useCallback( - async (hash: string) => { + async (entity: CRN) => { if (!noti) throw new Error('Notification not ready') noti.add({ variant: 'success', title: 'Success', - text: `Your node "${hash}" was updated successfully.`, + text: `Your node "${entity.hash}" was updated successfully.`, }) + + dispatch( + new EntityAddAction({ + name: 'crns', + entities: [entity], + }), + ) }, - [noti], + [dispatch, noti], ) const { @@ -97,7 +121,7 @@ export function useEditComputeResourceNodeForm({ onSubmit, onSuccess, resolver: zodResolver(NodeManager.updateCCNSchema), - readyDeps: [defaultValues?.hash], + readyDeps: [defaultValues], }) // @note: dont use watch, use useWatch instead: https://github.com/react-hook-form/react-hook-form/issues/10753 const values = useWatch({ control }) as UseEditComputeResourceNodeFormState diff --git a/src/hooks/form/useEditCoreChannelNodeForm.tsx b/src/hooks/form/useEditCoreChannelNodeForm.tsx index 08472ed..78761d1 100644 --- a/src/hooks/form/useEditCoreChannelNodeForm.tsx +++ b/src/hooks/form/useEditCoreChannelNodeForm.tsx @@ -9,8 +9,9 @@ import { useWatch, } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' -import { NodeManager, UpdateCCN } from '@/domain/node' +import { CCN, NodeManager, UpdateCCN } from '@/domain/node' import { useNotification } from '@aleph-front/aleph-core' +import { EntityAddAction } from '@/store/entity' export type UseEditCoreChannelNodeFormState = UpdateCCN @@ -48,11 +49,22 @@ export type UseEditCoreChannelNodeFormReturn = { handleSubmit: (e: FormEvent) => Promise } +function calculateVirtualNode(node: CCN, updated: Partial): CCN { + const virtualNode: CCN = { + ...(node || {}), + ...updated, + virtual: Date.now(), + } + + return virtualNode +} + export function useEditCoreChannelNodeForm({ defaultValues, }: UseEditCoreChannelNodeFormProps): UseEditCoreChannelNodeFormReturn { - const [appState] = useAppState() + const [appState, dispatch] = useAppState() const { account } = appState.account + const { entities: nodes } = appState.ccns const noti = useNotification() @@ -62,24 +74,36 @@ export function useEditCoreChannelNodeForm({ const onSubmit = useCallback( async (state: UseEditCoreChannelNodeFormState) => { if (!manager) throw new Error('Manager not ready') + if (!account) throw new Error('Invalid account') - await manager.updateCoreChannelNode(state) - return state.hash as string + const [, partialCCN] = await manager.updateCoreChannelNode(state) + + const node = nodes?.find((node) => node.hash === partialCCN.hash) + const entity = calculateVirtualNode(node as CCN, partialCCN) + + return entity }, - [manager], + [account, manager, nodes], ) const onSuccess = useCallback( - async (hash: string) => { + async (entity: CCN) => { if (!noti) throw new Error('Notification not ready') noti.add({ variant: 'success', title: 'Success', - text: `Your node "${hash}" was updated successfully.`, + text: `Your node "${entity.hash}" was updated successfully.`, }) + + dispatch( + new EntityAddAction({ + name: 'ccns', + entities: [entity], + }), + ) }, - [noti], + [dispatch, noti], ) const { @@ -91,7 +115,7 @@ export function useEditCoreChannelNodeForm({ onSubmit, onSuccess, resolver: zodResolver(NodeManager.updateCCNSchema), - readyDeps: [defaultValues?.hash], + readyDeps: [defaultValues], }) // @note: dont use watch, use useWatch instead: https://github.com/react-hook-form/react-hook-form/issues/10753 const values = useWatch({ control }) as UseEditCoreChannelNodeFormState diff --git a/src/hooks/pages/earn/useComputeResourceNodeDetailPage.tsx b/src/hooks/pages/earn/useComputeResourceNodeDetailPage.tsx index c856734..d1e7c36 100644 --- a/src/hooks/pages/earn/useComputeResourceNodeDetailPage.tsx +++ b/src/hooks/pages/earn/useComputeResourceNodeDetailPage.tsx @@ -94,8 +94,8 @@ export function useComputeResourceNodeDetailPage(): UseComputeResourceNodeDetail // ----------------------------- - const formProps = useEditComputeResourceNodeForm({ - defaultValues: { + const defaultValues = useMemo(() => { + return { hash: node?.hash, name: node?.name, description: node?.description, @@ -107,8 +107,10 @@ export function useComputeResourceNodeDetailPage(): UseComputeResourceNodeDetail picture: node?.picture, banner: node?.banner, address: node?.address, - }, - }) + } + }, [node]) + + const formProps = useEditComputeResourceNodeForm({ defaultValues }) return { nodes, diff --git a/src/hooks/pages/earn/useCoreChannelNodeDetailPage.tsx b/src/hooks/pages/earn/useCoreChannelNodeDetailPage.tsx index 497561d..c4aad29 100644 --- a/src/hooks/pages/earn/useCoreChannelNodeDetailPage.tsx +++ b/src/hooks/pages/earn/useCoreChannelNodeDetailPage.tsx @@ -101,8 +101,8 @@ export function useCoreChannelNodeDetailPage(): UseCoreChannelNodeDetailPageRetu // ----------------------------- - const formProps = useEditCoreChannelNodeForm({ - defaultValues: { + const defaultValues = useMemo(() => { + return { hash: node?.hash, name: node?.name, picture: node?.picture, @@ -114,8 +114,10 @@ export function useCoreChannelNodeDetailPage(): UseCoreChannelNodeDetailPageRetu registration_url: node?.registration_url, manager: node?.manager, multiaddress: node?.multiaddress, - }, - }) + } + }, [node]) + + const formProps = useEditCoreChannelNodeForm({ defaultValues }) return { aggregateLatency,