Commit 24cfa420 authored by Max Alekseenko's avatar Max Alekseenko

rework dapp page header

parent a6aed0e6
import { Alert } from '@chakra-ui/react';
import React from 'react';
import type { IconName } from 'ui/shared/IconSvg';
import IconSvg from 'ui/shared/IconSvg';
type Props = {
internalWallet: boolean | undefined;
isWalletConnected: boolean;
}
const MarketplaceAppAlert = ({ internalWallet, isWalletConnected }: Props) => {
const message = React.useMemo(() => {
let icon: IconName = 'wallet';
let text = 'Connect your wallet to Blockscout for full-featured access';
let status: 'warning' | 'success' = 'warning';
if (isWalletConnected && internalWallet) {
icon = 'integration/full';
text = 'Your wallet is connected with Blockscout';
status = 'success';
} else if (!internalWallet) {
icon = 'integration/partial';
text = 'Connect your wallet in the app below';
}
return { icon, text, status };
}, [ isWalletConnected, internalWallet ]);
return (
<Alert
status={ message.status }
borderRadius="base"
px={ 3 }
py={{ base: 3, md: 1.5 }}
fontSize="sm"
lineHeight={ 5 }
>
<IconSvg
name={ message.icon }
color={ message.status === 'success' ? 'green.600' : 'current' }
boxSize={ 5 }
flexShrink={ 0 }
mr={ 2 }
/>
{ message.text }
</Alert>
);
};
export default MarketplaceAppAlert;
import { chakra, Flex, Tooltip, Skeleton, useBoolean, Box } from '@chakra-ui/react'; import { chakra, Flex, Tooltip, Skeleton, useBoolean } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { MarketplaceAppOverview, MarketplaceAppSecurityReport } from 'types/client/marketplace'; import type { MarketplaceAppOverview, MarketplaceAppSecurityReport } from 'types/client/marketplace';
...@@ -6,16 +6,19 @@ import { ContractListTypes } from 'types/client/marketplace'; ...@@ -6,16 +6,19 @@ import { ContractListTypes } from 'types/client/marketplace';
import { route } from 'nextjs-routes'; import { route } from 'nextjs-routes';
import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app'; import { useAppContext } from 'lib/contexts/app';
import useFeatureValue from 'lib/growthbook/useFeatureValue'; import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg'; import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal'; import LinkExternal from 'ui/shared/LinkExternal';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
import NetworkLogo from 'ui/snippets/networkMenu/NetworkLogo';
import ProfileMenuDesktop from 'ui/snippets/profileMenu/ProfileMenuDesktop';
import WalletMenuDesktop from 'ui/snippets/walletMenu/WalletMenuDesktop';
import AppSecurityReport from './AppSecurityReport'; import AppSecurityReport from './AppSecurityReport';
import ContractListModal from './ContractListModal'; import ContractListModal from './ContractListModal';
import MarketplaceAppAlert from './MarketplaceAppAlert';
import MarketplaceAppInfo from './MarketplaceAppInfo'; import MarketplaceAppInfo from './MarketplaceAppInfo';
type Props = { type Props = {
...@@ -25,7 +28,7 @@ type Props = { ...@@ -25,7 +28,7 @@ type Props = {
securityReport?: MarketplaceAppSecurityReport; securityReport?: MarketplaceAppSecurityReport;
} }
const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityReport }: Props) => { const MarketplaceAppTopBar = ({ data, isLoading, /* isWalletConnected, */ securityReport }: Props) => {
const [ showContractList, setShowContractList ] = useBoolean(false); const [ showContractList, setShowContractList ] = useBoolean(false);
const appProps = useAppContext(); const appProps = useAppContext();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
...@@ -46,32 +49,14 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo ...@@ -46,32 +49,14 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo
return ( return (
<> <>
<Flex alignItems="center" flexWrap="wrap" mb={{ base: 6, md: 2 }} rowGap={ 3 } columnGap={ 2 }> <Flex alignItems="center" flexWrap="wrap" mb={{ base: 3, md: 2 }} rowGap={ 3 } columnGap={ 2 }>
<Tooltip label="Back to dApps list" order={ 1 }> { !isMobile && <NetworkLogo isCollapsed/> }
<LinkInternal display="inline-flex" href={ goBackUrl } h="32px" isLoading={ isLoading }> <Tooltip label="Back to dApps list">
<LinkInternal display="inline-flex" href={ goBackUrl } h="32px" isLoading={ isLoading } ml={ isMobile ? 0 : 4 }>
<IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" margin="auto" color="gray.400"/> <IconSvg name="arrows/east" boxSize={ 6 } transform="rotate(180deg)" margin="auto" color="gray.400"/>
</LinkInternal> </LinkInternal>
</Tooltip> </Tooltip>
<Skeleton width={{ base: '100%', md: 'auto' }} order={{ base: 5, md: 2 }} isLoaded={ !isLoading }>
<MarketplaceAppAlert internalWallet={ data?.internalWallet } isWalletConnected={ isWalletConnected }/>
</Skeleton>
<Skeleton order={{ base: 2, md: 3 }} isLoaded={ !isLoading }>
<MarketplaceAppInfo data={ data }/>
</Skeleton>
{ (isExperiment && (securityReport || isLoading)) && (
<Box order={{ base: 3, md: 4 }}>
<AppSecurityReport
id={ data?.id || '' }
securityReport={ securityReport }
showContractList={ setShowContractList.on }
isLoading={ isLoading }
onlyIcon={ isMobile }
source="App page"
/>
</Box>
) }
<LinkExternal <LinkExternal
order={{ base: 4, md: 5 }}
href={ data?.url } href={ data?.url }
variant="subtle" variant="subtle"
fontSize="sm" fontSize="sm"
...@@ -85,6 +70,25 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo ...@@ -85,6 +70,25 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo
{ getHostname(data?.url) } { getHostname(data?.url) }
</chakra.span> </chakra.span>
</LinkExternal> </LinkExternal>
<Skeleton isLoaded={ !isLoading }>
<MarketplaceAppInfo data={ data }/>
</Skeleton>
{ (isExperiment && (securityReport || isLoading)) && (
<AppSecurityReport
id={ data?.id || '' }
securityReport={ securityReport }
showContractList={ setShowContractList.on }
isLoading={ isLoading }
onlyIcon={ isMobile }
source="App page"
/>
) }
{ !isMobile && (
<Flex flex="1" justifyContent="flex-end">
{ config.features.account.isEnabled && <ProfileMenuDesktop boxSize="32px" fallbackIconSize={ 16 }/> }
{ config.features.blockchainInteraction.isEnabled && <WalletMenuDesktop size="sm"/> }
</Flex>
) }
</Flex> </Flex>
{ showContractList && ( { showContractList && (
<ContractListModal <ContractListModal
......
...@@ -8,9 +8,10 @@ import IconSvg from 'ui/shared/IconSvg'; ...@@ -8,9 +8,10 @@ import IconSvg from 'ui/shared/IconSvg';
interface Props { interface Props {
size: number; size: number;
fallbackIconSize?: number;
} }
const UserAvatar = ({ size }: Props) => { const UserAvatar = ({ size, fallbackIconSize = 20 }: Props) => {
const appProps = useAppContext(); const appProps = useAppContext();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies)); const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies));
const [ isImageLoadError, setImageLoadError ] = React.useState(false); const [ isImageLoadError, setImageLoadError ] = React.useState(false);
...@@ -34,7 +35,7 @@ const UserAvatar = ({ size }: Props) => { ...@@ -34,7 +35,7 @@ const UserAvatar = ({ size }: Props) => {
boxSize={ `${ size }px` } boxSize={ `${ size }px` }
borderRadius="full" borderRadius="full"
overflow="hidden" overflow="hidden"
fallback={ isImageLoadError || !data?.avatar ? <IconSvg name="profile" boxSize={ 5 }/> : undefined } fallback={ isImageLoadError || !data?.avatar ? <IconSvg name="profile" boxSize={ `${ fallbackIconSize }px` }/> : undefined }
onError={ handleImageLoadError } onError={ handleImageLoadError }
/> />
); );
......
...@@ -3,7 +3,6 @@ import React from 'react'; ...@@ -3,7 +3,6 @@ import React from 'react';
import type { Props } from './types'; import type { Props } from './types';
import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary'; import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile'; import HeaderMobile from 'ui/snippets/header/HeaderMobile';
import * as Layout from './components'; import * as Layout from './components';
...@@ -15,14 +14,13 @@ const LayoutDefault = ({ children }: Props) => { ...@@ -15,14 +14,13 @@ const LayoutDefault = ({ children }: Props) => {
<HeaderMobile/> <HeaderMobile/>
<Layout.MainArea> <Layout.MainArea>
<Layout.MainColumn <Layout.MainColumn
paddingTop={{ base: 16, lg: 6 }} paddingTop={{ base: 16, lg: 0 }}
paddingBottom={ 0 } paddingBottom={ 0 }
paddingX={{ base: 4, lg: 6 }} paddingX={{ base: 4, lg: 6 }}
height={{ base: 'calc(100vh - 92px)', sm: 'auto' }} // 92px = Layout.TopRow + HeaderMobile height={{ base: 'calc(100vh - 92px)', sm: 'auto' }} // 92px = Layout.TopRow + HeaderMobile
> >
<HeaderDesktop isMarketplaceAppPage/>
<AppErrorBoundary> <AppErrorBoundary>
<Layout.Content pt={{ base: 0, lg: 4 }} flexGrow={ 1 }> <Layout.Content pt={{ base: 0, lg: 2 }} flexGrow={ 1 }>
{ children } { children }
</Layout.Content> </Layout.Content>
</AppErrorBoundary> </AppErrorBoundary>
......
import type { IconButtonProps } from '@chakra-ui/react'; import type { IconButtonProps } from '@chakra-ui/react';
import { Popover, PopoverContent, PopoverBody, PopoverTrigger, IconButton, Tooltip, Box } from '@chakra-ui/react'; import { Popover, PopoverContent, PopoverBody, PopoverTrigger, IconButton, Tooltip, Box, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo'; import useFetchProfileInfo from 'lib/hooks/useFetchProfileInfo';
...@@ -12,9 +12,11 @@ import useMenuButtonColors from '../useMenuButtonColors'; ...@@ -12,9 +12,11 @@ import useMenuButtonColors from '../useMenuButtonColors';
type Props = { type Props = {
isHomePage?: boolean; isHomePage?: boolean;
className?: string;
fallbackIconSize?: number;
}; };
const ProfileMenuDesktop = ({ isHomePage }: Props) => { const ProfileMenuDesktop = ({ isHomePage, className, fallbackIconSize }: Props) => {
const { data, error, isPending } = useFetchProfileInfo(); const { data, error, isPending } = useFetchProfileInfo();
const loginUrl = useLoginUrl(); const loginUrl = useLoginUrl();
const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors(); const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors();
...@@ -81,8 +83,9 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => { ...@@ -81,8 +83,9 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => {
<Box> <Box>
<PopoverTrigger> <PopoverTrigger>
<IconButton <IconButton
className={ className }
aria-label="profile menu" aria-label="profile menu"
icon={ <UserAvatar size={ 20 }/> } icon={ <UserAvatar size={ 20 } fallbackIconSize={ fallbackIconSize }/> }
variant={ variant } variant={ variant }
colorScheme="blue" colorScheme="blue"
boxSize="40px" boxSize="40px"
...@@ -104,4 +107,4 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => { ...@@ -104,4 +107,4 @@ const ProfileMenuDesktop = ({ isHomePage }: Props) => {
); );
}; };
export default ProfileMenuDesktop; export default chakra(ProfileMenuDesktop);
import type { ButtonProps } from '@chakra-ui/react'; import type { ButtonProps } from '@chakra-ui/react';
import { Popover, PopoverContent, PopoverBody, PopoverTrigger, Button, Box, useBoolean } from '@chakra-ui/react'; import { Popover, PopoverContent, PopoverBody, PopoverTrigger, Button, Box, useBoolean, chakra } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -14,9 +14,11 @@ import WalletTooltip from './WalletTooltip'; ...@@ -14,9 +14,11 @@ import WalletTooltip from './WalletTooltip';
type Props = { type Props = {
isHomePage?: boolean; isHomePage?: boolean;
className?: string;
size?: 'sm' | 'md';
}; };
const WalletMenuDesktop = ({ isHomePage }: Props) => { const WalletMenuDesktop = ({ isHomePage, className, size = 'md' }: Props) => {
const { isWalletConnected, address, connect, disconnect, isModalOpening, isModalOpen } = useWallet({ source: 'Header' }); const { isWalletConnected, address, connect, disconnect, isModalOpening, isModalOpen } = useWallet({ source: 'Header' });
const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors(); const { themedBackground, themedBorderColor, themedColor } = useMenuButtonColors();
const [ isPopoverOpen, setIsPopoverOpen ] = useBoolean(false); const [ isPopoverOpen, setIsPopoverOpen ] = useBoolean(false);
...@@ -67,6 +69,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => { ...@@ -67,6 +69,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
<Box ml={ 2 }> <Box ml={ 2 }>
<PopoverTrigger> <PopoverTrigger>
<Button <Button
className={ className }
variant={ variant } variant={ variant }
colorScheme="blue" colorScheme="blue"
flexShrink={ 0 } flexShrink={ 0 }
...@@ -74,6 +77,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => { ...@@ -74,6 +77,7 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
loadingText="Connect wallet" loadingText="Connect wallet"
onClick={ isWalletConnected ? openPopover : connect } onClick={ isWalletConnected ? openPopover : connect }
fontSize="sm" fontSize="sm"
size={ size }
{ ...buttonStyles } { ...buttonStyles }
> >
{ isWalletConnected ? ( { isWalletConnected ? (
...@@ -99,4 +103,4 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => { ...@@ -99,4 +103,4 @@ const WalletMenuDesktop = ({ isHomePage }: Props) => {
); );
}; };
export default WalletMenuDesktop; export default chakra(WalletMenuDesktop);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment