Skip to content

Commit

Permalink
fix(tangle-dapp): Restake New Design (#2436)
Browse files Browse the repository at this point in the history
  • Loading branch information
AtelyPham authored Jul 20, 2024
1 parent e33df9d commit 60b225a
Show file tree
Hide file tree
Showing 79 changed files with 1,803 additions and 1,359 deletions.
35 changes: 35 additions & 0 deletions apps/tangle-dapp/app/restake/AssetList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { TokenListCard } from '@webb-tools/webb-ui-components/components/ListCard/TokenListCard';
import type { TokenListCardProps } from '@webb-tools/webb-ui-components/components/ListCard/types';
import { twMerge } from 'tailwind-merge';

const AssetList = ({
className,
title = 'Select an asset',
popularTokens = [],
unavailableTokens = [],
selectTokens = [],
...props
}: Partial<TokenListCardProps>) => {
return (
<TokenListCard
type="asset"
overrideTitleProps={{
variant: 'h4',
}}
{...props}
selectTokens={selectTokens}
popularTokens={popularTokens}
unavailableTokens={unavailableTokens}
title={title}
className={twMerge(
'h-full mx-auto dark:bg-[var(--restake-card-bg-dark)]',
className,
)}
overrideInputProps={{
placeholder: 'Search for an asset',
}}
/>
);
};

export default AssetList;
36 changes: 25 additions & 11 deletions apps/tangle-dapp/app/restake/AvatarWithText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,28 @@ import { Typography } from '@webb-tools/webb-ui-components/typography/Typography
import { shortenHex } from '@webb-tools/webb-ui-components/utils/shortenHex';
import { shortenString } from '@webb-tools/webb-ui-components/utils/shortenString';
import isEqual from 'lodash/isEqual';
import { type ComponentProps, memo } from 'react';
import { type ComponentProps, memo, type ReactNode } from 'react';
import { twMerge } from 'tailwind-merge';
import { isHex } from 'viem';

type Props = ComponentProps<'div'> & {
accountAddress: string;
description?: ReactNode;
identityName?: string | null;
overrideAvatarProps?: Partial<ComponentProps<typeof Avatar>>;
overrideTypographyProps?: Partial<ComponentProps<typeof Typography>>;
};

const AvatarWithText = ({
accountAddress,
className,
description,
identityName,
overrideAvatarProps,
overrideTypographyProps,
className,
...props
}: Props) => {
console.log('identityName', identityName);
return (
<div
{...props}
Expand All @@ -41,15 +46,24 @@ const AvatarWithText = ({
)}
/>

<Typography
variant="body2"
{...overrideTypographyProps}
className={twMerge('truncate', overrideTypographyProps?.className)}
>
{isHex(accountAddress)
? shortenHex(accountAddress)
: shortenString(accountAddress)}
</Typography>
<div>
<Typography
component="span"
variant="body2"
{...overrideTypographyProps}
className={twMerge(
'truncate block',
overrideTypographyProps?.className,
)}
>
{identityName ||
(isHex(accountAddress)
? shortenHex(accountAddress)
: shortenString(accountAddress))}
</Typography>

{description}
</div>
</div>
);
};
Expand Down
25 changes: 0 additions & 25 deletions apps/tangle-dapp/app/restake/Card.tsx

This file was deleted.

6 changes: 2 additions & 4 deletions apps/tangle-dapp/app/restake/ChainList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const ChainList = ({
return (
<ChainListCard
chainType="source"
disclaimer=""
overrideTitleProps={{
variant: 'h4',
}}
Expand All @@ -66,13 +67,10 @@ const ChainList = ({
}
defaultCategory={defaultCategory}
isConnectingToChain={loading}
overrideScrollAreaProps={{
className: 'h-[320px]',
}}
{...props}
onClose={onClose}
className={twMerge(
'p-0 dark:bg-[var(--restake-card-bg-dark)]',
'h-full mx-auto dark:bg-[var(--restake-card-bg-dark)]',
className,
)}
/>
Expand Down
21 changes: 21 additions & 0 deletions apps/tangle-dapp/app/restake/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type ComponentProps, forwardRef } from 'react';
import { twMerge } from 'tailwind-merge';

const Form = forwardRef<HTMLFormElement, ComponentProps<'form'>>(
({ className, ...props }, ref) => {
return (
<form
{...props}
className={twMerge(
'w-full max-w-lg mx-auto overflow-hidden',
className,
)}
ref={ref}
/>
);
},
);

Form.displayName = 'Form';

export default Form;
37 changes: 37 additions & 0 deletions apps/tangle-dapp/app/restake/ModalContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
ModalContent as ModalContentCmp,
ModalDescription,
ModalTitle,
} from '@webb-tools/webb-ui-components/components/Modal';
import { ComponentProps } from 'react';
import { twMerge } from 'tailwind-merge';

const ModalContent = ({
children,
className,
title,
description,
...props
}: ComponentProps<typeof ModalContentCmp> & {
title: string;
description: string;
}) => {
return (
<ModalContentCmp
isCenter
{...props}
className={twMerge(
'w-full h-full p-4 max-w-xl max-h-[var(--restake-modal-max-height)]',
className,
)}
>
<ModalTitle className="sr-only">{title}</ModalTitle>

<ModalDescription className="sr-only">{description}</ModalDescription>

{children}
</ModalContentCmp>
);
};

export default ModalContent;
157 changes: 157 additions & 0 deletions apps/tangle-dapp/app/restake/OperatorList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
'use client';

import { Cross1Icon } from '@radix-ui/react-icons';
import { Search } from '@webb-tools/icons/Search';
import Button from '@webb-tools/webb-ui-components/components/buttons/Button';
import { Input } from '@webb-tools/webb-ui-components/components/Input';
import { KeyValueWithButton } from '@webb-tools/webb-ui-components/components/KeyValueWithButton';
import { ListCardWrapper } from '@webb-tools/webb-ui-components/components/ListCard/ListCardWrapper';
import { ListItem } from '@webb-tools/webb-ui-components/components/ListCard/ListItem';
import { ScrollArea } from '@webb-tools/webb-ui-components/components/ScrollArea';
import { Typography } from '@webb-tools/webb-ui-components/typography/Typography';
import { shortenString } from '@webb-tools/webb-ui-components/utils/shortenString';
import isFunction from 'lodash/isFunction';
import keys from 'lodash/keys';
import omitBy from 'lodash/omitBy';
import pick from 'lodash/pick';
import { type ComponentProps, forwardRef, useMemo, useState } from 'react';
import { twMerge } from 'tailwind-merge';

import type { OperatorMap } from '../../types/restake';
import type { IdentityType } from '../../utils/polkadot';
import AvatarWithText from './AvatarWithText';

type Props = Partial<ComponentProps<typeof ListCardWrapper>> & {
operatorMap: OperatorMap;
operatorIdentities?: Record<string, IdentityType | null> | null;
selectedOperatorAccountId: string;
onOperatorAccountIdChange?: (accountId: string) => void;
onResetSelection?: () => void;
};

const OperatorList = forwardRef<HTMLDivElement, Props>(
(
{
onClose,
overrideTitleProps,
operatorMap: operatorMapProp,
operatorIdentities,
selectedOperatorAccountId,
onOperatorAccountIdChange,
onResetSelection,
...props
},
ref,
) => {
const [searchText, setSearchText] = useState('');

// Only show active operators
const activeOperator = useMemo(
() => omitBy(operatorMapProp, (operator) => operator.status !== 'Active'),
[operatorMapProp],
);

const isEmpty = Object.keys(activeOperator).length === 0;

const filteredOperator = useMemo(() => {
if (searchText === '') return activeOperator;

const pickedOperators = keys(activeOperator).filter((operator) => {
const identity = operatorIdentities?.[operator]?.name;
if (!identity) return operator.includes(searchText);

return (
identity.toLowerCase().includes(searchText.toLowerCase()) ||
operator.includes(searchText)
);
});

return pick(activeOperator, pickedOperators);
}, [activeOperator, operatorIdentities, searchText]);

return (
<ListCardWrapper
{...props}
title="Select Operator"
onClose={onClose}
ref={ref}
>
{!isEmpty && (
<>
<div className="py-4">
<Input
id="token"
rightIcon={<Search />}
placeholder="Search Operator"
isControlled
value={searchText}
onChange={(val) => setSearchText(val.toString())}
/>
</div>

<ScrollArea className={twMerge('h-full py-2')}>
<ul>
{keys(filteredOperator).map((current) => (
<ListItem
key={current}
className="px-4 cursor-pointer max-w-none dark:bg-transparent"
onClick={() => onOperatorAccountIdChange?.(current)}
>
<AvatarWithText
accountAddress={current}
overrideAvatarProps={{ size: 'lg' }}
overrideTypographyProps={{ variant: 'h5' }}
identityName={
operatorIdentities?.[current]?.name || '<Unknown>'
}
description={
<KeyValueWithButton
size="sm"
keyValue={current}
shortenFn={shortenString}
/>
}
/>
</ListItem>
))}
</ul>
</ScrollArea>

{isFunction(onResetSelection) && (
<Button
isDisabled={!selectedOperatorAccountId}
className="mt-auto"
leftIcon={<Cross1Icon />}
isFullWidth
onClick={onResetSelection}
>
Clear Selection
</Button>
)}
</>
)}

{isEmpty && (
<div className="flex flex-col items-center justify-center space-y-4 grow">
<Typography variant="h5" fw="bold" ta="center">
No Operator Found.
</Typography>

<Typography
variant="body1"
fw="semibold"
className="max-w-xs mt-1 text-mono-100 dark:text-mono-80"
ta="center"
>
You can comeback later or add apply to become a operator.
</Typography>
</div>
)}
</ListCardWrapper>
);
},
);

OperatorList.displayName = 'OperatorList';

export default OperatorList;
Loading

0 comments on commit 60b225a

Please sign in to comment.