Skip to content

MAS UI integration v2 #189

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Sep 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
0bce397
Open a modal to authorize apps
r-czajkowski Sep 12, 2022
0bc9be4
Update staking application card
r-czajkowski Sep 13, 2022
11151f0
Update `StakeAddressInfo` component
r-czajkowski Sep 13, 2022
bfea1a2
Create `AuthorizeStakingApps` modal
r-czajkowski Sep 13, 2022
c703438
Open `AuthorizeStakingApps` modal
r-czajkowski Sep 13, 2022
3574ad0
Update authorization page
r-czajkowski Sep 13, 2022
e87c3b1
Fix submitting apps authorization form
r-czajkowski Sep 14, 2022
9bb6120
Refactor `useSendTransaction`
r-czajkowski Sep 14, 2022
86c4141
Rename and export variable
r-czajkowski Sep 14, 2022
794edce
Add new hook
r-czajkowski Sep 14, 2022
dd148e7
Define the new modal type
r-czajkowski Sep 14, 2022
2f9458f
Add new staking apps hooks
r-czajkowski Sep 14, 2022
6c9d8bc
Update authorize staking apps modal
r-czajkowski Sep 14, 2022
27617ab
Fix typos
r-czajkowski Sep 14, 2022
f15a7eb
Update `ExternalLink` component
r-czajkowski Sep 15, 2022
c8ca9a4
Create a success modal for apps authorization
r-czajkowski Sep 15, 2022
d37dbd2
Update hook that authrozie staking apps
r-czajkowski Sep 15, 2022
55a954b
Open auth staking app modal correctly
r-czajkowski Sep 15, 2022
1fac196
Merge branch 'main' into mas-integration-v2
r-czajkowski Sep 20, 2022
a431ed4
Fix error after merge
r-czajkowski Sep 20, 2022
311fab8
Update success modal for multiple auth flow
r-czajkowski Sep 20, 2022
bd465ca
Fix typo
r-czajkowski Sep 21, 2022
303b6ab
Update copy in multi app staking modal flow
r-czajkowski Sep 21, 2022
da5ad3e
Format percentage value more precisely
r-czajkowski Sep 22, 2022
9e4e044
Conditionally register MAS listeners
r-czajkowski Sep 22, 2022
fec8758
Remove unnecessary `async`
r-czajkowski Sep 22, 2022
d1ea737
Fix typo
r-czajkowski Sep 22, 2022
f82d8d7
Fix listener effect predicate function
r-czajkowski Sep 22, 2022
3ed47b0
Format percentage value in stake card
r-czajkowski Sep 22, 2022
1c939e8
Add missing `key` prop
r-czajkowski Sep 22, 2022
0ca4fe5
Fix `function component cannot be given refs` err
r-czajkowski Sep 22, 2022
37a06fb
Update `formatPercentage` fn
r-czajkowski Sep 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 27 additions & 28 deletions src/components/ExternalLink/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import { FC, ReactElement } from "react"
import { Icon, Link, LinkProps, useColorModeValue } from "@chakra-ui/react"
import {
Icon,
Link,
LinkProps,
useColorModeValue,
forwardRef,
} from "@threshold-network/components"
import { FiArrowUpRight } from "react-icons/all"

interface Props {
Expand All @@ -9,34 +15,27 @@ interface Props {
icon?: ReactElement
}

const ExternalLink: FC<Props & LinkProps> = ({
text,
href,
withArrow,
icon,
...props
}) => {
const defaultColor = useColorModeValue("brand.500", "white")
const finalColor = props.color ? props.color : defaultColor
const ExternalLink: FC<Props & LinkProps> = forwardRef(
({ text, href, withArrow, icon, ...props }, ref) => {
const defaultColor = useColorModeValue("brand.500", "white")
const finalColor = props.color ? props.color : defaultColor

return (
<Link
href={href}
target="_blank"
rel="noopener noreferrer"
color={finalColor}
textDecoration="underline"
{...props}
>
{text}
{withArrow ? (
<Icon boxSize="12px" ml="1" as={FiArrowUpRight} color={finalColor} />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this just a code cleanup change because the Icon inherits the final color?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes exactly. The previous impl won't work in a case like that:

<Button
  variant="outline"
  as={ExternalLink}
  mr={2}
  href={link}
  text="text"
  color="red"
  withArrow
/>

This will render the arrow in default color even if we pass color prop- the icon won't inherit color from parent.

) : (
icon
)}
</Link>
)
}
return (
<Link
ref={ref}
href={href}
target="_blank"
rel="noopener noreferrer"
color={finalColor}
textDecoration="underline"
{...props}
>
{text}
{withArrow ? <Icon boxSize="12px" ml="1" as={FiArrowUpRight} /> : icon}
</Link>
)
}
)

export * from "./SharedLinks"

Expand Down
121 changes: 121 additions & 0 deletions src/components/Modal/StakingApplications/AuthorizeStakingApps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { FC } from "react"
import {
BodyLg,
Button,
Card,
H5,
ModalBody,
ModalCloseButton,
ModalFooter,
ModalHeader,
LabelSm,
BodyMd,
List,
ListItem,
} from "@threshold-network/components"
import { CheckCircleIcon } from "@chakra-ui/icons"
import InfoBox from "../../InfoBox"
import TokenBalance from "../../TokenBalance"
import StakeAddressInfo from "../../../pages/Staking/StakeCard/StakeAddressInfo"
import withBaseModal from "../withBaseModal"
import {
calculatePercenteage,
formatPercentage,
} from "../../../utils/percentage"
import { BaseModalProps } from "../../../types"
import { useAuthorizeMultipleAppsTransaction } from "../../../hooks/staking-applications"

export type AuthorizeAppsProps = BaseModalProps & {
stakingProvider: string
totalInTStake: string
applications: {
appName: string
address: string
authorizationAmount: string
}[]
}

const AuthorizeStakingAppsBase: FC<AuthorizeAppsProps> = ({
stakingProvider,
totalInTStake,
applications,
closeModal,
}) => {
const { authorizeMultipleApps } = useAuthorizeMultipleAppsTransaction()
const onAuthorize = async () => {
await authorizeMultipleApps(
applications.map((_) => ({
address: _.address,
amount: _.authorizationAmount,
})),
stakingProvider
)
}
Comment on lines +45 to +53
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const onAuthorize = async () => {
await authorizeMultipleApps(
applications.map((_) => ({
address: _.address,
amount: _.authorizationAmount,
})),
stakingProvider
)
}
const onAuthorize = useCallback(async () => {
await authorizeMultipleApps(
applications.map((_) => ({
address: _.address,
amount: _.authorizationAmount,
})),
stakingProvider
)
}, [authorizeMultipleApps, applications, stakingProvider])

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't memoize the submit function because the applications props is an array. You can check with simple test:

const [increment, setIncrement] = useState(1) // run `setIncrement((i) => i+1)` on button click
  console.log("increment", increment)
  const onAuthorize = useCallback(async () => {
    await authorizeMultipleApps(
      applications.map((_) => ({
        address: _.address,
        amount: _.authorizationAmount,
      })),
      stakingProvider
    )
  }, [authorizeMultipleApps, applications, stakingProvider])

  useEffect(() => {
    console.log("updated")
  }, [onAuthorize])

Also I noticed that we use openModal in useAuthorizeMultipleAppsTransaction and in useSendTranacion which is not memoized and hook always returns a new instance of function on every render. See https://github.com/threshold-network/token-dashboard/blob/main/src/hooks/useModal.ts#L15-L16. We need to update it but I would address it in a separate PR.

const numberOfApps = applications.length

return (
<>
<ModalHeader>Authorize Apps</ModalHeader>
<ModalCloseButton />
<ModalBody>
<InfoBox variant="modal" mb="6" mt="0">
<H5>
You are authorizing your stake for Threshold application
{numberOfApps > 1 ? "s" : ""}.
</H5>
<BodyLg mt="4">
This will require {numberOfApps} transaction
{numberOfApps > 1 ? "s" : ""}. You can adjust the authorization
amount at any time.
</BodyLg>
</InfoBox>
<List spacing="2.5">
{applications.map((app) => (
<ListItem key={app.appName}>
<StakingApplicationToAuth
{...app}
stakingProvider={stakingProvider}
totalInTStake={totalInTStake}
/>
</ListItem>
))}
</List>
</ModalBody>
Comment on lines +72 to +83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'm going to add alerts in a separate PR- there are too many files to review now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gonna keep this convo as unresolved to not lost the track of it

<ModalFooter>
<Button onClick={closeModal} variant="outline" mr={2}>
Dismiss
</Button>
<Button mr={2} onClick={onAuthorize}>
Authorize
</Button>
</ModalFooter>
</>
)
}

const StakingApplicationToAuth: FC<{
appName: string
authorizationAmount: string
stakingProvider: string
totalInTStake: string
}> = ({ appName, authorizationAmount, stakingProvider, totalInTStake }) => {
const percentage = formatPercentage(
calculatePercenteage(authorizationAmount, totalInTStake),
undefined,
true
)

return (
<Card>
<LabelSm mb="4">
<CheckCircleIcon color="green.500" verticalAlign="top" mr="2" />
{appName} - {percentage}
</LabelSm>
<BodyMd mb="3">Authorization Amount</BodyMd>
<TokenBalance tokenAmount={authorizationAmount} isLarge />
<StakeAddressInfo stakingProvider={stakingProvider} mb="0" />
</Card>
)
}

export const AuthorizeStakingApps = withBaseModal(AuthorizeStakingAppsBase)
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { FC, Fragment } from "react"
import { Link as RouterLink, useNavigate } from "react-router-dom"
import {
HStack,
BodyLg,
Button,
H5,
ModalBody,
ModalCloseButton,
ModalFooter,
ModalHeader,
List,
ListItem,
Alert,
AlertIcon,
BodySm,
Divider,
Link,
FlowStepStatus,
} from "@threshold-network/components"
import InfoBox from "../../InfoBox"
import ExternalLink from "../../ExternalLink"
import ViewInBlockExplorer from "../../ViewInBlockExplorer"
import withBaseModal from "../withBaseModal"
import { useAppSelector } from "../../../hooks/store"
import { selectStakeByStakingProvider } from "../../../store/staking"
import {
calculatePercenteage,
formatPercentage,
} from "../../../utils/percentage"
import shortenAddress from "../../../utils/shortenAddress"
import { formatTokenAmount } from "../../../utils/formatAmount"
import { ExplorerDataType } from "../../../utils/createEtherscanLink"
import { ExternalHref } from "../../../enums"
import { BaseModalProps } from "../../../types"
import { getStakingAppNameFromAddress } from "../../../utils/getStakingAppNameFromAddress"
import StakingTimeline from "../../StakingTimeline"

export type StakingApplicationsAuthorizeProps = BaseModalProps & {
stakingProvider: string
authorizedStakingApplications: {
address: string
amount: string
txHash: string
}[]
}

const StakingApplicationsAuthorizedBase: FC<
StakingApplicationsAuthorizeProps
> = ({ stakingProvider, authorizedStakingApplications, closeModal }) => {
const stake = useAppSelector((state) =>
selectStakeByStakingProvider(state, stakingProvider)
)
const navigate = useNavigate()
const onAuthorizeOtherApps = () => {
closeModal()
navigate(`/staking/${stakingProvider}/authorize`)
Copy link
Contributor

@georgeweiler georgeweiler Sep 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is OK for now because I am doing the same in many modals, and I don't have a good solution to implement before the launch. But it's going to throw a warning, see:

https://dev.to/jexperton/how-to-fix-the-react-memory-leak-warning-d4i

also nit: why async method here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah we need to get rid of the warning some day. Reagrding async- it is unnecessary. I'll remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

return (
<>
<ModalHeader>Step 2 Completed</ModalHeader>
<ModalCloseButton />
<ModalBody>
<Alert status="success" mb={4}>
<AlertIcon />
Your authorization was successful!
</Alert>
<List spacing="2" mb="6">
<ListItem>
<HStack justifyContent="space-between">
<BodySm>Provider Address</BodySm>
<BodySm>{shortenAddress(stakingProvider)}</BodySm>
</HStack>
</ListItem>
{authorizedStakingApplications.map((_) => (
<ListItem key={_.address}>
<HStack justifyContent="space-between">
<BodySm>{`${getStakingAppNameFromAddress(
_.address
)} Authorization Amount`}</BodySm>
<BodySm>{`${formatTokenAmount(_.amount)} T (${formatPercentage(
calculatePercenteage(_.amount, stake?.totalInTStake)
)})`}</BodySm>
</HStack>
</ListItem>
))}
</List>
<InfoBox variant="modal">
<H5>
You can authorize more apps, or continue to Step 3 to set up nodes.
</H5>
<BodyLg mt="4">
You can adjust the authorization amount at any time from the{" "}
<Link as={RouterLink} to="/staking" color="brand.500">
Staking page
</Link>
.
</BodyLg>
</InfoBox>
<StakingTimeline
mt="9"
statuses={[
FlowStepStatus.complete,
FlowStepStatus.complete,
FlowStepStatus.active,
]}
/>
<BodySm align="center" mt="12">
{authorizedStakingApplications.length === 1 ? (
<>
<ViewInBlockExplorer
text="View"
id={authorizedStakingApplications[0].txHash}
type={ExplorerDataType.TRANSACTION}
/>{" "}
transaction on Etherscan
</>
) : (
<>
View{" "}
{authorizedStakingApplications.map((_, index) => (
<Fragment key={_.address}>
<ViewInBlockExplorer
text={`transaction ${index + 1}`}
id={_.txHash}
type={ExplorerDataType.TRANSACTION}
/>
{index + 1 === authorizedStakingApplications.length
? " "
: " and "}
</Fragment>
))}
on Etherscan
</>
)}
Comment on lines +119 to +136
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could specify here which transaction is which by displaying the name of the app? For example:

View TBTC authorization transaction and Random Beacon transaction on Etherscan

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we do that? We might need to make some changes in the copy, so we do not have a wall of text, though.

Making the changes now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@michalsmiarowski let's update that once we get the final copy, ok?

</BodySm>
<Divider mt="4" />
</ModalBody>
<ModalFooter>
<Button
variant="outline"
as={ExternalLink}
mr={2}
href={ExternalHref.setupNodes}
text="Node Setup Doc"
withArrow
/>
<Button onClick={onAuthorizeOtherApps} mr={2}>
Authorize Other Apps
</Button>
</ModalFooter>
</>
)
}

export const StakingApplicationsAuthorized = withBaseModal(
StakingApplicationsAuthorizedBase
)
2 changes: 2 additions & 0 deletions src/components/Modal/StakingApplications/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { AuthorizeStakingApps } from "./AuthorizeStakingApps"
export { StakingApplicationsAuthorized } from "./StakingApplicationsAuthorized"
Loading