Skip to content

Commit

Permalink
fix: unlink buttons for CRN owner in detail pages
Browse files Browse the repository at this point in the history
  • Loading branch information
amalcaraz committed Apr 1, 2024
1 parent b75af8d commit 55634fa
Show file tree
Hide file tree
Showing 14 changed files with 250 additions and 142 deletions.
8 changes: 5 additions & 3 deletions src/components/common/ComputeResourceNodesTable/cmp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@ import ButtonLink from '../ButtonLink'
import Image from 'next/image'
import { apiServer } from '@/helpers/constants'
import { UseSortedListReturn } from '@/hooks/common/useSortedList'
import { UseLinkingReturn } from '@/hooks/common/useLinking'

export type ComputeResourceNodesTableProps = {
export type ComputeResourceNodesTableProps = Pick<
UseLinkingReturn,
'handleLink' | 'handleUnlink'
> & {
filteredNodes?: CRN[]
userNode?: CCN
account?: Account
Expand All @@ -27,8 +31,6 @@ export type ComputeResourceNodesTableProps = {
loadItemsDisabled?: boolean
handleLoadItems?: () => Promise<void>
handleSortItems?: UseSortedListReturn<CRN>['handleSortItems']
handleLink: (nodeHash: string) => void
handleUnlink: (nodeHash: string) => void
}

export const ComputeResourceNodesTable = ({
Expand Down
50 changes: 29 additions & 21 deletions src/components/common/LinkCRNButton/cmp.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,63 @@
import { memo, useCallback, useMemo } from 'react'
import { Button } from '@aleph-front/core'
import { Account } from 'aleph-sdk-ts/dist/accounts/account'
import { CCN, CRN, NodeManager } from '@/domain/node'
import { CCN, CRN } from '@/domain/node'
import { UseLinkingReturn, useLinking } from '@/hooks/common/useLinking'

export type LinkCRNButtonProps = {
node: CRN
userNode?: CCN
account?: Account
accountBalance?: number
onLink: (nodeHash: string) => void
onUnlink: (nodeHash: string) => void
onLink?: UseLinkingReturn['handleLink']
onUnlink?: UseLinkingReturn['handleUnlink']
}

// https://github.com/aleph-im/aleph-account/blob/main/src/components/NodesTable.vue#L298
export const LinkCRNButton = ({
node,
userNode,
account,
onLink,
onUnlink,
}: LinkCRNButtonProps) => {
// @todo: Refactor this (use singleton)
const nodeManager = useMemo(() => new NodeManager(account), [account])
const {
isLinkableByUser: isLinkableByUserCheck,
isUnlinkableByUser: isUnlinkableByUserCheck,
handleLink: defaultHandleLink,
handleUnlink: defaultHandleUnlink,
} = useLinking()

const isLinkedNode = useMemo(() => {
return nodeManager.isUserLinked(node, userNode)
}, [node, nodeManager, userNode])
const handleLink = onLink || defaultHandleLink
const handleUnlink = onUnlink || defaultHandleUnlink

const isDisabled = useMemo(() => {
const [canLink] = nodeManager.isLinkable(node, userNode)
return !canLink
}, [nodeManager, node, userNode])
const isLinkableByUser = useMemo(
() => node && userNode && isLinkableByUserCheck(node, userNode),
[isLinkableByUserCheck, node, userNode],
)

const isUnlinkableByUser = useMemo(
() => node && isUnlinkableByUserCheck(node),
[isUnlinkableByUserCheck, node],
)

const handleOnClick = useCallback(() => {
if (isLinkedNode) {
onUnlink(node.hash)
if (!userNode) return

if (isUnlinkableByUser) {
handleUnlink(node.hash)
} else {
onLink(node.hash)
handleLink(node, userNode)
}
}, [isLinkedNode, onUnlink, node.hash, onLink])
}, [handleLink, handleUnlink, isUnlinkableByUser, node, userNode])

return (
<>
{!isLinkedNode ? (
{!isUnlinkableByUser ? (
<Button
kind="neon"
size="md"
variant="secondary"
color="main0"
onClick={handleOnClick}
disabled={isDisabled}
disabled={!isLinkableByUser}
>
Link
</Button>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/StakeButton/cmp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const StakeButton = ({
}, [node, nodeManager])

const isDisabled = useMemo(() => {
const [canStake] = nodeManager.isStakeable(node, accountBalance)
const [canStake] = nodeManager.isStakeableBy(node, accountBalance)
return !canStake
}, [nodeManager, node, accountBalance])

Expand Down
41 changes: 21 additions & 20 deletions src/components/pages/earn/ComputeResourceNodeDetailPage/cmp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ export const ComputeResourceNodeDetailPage = () => {
baseLatency,
lastMetricsCheck,
calculatedRewards,
isUserLinked,
isLinkable,
creationDate,
nameCtrl,
descriptionCtrl,
Expand All @@ -47,6 +45,9 @@ export const ComputeResourceNodeDetailPage = () => {
nodeSpecs,
nodeIssue,
createInstanceUrl,
isLinked,
isLinkableByUser,
isUnlinkableByUser,
// nodeBenchmark,
handleRemove,
handleSubmit,
Expand Down Expand Up @@ -355,11 +356,11 @@ export const ComputeResourceNodeDetailPage = () => {
</div>
<div tw="flex-1 w-1/3 min-w-[20rem] flex flex-col gap-9">
<Card2 title="LINKED CORE NODE">
{!node?.parentData ? (
{!isLinked ? (
<div tw="inline-flex gap-3 items-center">
<div tw="w-6 h-6 rounded-full bg-[#C4C4C433]" />
<div className="fs-10" tw="leading-4">
{!isUserLinked && isLinkable ? (
{isLinkableByUser ? (
<Button
color="main2"
size="md"
Expand All @@ -378,25 +379,25 @@ export const ComputeResourceNodeDetailPage = () => {
</div>
) : (
<div tw="flex items-center">
<Link
href={`/earn/ccn/${node.parentData.hash}`}
legacyBehavior
>
<NodeName
hash={node.parentData.hash}
name={node.parentData.name}
picture={node.parentData.picture}
tw="mr-auto w-auto cursor-pointer"
apiServer={apiServer}
ImageCmp={Image}
/>
</Link>
{isUserLinked ? (
{node?.parentData && (
<Link
href={`/earn/ccn/${node.parentData.hash}`}
legacyBehavior
>
<NodeName
hash={node.parentData.hash}
name={node.parentData.name}
picture={node.parentData.picture}
tw="mr-auto w-auto cursor-pointer"
apiServer={apiServer}
ImageCmp={Image}
/>
</Link>
)}
{isUnlinkableByUser && (
<button onClick={handleUnlink}>
<Icon name="trash" color="error" />
</button>
) : (
<></>
)}
</div>
)}
Expand Down
11 changes: 6 additions & 5 deletions src/components/pages/earn/CoreChannelNodeDetailPage/cmp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ export const CoreChannelNodeDetailPage = () => {
lockedCtrl,
registrationUrlCtrl,
isDirty,
account,
isUnlinkableByUser,
handleUnlink,
handleRemove,
handleSubmit,
handleUnlink,
} = useCoreChannelNodeDetailPage()

return (
Expand Down Expand Up @@ -258,6 +260,7 @@ export const CoreChannelNodeDetailPage = () => {
},
(_, i) => {
const crn = node?.crnsData[i]
const isCRNOwner = crn?.owner === account?.address

return !crn ? (
<div key={i} tw="inline-flex gap-3 items-center">
Expand Down Expand Up @@ -292,12 +295,10 @@ export const CoreChannelNodeDetailPage = () => {
ImageCmp={Image}
/>
</Link>
{isOwner ? (
<button onClick={() => handleUnlink(crn.hash)}>
{isUnlinkableByUser(crn) && (
<button onClick={() => handleUnlink(crn)}>
<Icon name="trash" color="error" />
</button>
) : (
<></>
)}
</div>
)
Expand Down
37 changes: 28 additions & 9 deletions src/domain/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,11 @@ export class NodeManager {
return !!node.stakers[this.account.address]
}

isUserLinked(node: CRN, userNode?: CCN): boolean {
isLinked(node: CRN): boolean {
return !!node.parentData
}

isUnlinkableBy(node: CRN, userNode?: CCN): boolean {
if (!userNode) return false

return (
Expand All @@ -615,17 +619,22 @@ export class NodeManager {
)
}

isStakeable(node: CCN, balance: number): [boolean, string] {
if (!this.account) return [false, 'Please login']

if (balance < 10_000)
return [false, 'You need at least 10000 ALEPH to stake']

isStakeable(node: CCN): [boolean, string] {
if (node.total_staked >= NodeManager.maxStakedPerNode)
return [false, 'Too many ALEPH staked on that node']

if (this.isLocked(node)) return [false, 'This node is locked']

return [true, `${node.hash} is stakeable`]
}

isStakeableBy(node: CCN, balance: number | undefined): [boolean, string] {
const isStakeable = this.isStakeable(node)
if (!isStakeable[0]) return isStakeable

if (!balance || balance < 10_000)
return [false, 'You need at least 10000 ALEPH to stake']

if (this.isUserNode(node))
return [false, "You can't stake while you operate a node"]

Expand All @@ -634,8 +643,18 @@ export class NodeManager {
return [true, `Stake ${balance.toFixed(2)} ALEPH in this node`]
}

isLinkable(node: CRN, userNode?: CCN): [boolean, string] {
if (!this.account) return [false, 'Please login']
isLinkable(node: CRN): [boolean, string] {
if (node.locked) return [false, 'This node is locked']

if (!!node.parent)
return [false, `The node is already linked to ${node.parent} ccn`]

return [true, `${node.hash} is linkable`]
}

isLinkableBy(node: CRN, userNode: CCN | undefined): [boolean, string] {
const isLinkable = this.isLinkable(node)
if (!isLinkable[0]) return isLinkable

if (!userNode || !this.isUserNode(userNode))
return [false, "The user doesn't own a core channel node"]
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/common/useFilterUserLinkedNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function useFilterUserLinkedNodes({
const filterUserNodes = useCallback(
(nodes?: CRN[]) => {
if (!nodes) return
return nodes.filter((node) => nodeManager.isUserLinked(node, userNode))
return nodes.filter((node) => nodeManager.isUnlinkableBy(node, userNode))
},
[nodeManager, userNode],
)
Expand Down
Loading

0 comments on commit 55634fa

Please sign in to comment.