diff --git a/dapp/src/assets/icons/LoadingSpinnerSuccessIcon.tsx b/dapp/src/assets/icons/LoadingSpinnerSuccessIcon.tsx index 78abf496b..a4501cb14 100644 --- a/dapp/src/assets/icons/LoadingSpinnerSuccessIcon.tsx +++ b/dapp/src/assets/icons/LoadingSpinnerSuccessIcon.tsx @@ -3,22 +3,22 @@ import { createIcon } from "@chakra-ui/react" export const LoadingSpinnerSuccessIcon = createIcon({ displayName: "LoadingSpinnerSuccessIcon", - viewBox: "0 0 72 73", + viewBox: "0 0 48 48", defaultProps: { fill: "none", }, path: [ , , diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx new file mode 100644 index 000000000..d4df818ed --- /dev/null +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesList.tsx @@ -0,0 +1,53 @@ +import React from "react" +import { List, ListItem, ListProps } from "@chakra-ui/react" +import { AnimatePresence, Variants, motion } from "framer-motion" +import { useActivitiesNEW as useActivities } from "#/hooks" +import ActivitiesListItem from "./ActivitiesListItem" + +const MotionList = motion(List) +const MotionListItem = motion(ListItem) + +const listItemVariants: Variants = { + mounted: { opacity: 1, x: 0 }, + dismissed: { opacity: 0, x: -48 }, +} + +function ActivitiesList(props: ListProps) { + const activities = useActivities() + + const [dismissedActivities, setDismissedActivities] = React.useState< + string[] + >([]) + + const handleItemDismiss = (txHash: string) => { + setDismissedActivities((prev) => [...prev, txHash]) + } + + return ( + + {activities.map((item) => ( + + {!dismissedActivities.includes(item.txHash) && ( + + handleItemDismiss(item.txHash)} + /> + + )} + + ))} + + ) +} + +export default ActivitiesList diff --git a/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx new file mode 100644 index 000000000..73403a4ed --- /dev/null +++ b/dapp/src/components/shared/ActivitiesList/ActivitiesListItem.tsx @@ -0,0 +1,93 @@ +import React from "react" +import { ArrowUpRight, LoadingSpinnerSuccessIcon } from "#/assets/icons" +import { Activity as ActivityType } from "#/types" +import { + Alert, + AlertDescription, + AlertIcon, + AlertProps, + AlertTitle, + Button, + HStack, + Text, + VStack, + VisuallyHidden, +} from "@chakra-ui/react" +import { CurrencyBalance } from "../CurrencyBalance" +import Spinner from "../Spinner" +import BlockExplorerLink from "../BlockExplorerLink" + +type ActivitiesListItemProps = Omit & + ActivityType & { + handleDismiss?: () => void + } + +function ActivitiesListItem(props: ActivitiesListItemProps) { + const { amount, status, txHash, type, handleDismiss, ...restProps } = props + + return ( + + + + + + + {status === "completed" + ? `${type === "withdraw" ? "Unstaking" : "Staking"} completed` + : `${type === "withdraw" ? "Unstaking" : "Staking"}...`} + + + + + + {status === "completed" ? ( + + ) : ( + // TODO: To implement. Estimated activity time is not in scope of MVP. + // eslint-disable-next-line react/jsx-no-useless-fragment + <> + {/* Est. time remaining + + {new Date(estimatedTime).getHours()}h + */} + + )} + + + + {txHash && ( + + + View transaction details + + )} + + ) +} + +export default ActivitiesListItem diff --git a/dapp/src/components/shared/ActivitiesList/index.ts b/dapp/src/components/shared/ActivitiesList/index.ts new file mode 100644 index 000000000..30619d084 --- /dev/null +++ b/dapp/src/components/shared/ActivitiesList/index.ts @@ -0,0 +1 @@ +export { default as ActivitiesList } from "./ActivitiesList" diff --git a/dapp/src/components/shared/BlockExplorerLink/index.tsx b/dapp/src/components/shared/BlockExplorerLink/index.tsx index 82b65d503..f97f61435 100644 --- a/dapp/src/components/shared/BlockExplorerLink/index.tsx +++ b/dapp/src/components/shared/BlockExplorerLink/index.tsx @@ -4,25 +4,24 @@ import { Link, LinkProps } from "@chakra-ui/react" import { createLinkToBlockExplorerForChain } from "#/utils" type BlockExplorerLinkProps = { - text?: string id: string type: ExplorerDataType chain?: Chain } & Omit function BlockExplorerLink({ - text, id, type, chain = "ethereum", - ...linkProps + children, + ...restProps }: BlockExplorerLinkProps) { const { link, title } = createLinkToBlockExplorerForChain(chain, id, type) // TODO: Update when ButtonLink is ready return ( - - {text ?? title} + + {children ?? title} ) } diff --git a/dapp/src/pages/DashboardPage/DashboardCard.tsx b/dapp/src/pages/DashboardPage/DashboardCard.tsx index 53bd4b16d..5b97bd116 100644 --- a/dapp/src/pages/DashboardPage/DashboardCard.tsx +++ b/dapp/src/pages/DashboardPage/DashboardCard.tsx @@ -16,6 +16,7 @@ import IconTag from "#/components/shared/IconTag" import { BoostArrowIcon } from "#/assets/icons" import { CurrencyBalanceWithConversion } from "#/components/shared/CurrencyBalanceWithConversion" import { AmountType } from "#/types" +import { ActivitiesList } from "#/components/shared/ActivitiesList" const buttonStyles: ButtonProps = { size: "lg", @@ -87,6 +88,8 @@ export default function DashboardCard(props: DashboardCardProps) { Withdraw + + ) diff --git a/dapp/src/theme/Alert.ts b/dapp/src/theme/Alert.ts index cb002ea56..f5dd4b392 100644 --- a/dapp/src/theme/Alert.ts +++ b/dapp/src/theme/Alert.ts @@ -6,7 +6,11 @@ import { defineStyle, } from "@chakra-ui/react" -const baseStyleDialog = defineStyle({ +const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys) + +// Base styles + +const baseContainerStyles = defineStyle({ px: 5, borderRadius: "xl", textAlign: "left", @@ -15,17 +19,17 @@ const baseStyleDialog = defineStyle({ boxShadow: "0px 8px 12px 0px var(--chakra-colors-opacity-black-15)", }) -const baseStyleIcon = defineStyle({ +const baseIconStyles = defineStyle({ mr: 2, }) -const multiStyleConfig = createMultiStyleConfigHelpers(parts.keys) - const baseStyle = multiStyleConfig.definePartsStyle({ - container: baseStyleDialog, - icon: baseStyleIcon, + container: baseContainerStyles, + icon: baseIconStyles, }) +// Status styles + const statusInfo = multiStyleConfig.definePartsStyle({ container: { bg: "blue.500", @@ -57,12 +61,53 @@ const statusStyles = (props: StyleFunctionProps) => { return styleMap[status as string] || {} } +// Subtle variant styles + const variantSubtle = multiStyleConfig.definePartsStyle((props) => statusStyles(props), ) +// Process variant styles + +const processContainerStyles = defineStyle({ + px: 6, + py: 4, + bg: "gold.300", + borderWidth: 1, + borderColor: "gold.100", + shadow: "none", + alignItems: "flex-start", +}) + +const processIconStyles = defineStyle({ + mr: 4, + w: 12, + h: 12, +}) + +const processTitleStyles = defineStyle({ + color: "grey.700", + fontWeight: "bold", + m: 0, +}) + +const processDescriptionStyles = defineStyle({ + color: "grey.500", + fontWeight: "medium", +}) + +const processVariant = multiStyleConfig.definePartsStyle({ + container: processContainerStyles, + icon: processIconStyles, + title: processTitleStyles, + description: processDescriptionStyles, +}) + +// Theme definition + const variants = { subtle: variantSubtle, + process: processVariant, } export const alertTheme = multiStyleConfig.defineMultiStyleConfig({ diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/Button.ts index 5b221fb44..94a299791 100644 --- a/dapp/src/theme/Button.ts +++ b/dapp/src/theme/Button.ts @@ -133,5 +133,12 @@ export const buttonTheme: ComponentSingleStyleConfig = { pointerEvents: "none", }, }, + link: { + bg: "initial", + color: "inherit", + p: 0, + textDecoration: "underline", + fontWeight: "medium", + }, }, }