Skip to content

Commit

Permalink
Merge pull request blockscout#1822 from blockscout/rework-marketplace…
Browse files Browse the repository at this point in the history
…-app-header

Rework marketplace app header and tooltips
  • Loading branch information
maxaleks authored Apr 17, 2024
2 parents 528ee6f + 7828a98 commit d1f7e16
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 165 deletions.
48 changes: 48 additions & 0 deletions lib/contexts/marketplace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { useRouter } from 'next/router';
import React, { createContext, useContext, useEffect, useState, useMemo } from 'react';

type Props = {
children: React.ReactNode;
}

type TMarketplaceContext = {
isAutoConnectDisabled: boolean;
setIsAutoConnectDisabled: (isAutoConnectDisabled: boolean) => void;
}

const MarketplaceContext = createContext<TMarketplaceContext>({
isAutoConnectDisabled: false,
setIsAutoConnectDisabled: () => {},
});

export function MarketplaceContextProvider({ children }: Props) {
const router = useRouter();
const [ isAutoConnectDisabled, setIsAutoConnectDisabled ] = useState(false);

useEffect(() => {
const handleRouteChange = () => {
setIsAutoConnectDisabled(false);
};

router.events.on('routeChangeStart', handleRouteChange);

return () => {
router.events.off('routeChangeStart', handleRouteChange);
};
}, [ router.events ]);

const value = useMemo(() => ({
isAutoConnectDisabled,
setIsAutoConnectDisabled,
}), [ isAutoConnectDisabled, setIsAutoConnectDisabled ]);

return (
<MarketplaceContext.Provider value={ value }>
{ children }
</MarketplaceContext.Provider>
);
}

