Skip to content

Commit

Permalink
feat(Wallet): add welcome banner (#1337)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsimao authored Jun 29, 2023
1 parent c1e15ac commit 66db4d1
Show file tree
Hide file tree
Showing 12 changed files with 915 additions and 13 deletions.
705 changes: 705 additions & 0 deletions src/assets/img/btc-defi.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/assets/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,9 @@
"no_assets_available": "No assets available",
"total_governance_locked": "Total {{token}} Locked",
"available_to_stake": "Available to stake",
"voting_power_governance": "Voting Power {{token}}"
"voting_power_governance": "Voting Power {{token}}",
"welcome_to_dapp": "Welcome to {{ name }}",
"dapp_is_a_one_stop_shop_for_bitcoin_defi": "{{ name }} is a one-stop shop for Bitcoin DeFi: swaps, lending, staking, and 1-click strategies. {{ wrappedToken }} is trustless multi-chain Bitcoin, secured by collateral, cryptography, and a decentralized vault network."
},
"strategy": {
"withdraw_rewards_in_wrapped": "Withdraw rewards in {{wrappedCurrencySymbol}}:",
Expand Down
11 changes: 10 additions & 1 deletion src/component-library/CTA/CTA.style.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import styled from 'styled-components';

import { ArrowTopRightOnSquare } from '@/assets/icons';

import { LoadingSpinner } from '../LoadingSpinner';
import { theme } from '../theme';
import { CTASizes, CTAVariants } from '../utils/prop-types';
Expand Down Expand Up @@ -82,5 +84,12 @@ const StyledLoadingSpinner = styled(LoadingSpinner)<StyledLoadingSpinnerProps>`
border-left-color: transparent;
`;

export { LoadingWrapper, OutlinedCTA, PrimaryCTA, SecondaryCTA, StyledLoadingSpinner, TextCTA };
const StyledIcon = styled(ArrowTopRightOnSquare)`
margin-left: ${theme.spacing.spacing2};
width: 1.2em;
height: 1.2em;
color: inherit;
`;

export { LoadingWrapper, OutlinedCTA, PrimaryCTA, SecondaryCTA, StyledIcon, StyledLoadingSpinner, TextCTA };
export type { StyledCTAProps };
5 changes: 4 additions & 1 deletion src/component-library/CTA/CTALink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { Link, LinkProps } from 'react-router-dom';

import { useDOMRef } from '../utils/dom';
import { BaseCTA, BaseCTAProps } from './BaseCTA';
import { StyledIcon } from './CTA.style';

type Props = {
external?: boolean;
disabled?: boolean;
icon?: boolean;
};

type NativeAttrs = Omit<LinkProps, keyof Props | 'href' | 'onFocus' | 'onBlur'>;
Expand All @@ -22,7 +24,7 @@ type CTALinkProps = Props & NativeAttrs & AriaAttrs & InheritAttrs;

// TODO: Does this need to be changed to a React Router link component?
const CTALink = forwardRef<HTMLAnchorElement, CTALinkProps>(
({ disabled, external, to: toProp, children, ...props }, ref): JSX.Element => {
({ disabled, external, to: toProp, children, icon, ...props }, ref): JSX.Element => {
const linkRef = useDOMRef(ref);

const ariaProps = {
Expand All @@ -49,6 +51,7 @@ const CTALink = forwardRef<HTMLAnchorElement, CTALinkProps>(
})}
>
{children}
{icon && <StyledIcon color='secondary' />}
</BaseCTA>
);
}
Expand Down
2 changes: 2 additions & 0 deletions src/config/links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const INTERLAY_SUBSCAN_LINK = 'https://interlay.subscan.io';
const KINTSUGI_SUBSCAN_LINK = 'https://kintsugi.subscan.io';
const INTERLAY_VAULT_DOCS_LINK = 'https://docs.interlay.io/#/vault/overview';
const INTERLAY_DOS_AND_DONTS_DOCS_LINK = 'https://docs.interlay.io/#/vault/installation?id=dos-and-donts';
const INTERLAY_WHITEPAPPER = 'https://gateway.pinata.cloud/ipfs/QmWp62gdLssFpAoG2JqK8sy3m3rTRUa8LyzoSY8ZFisYNB';

const BANXA_LINK = 'http://talisman.banxa.com/';

Expand All @@ -41,6 +42,7 @@ export {
INTERLAY_TWITTER_LINK,
INTERLAY_USE_WRAPPED_CURRENCY_LINK,
INTERLAY_VAULT_DOCS_LINK,
INTERLAY_WHITEPAPPER,
KINTSUGI_CROWDLOAN_LINK,
KINTSUGI_GOVERNANCE_LINK,
KINTSUGI_SUBSCAN_LINK,
Expand Down
8 changes: 7 additions & 1 deletion src/pages/Wallet/WalletOverview/WalletOverview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-
import { useGetLoanAssets } from '@/utils/hooks/api/loans/use-get-loan-assets';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import useAccountId from '@/utils/hooks/use-account-id';
import { LocalStorageKey, useLocalStorage } from '@/utils/hooks/use-local-storage';

import { AvailableAssetsTable, StakingTable, WalletInsights } from './components';
import { AvailableAssetsTable, StakingTable, WalletInsights, WelcomeBanner } from './components';

const WalletOverview = (): JSX.Element => {
const [isBannerOpen, setBannerOpen] = useLocalStorage(LocalStorageKey.WALLET_WELCOME_BANNER, true);

const accountId = useAccountId();
const { data: balances } = useGetBalances();
const { data: accountPoolsData } = useGetAccountPools();
Expand All @@ -29,6 +32,8 @@ const WalletOverview = (): JSX.Element => {
return <FullLoadingSpinner />;
}

const handleCloseBanner = () => setBannerOpen(false);

const hasStakingTable =
accountStakingData &&
accountVotingBalance &&
Expand All @@ -38,6 +43,7 @@ const WalletOverview = (): JSX.Element => {

return (
<MainContainer>
{isBannerOpen && <WelcomeBanner onClose={handleCloseBanner} />}
<WalletInsights balances={balances} />
<AvailableAssetsTable balances={balances} pooledTickers={pooledTickers} />
{!!lendPositions?.length && assets && (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import styled from 'styled-components';

import { XMark } from '@/assets/icons';
import { ReactComponent as BTCDefi } from '@/assets/img/btc-defi.svg';
import { Card, CTA, CTALink, Flex, H2, P, theme } from '@/component-library';

const StyledCard = styled(Card)`
position: relative;
overflow: hidden;
z-index: 0;
background-color: #1a0144;
`;

const StyledImageWrapper = styled(Flex)`
display: none;
flex: 0;
@media ${theme.breakpoints.up('md')} {
display: flex;
margin-right: ${theme.spacing.spacing6};
}
@media ${theme.breakpoints.up('lg')} {
margin-right: ${theme.spacing.spacing10};
}
`;

const StyledSVG = styled(BTCDefi)`
width: 9.875rem;
height: 8.125rem;
@media ${theme.breakpoints.up('md')} {
display: block;
top: ${theme.spacing.spacing8};
bottom: ${theme.spacing.spacing8};
right: ${theme.spacing.spacing18};
}
`;

const StyledTextWrapper = styled(Flex)`
@media ${theme.breakpoints.up('md')} {
max-width: 70%;
}
`;

const StyledCloseCTA = styled(CTA)`
position: absolute;
top: ${theme.spacing.spacing3};
right: ${theme.spacing.spacing3};
`;

const StyledXMark = styled(XMark)`
color: var(--colors-neutral-white);
`;

const StyledP = styled(P)`
color: #ffffffb2;
`;

const StyledCTALink = styled(CTALink)`
border-radius: ${theme.rounded.lg};
font-size: ${theme.text.s};
padding: ${theme.spacing.spacing2} ${theme.spacing.spacing6};
color: #1a0144;
background-color: var(--colors-neutral-white);
&:hover:not([disabled]) {
background-color: var(--colors-neutral-lighter-grey);
}
`;

const StyledTitle = styled(H2)`
color: var(--colors-neutral-white);
font-size: ${theme.text.lg};
`;

const StyledCTAGroup = styled(Flex)`
flex-wrap: wrap;
@media ${theme.breakpoints.up('sm')} {
flex-wrap: nowrap;
}
@media ${theme.breakpoints.up('lg')} {
max-width: 70%;
}
@media ${theme.breakpoints.up('xl')} {
max-width: 50%;
}
`;

export {
StyledCard,
StyledCloseCTA,
StyledCTAGroup,
StyledCTALink,
StyledImageWrapper,
StyledP,
StyledSVG,
StyledTextWrapper,
StyledTitle,
StyledXMark
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useTranslation } from 'react-i18next';

import { Flex } from '@/component-library';
import { INTERLAY_WHITEPAPPER } from '@/config/links';
import { APP_NAME, WRAPPED_TOKEN } from '@/config/relay-chains';

import {
StyledCard,
StyledCloseCTA,
StyledCTAGroup,
StyledCTALink,
StyledImageWrapper,
StyledP,
StyledSVG,
StyledTextWrapper,
StyledTitle,
StyledXMark
} from './WelcomeBanner.styles';

type WelcomeBannerProps = {
onClose: () => void;
};

const WelcomeBanner = ({ onClose }: WelcomeBannerProps): JSX.Element => {
const { t } = useTranslation();

return (
<StyledCard direction='row' gap='spacing4' justifyContent='space-between' alignItems='center'>
<StyledTextWrapper direction='column' gap='spacing6'>
<Flex direction='column' gap='spacing4'>
<StyledTitle weight='bold' size='xl2'>
{t('wallet.welcome_to_dapp', { name: APP_NAME })}
</StyledTitle>
<StyledP size='s' color='tertiary'>
{t('wallet.dapp_is_a_one_stop_shop_for_bitcoin_defi', {
name: APP_NAME,
wrappedToken: WRAPPED_TOKEN.ticker
})}
</StyledP>
</Flex>
<StyledCTAGroup gap='spacing4'>
<StyledCTALink fullWidth external icon to={INTERLAY_WHITEPAPPER}>
Whitepaper
</StyledCTALink>
{/* TODO: to be added */}
<StyledCTALink fullWidth external icon to={'#'}>
Fund Wallet Guide
</StyledCTALink>
</StyledCTAGroup>
</StyledTextWrapper>
<StyledImageWrapper>
<StyledSVG aria-label='bitcoin defi' />
</StyledImageWrapper>
<StyledCloseCTA size='small' variant='text' aria-label='dimiss welcome banner' onPress={onClose}>
<StyledXMark />
</StyledCloseCTA>
</StyledCard>
);
};

export { WelcomeBanner };
export type { WelcomeBannerProps };
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { WelcomeBannerProps } from './WelcomeBanner';
export { WelcomeBanner } from './WelcomeBanner';
5 changes: 3 additions & 2 deletions src/pages/Wallet/WalletOverview/components/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { AvailableAssetsTable, AvailableAssetsTableProps } from './AvailableAssetsTable';
import { StakingTable, StakingTableProps } from './StakingTable';
import { WalletInsights, WalletInsightsProps } from './WalletInsights';
import { WelcomeBanner, WelcomeBannerProps } from './WelcomeBanner';

export { AvailableAssetsTable, StakingTable, WalletInsights };
export type { AvailableAssetsTableProps, StakingTableProps, WalletInsightsProps };
export { AvailableAssetsTable, StakingTable, WalletInsights, WelcomeBanner };
export type { AvailableAssetsTableProps, StakingTableProps, WalletInsightsProps, WelcomeBannerProps };
16 changes: 11 additions & 5 deletions src/utils/hooks/use-local-storage.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { useLocalStorage as useLibLocalStorage } from 'react-use';

enum LocalStorageKey {
TC_SIGNATURES = 'TC_SIGNATURES'
TC_SIGNATURES = 'TC_SIGNATURES',
WALLET_WELCOME_BANNER = 'WALLET_WELCOME_BANNER'
}

type TCSignaturesData = Record<string, boolean>;
type LocalStorageValueTypes = {
[LocalStorageKey.TC_SIGNATURES]: Record<string, boolean>;
[LocalStorageKey.WALLET_WELCOME_BANNER]: boolean;
};

type Options<T = unknown> =
| {
Expand All @@ -18,8 +22,10 @@ type Options<T = unknown> =
| undefined;

// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
const useLocalStorage = <T = unknown>(key: LocalStorageKey, initialValue?: T, options?: Options<T>) =>
useLibLocalStorage(key, initialValue, options);
const useLocalStorage = <T extends keyof LocalStorageValueTypes>(
key: T,
initialValue?: LocalStorageValueTypes[T],
options?: Options<LocalStorageValueTypes[T]>
) => useLibLocalStorage<LocalStorageValueTypes[T]>(key, initialValue, options);

export { LocalStorageKey, useLocalStorage };
export type { TCSignaturesData };
4 changes: 2 additions & 2 deletions src/utils/hooks/use-sign-message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { KeyringPair, useSubstrateSecureState } from '@/lib/substrate';

import { NotificationToastType, useNotifications } from '../context/Notifications';
import { signMessage } from '../helpers/wallet';
import { LocalStorageKey, TCSignaturesData, useLocalStorage } from './use-local-storage';
import { LocalStorageKey, useLocalStorage } from './use-local-storage';

interface GetSignatureData {
exists: boolean;
Expand Down Expand Up @@ -56,7 +56,7 @@ const useSignMessage = (): UseSignMessageResult => {
const notifications = useNotifications();

const dispatch = useDispatch();
const [signatures, setSignatures] = useLocalStorage<TCSignaturesData>(LocalStorageKey.TC_SIGNATURES);
const [signatures, setSignatures] = useLocalStorage(LocalStorageKey.TC_SIGNATURES);
const { selectedAccount } = useSubstrateSecureState();

const setSignature = useCallback(
Expand Down

2 comments on commit 66db4d1

@vercel
Copy link

@vercel vercel bot commented on 66db4d1 Jun 29, 2023

Choose a reason for hiding this comment

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

@vercel
Copy link

@vercel vercel bot commented on 66db4d1 Jun 29, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.