export function useMarketplaceContext() {
return useContext(MarketplaceContext);
}
5 changes: 4 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import config from 'configs/app';
import useQueryClientConfig from 'lib/api/useQueryClientConfig';
import { AppContextProvider } from 'lib/contexts/app';
import { ChakraProvider } from 'lib/contexts/chakra';
import { MarketplaceContextProvider } from 'lib/contexts/marketplace';
import { ScrollDirectionProvider } from 'lib/contexts/scrollDirection';
import { growthBook } from 'lib/growthbook/init';
import useLoadFeatures from 'lib/growthbook/useLoadFeatures';
Expand Down Expand Up @@ -67,7 +68,9 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) {
<GrowthBookProvider growthbook={ growthBook }>
<ScrollDirectionProvider>
<SocketProvider url={ `${ config.api.socket }${ config.api.basePath }/socket/v2` }>
{ getLayout(<Component { ...pageProps }/>) }
<MarketplaceContextProvider>
{ getLayout(<Component { ...pageProps }/>) }
</MarketplaceContextProvider>
</SocketProvider>
</ScrollDirectionProvider>
</GrowthBookProvider>
Expand Down
1 change: 1 addition & 0 deletions ui/marketplace/AppSecurityReport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const AppSecurityReport = ({ id, securityReport, height, showContractList, isLoa
onClick={ handleButtonClick }
height={ height }
onlyIcon={ onlyIcon }
label="The security score is based on analysis of a DApp's smart contracts."
/>
</PopoverTrigger>
<PopoverContent w={{ base: '100vw', lg: '328px' }}>
Expand Down
51 changes: 0 additions & 51 deletions ui/marketplace/MarketplaceAppAlert.tsx

This file was deleted.

55 changes: 29 additions & 26 deletions ui/marketplace/MarketplaceAppTopBar.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
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 type { MarketplaceAppOverview, MarketplaceAppSecurityReport } from 'types/client/marketplace';
import { ContractListTypes } from 'types/client/marketplace';

import { route } from 'nextjs-routes';

import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useIsMobile from 'lib/hooks/useIsMobile';
import IconSvg from 'ui/shared/IconSvg';
import LinkExternal from 'ui/shared/LinkExternal';
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 ContractListModal from './ContractListModal';
import MarketplaceAppAlert from './MarketplaceAppAlert';
import MarketplaceAppInfo from './MarketplaceAppInfo';

type Props = {
data: MarketplaceAppOverview | undefined;
isLoading: boolean;
isWalletConnected: boolean;
securityReport?: MarketplaceAppSecurityReport;
}

const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityReport }: Props) => {
const MarketplaceAppTopBar = ({ data, isLoading, securityReport }: Props) => {
const [ showContractList, setShowContractList ] = useBoolean(false);
const appProps = useAppContext();
const isMobile = useIsMobile();
Expand All @@ -46,32 +48,14 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo

return (
<>
<Flex alignItems="center" flexWrap="wrap" mb={{ base: 6, md: 2 }} rowGap={ 3 } columnGap={ 2 }>
<Tooltip label="Back to dApps list" order={ 1 }>
<LinkInternal display="inline-flex" href={ goBackUrl } h="32px" isLoading={ isLoading }>
<Flex alignItems="center" flexWrap="wrap" mb={{ base: 3, md: 2 }} rowGap={ 3 } columnGap={ 2 }>
{ !isMobile && <NetworkLogo isCollapsed/> }
<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"/>
</LinkInternal>
</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
order={{ base: 4, md: 5 }}
href={ data?.url }
variant="subtle"
fontSize="sm"
Expand All @@ -85,6 +69,25 @@ const MarketplaceAppTopBar = ({ data, isLoading, isWalletConnected, securityRepo
{ getHostname(data?.url) }
</chakra.span>
</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>
{ showContractList && (
<ContractListModal
Expand Down
6 changes: 4 additions & 2 deletions ui/pages/MarketplaceApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { route } from 'nextjs-routes';
import config from 'configs/app';
import type { ResourceError } from 'lib/api/resources';
import useApiFetch from 'lib/api/useApiFetch';
import { useMarketplaceContext } from 'lib/contexts/marketplace';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import useFetch from 'lib/hooks/useFetch';
import * as metadata from 'lib/metadata';
Expand Down Expand Up @@ -129,15 +130,17 @@ const MarketplaceApp = () => {
enabled: feature.isEnabled,
});
const { data, isPending } = query;
const { setIsAutoConnectDisabled } = useMarketplaceContext();

useEffect(() => {
if (data) {
metadata.update(
{ pathname: '/apps/[id]', query: { id: data.id } },
{ app_name: data.title },
);
setIsAutoConnectDisabled(!data.internalWallet);
}
}, [ data ]);
}, [ data, setIsAutoConnectDisabled ]);

throwOnResourceLoadError(query);

Expand All @@ -146,7 +149,6 @@ const MarketplaceApp = () => {
<MarketplaceAppTopBar
data={ data }
isLoading={ isPending || isSecurityReportsLoading }
isWalletConnected={ Boolean(address) }
securityReport={ securityReports?.[id] }
/>
<DappscoutIframeProvider
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions ui/shared/UserAvatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import IconSvg from 'ui/shared/IconSvg';

interface Props {
size: number;
fallbackIconSize?: number;
}

const UserAvatar = ({ size }: Props) => {
const UserAvatar = ({ size, fallbackIconSize = 20 }: Props) => {
const appProps = useAppContext();
const hasAuth = Boolean(cookies.get(cookies.NAMES.API_TOKEN, appProps.cookies));
const [ isImageLoadError, setImageLoadError ] = React.useState(false);
Expand All @@ -34,7 +35,7 @@ const UserAvatar = ({ size }: Props) => {
boxSize={ `${ size }px` }
borderRadius="full"
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 }
/>
);
Expand Down
18 changes: 10 additions & 8 deletions ui/shared/layout/LayoutApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@ import React from 'react';
import type { Props } from './types';

import AppErrorBoundary from 'ui/shared/AppError/AppErrorBoundary';
import HeaderDesktop from 'ui/snippets/header/HeaderDesktop';
import HeaderMobile from 'ui/snippets/header/HeaderMobile';

import * as Layout from './components';

const LayoutDefault = ({ children }: Props) => {
return (
<Layout.Container overflowY="hidden" height="100vh">
<Layout.Container
overflowY="hidden"
height={ [ '-webkit-fill-available', '100vh' ] }
display="flex"
flexDirection="column"
>
<Layout.TopRow/>
<HeaderMobile/>
<Layout.MainArea>
<HeaderMobile hideSearchBar/>
<Layout.MainArea minH="auto" flex={ 1 }>
<Layout.MainColumn
paddingTop={{ base: 16, lg: 6 }}
paddingTop={{ base: 0, lg: 0 }}
paddingBottom={ 0 }
paddingX={{ base: 4, lg: 6 }}
height={{ base: 'calc(100vh - 92px)', sm: 'auto' }} // 92px = Layout.TopRow + HeaderMobile
>
<HeaderDesktop isMarketplaceAppPage/>
<AppErrorBoundary>
<Layout.Content pt={{ base: 0, lg: 4 }} flexGrow={ 1 }>
<Layout.Content pt={{ base: 0, lg: 2 }} flexGrow={ 1 }>
{ children }
</Layout.Content>
</AppErrorBoundary>
Expand Down
2 changes: 1 addition & 1 deletion ui/shared/layout/LayoutHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const LayoutHome = ({ children }: Props) => {
return (
<Layout.Container>
<Layout.TopRow/>
<HeaderMobile isHomePage/>
<HeaderMobile hideSearchBar/>
<Layout.MainArea>
<Layout.SideBar/>
<Layout.MainColumn
Expand Down
9 changes: 5 additions & 4 deletions ui/shared/layout/components/MainArea.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { Flex } from '@chakra-ui/react';
import { Flex, chakra } from '@chakra-ui/react';
import React from 'react';

interface Props {
children: React.ReactNode;
className?: string;
}

const MainArea = ({ children }: Props) => {
const MainArea = ({ children, className }: Props) => {
return (
<Flex w="100%" minH="calc(100vh - 36px)" alignItems="stretch">
<Flex className={ className } w="100%" minH="calc(100vh - 36px)" alignItems="stretch">
{ children }
</Flex>
);
};

export default React.memo(MainArea);
export default React.memo(chakra(MainArea));
5 changes: 3 additions & 2 deletions ui/shared/solidityscanReport/SolidityscanReportButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@ interface Props {
height?: string;
onlyIcon?: boolean;
onClick?: () => void;
label?: string;
}

const SolidityscanReportButton = (
{ className, score, isLoading, height = '32px', onlyIcon, onClick }: Props,
{ className, score, isLoading, height = '32px', onlyIcon, onClick, label = 'Security score' }: Props,
ref: React.ForwardedRef<HTMLButtonElement>,
) => {
const { scoreColor } = useScoreLevelAndColor(score);

return (
<PopoverTriggerTooltip label="Security score" isLoading={ isLoading } className={ className }>
<PopoverTriggerTooltip label={ label } isLoading={ isLoading } className={ className }>
<Button
ref={ ref }
color={ scoreColor }
Expand Down
6 changes: 3 additions & 3 deletions ui/snippets/header/HeaderMobile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ const LOGO_IMAGE_PROPS = {
};

type Props = {
isHomePage?: boolean;
hideSearchBar?: boolean;
renderSearchBar?: () => React.ReactNode;
}

const HeaderMobile = ({ isHomePage, renderSearchBar }: Props) => {
const HeaderMobile = ({ hideSearchBar, renderSearchBar }: Props) => {
const bgColor = useColorModeValue('white', 'black');
const scrollDirection = useScrollDirection();
const { ref, inView } = useInView({ threshold: 1 });
Expand Down Expand Up @@ -57,7 +57,7 @@ const HeaderMobile = ({ isHomePage, renderSearchBar }: Props) => {
{ config.features.blockchainInteraction.isEnabled && <WalletMenuMobile/> }
</Flex>
</Flex>
{ !isHomePage && searchBar }
{ !hideSearchBar && searchBar }
</Box>
);
};
Expand Down
Loading

0 comments on commit d1f7e16

Please sign in to comment.