Commit 6ecb1e28 authored by Max Alekseenko's avatar Max Alekseenko

rework view switch to use url param and add skeleton

parent 35f732fb
...@@ -15,7 +15,6 @@ export enum NAMES { ...@@ -15,7 +15,6 @@ export enum NAMES {
MIXPANEL_DEBUG='_mixpanel_debug', MIXPANEL_DEBUG='_mixpanel_debug',
ADDRESS_NFT_DISPLAY_TYPE='address_nft_display_type', ADDRESS_NFT_DISPLAY_TYPE='address_nft_display_type',
UUID='uuid', UUID='uuid',
MARKETPLACE_DISPLAY_TYPE='marketplace_display_type',
} }
export function get(name?: NAMES | undefined | null, serverCookie?: string) { export function get(name?: NAMES | undefined | null, serverCookie?: string) {
......
...@@ -38,3 +38,8 @@ export enum ContractListTypes { ...@@ -38,3 +38,8 @@ export enum ContractListTypes {
ALL = 'All', ALL = 'All',
VERIFIED = 'Verified', VERIFIED = 'Verified',
} }
export enum MarketplaceDisplayType {
DEFAULT = 'default',
SCORES = 'scores',
}
...@@ -3,7 +3,7 @@ import { useRouter } from 'next/router'; ...@@ -3,7 +3,7 @@ import { useRouter } from 'next/router';
import React from 'react'; import React from 'react';
import type { ContractListTypes } from 'types/client/marketplace'; import type { ContractListTypes } from 'types/client/marketplace';
import { MarketplaceCategory } from 'types/client/marketplace'; import { MarketplaceCategory, MarketplaceDisplayType } from 'types/client/marketplace';
import useDebounce from 'lib/hooks/useDebounce'; import useDebounce from 'lib/hooks/useDebounce';
import * as mixpanel from 'lib/mixpanel/index'; import * as mixpanel from 'lib/mixpanel/index';
...@@ -26,9 +26,15 @@ export default function useMarketplace() { ...@@ -26,9 +26,15 @@ export default function useMarketplace() {
const router = useRouter(); const router = useRouter();
const defaultCategoryId = getQueryParamString(router.query.category); const defaultCategoryId = getQueryParamString(router.query.category);
const defaultFilterQuery = getQueryParamString(router.query.filter); const defaultFilterQuery = getQueryParamString(router.query.filter);
const defaultDisplayType = getQueryParamString(router.query.view);
const [ selectedAppId, setSelectedAppId ] = React.useState<string | null>(null); const [ selectedAppId, setSelectedAppId ] = React.useState<string | null>(null);
const [ selectedCategoryId, setSelectedCategoryId ] = React.useState<string>(MarketplaceCategory.ALL); const [ selectedCategoryId, setSelectedCategoryId ] = React.useState<string>(MarketplaceCategory.ALL);
const [ selectedDisplayType, setSelectedDisplayType ] = React.useState<string>(
Object.values(MarketplaceDisplayType).includes(defaultDisplayType as MarketplaceDisplayType) ?
defaultDisplayType :
MarketplaceDisplayType.DEFAULT,
);
const [ filterQuery, setFilterQuery ] = React.useState(defaultFilterQuery); const [ filterQuery, setFilterQuery ] = React.useState(defaultFilterQuery);
const [ favoriteApps, setFavoriteApps ] = React.useState<Array<string>>([]); const [ favoriteApps, setFavoriteApps ] = React.useState<Array<string>>([]);
const [ isFavoriteAppsLoaded, setIsFavoriteAppsLoaded ] = React.useState<boolean>(false); const [ isFavoriteAppsLoaded, setIsFavoriteAppsLoaded ] = React.useState<boolean>(false);
...@@ -80,6 +86,10 @@ export default function useMarketplace() { ...@@ -80,6 +86,10 @@ export default function useMarketplace() {
setSelectedCategoryId(newCategory); setSelectedCategoryId(newCategory);
}, []); }, []);
const handleDisplayTypeChange = React.useCallback((newView: MarketplaceDisplayType) => {
setSelectedDisplayType(newView);
}, []);
const { const {
isPlaceholderData, isError, error, data, displayedApps, isPlaceholderData, isError, error, data, displayedApps,
} = useMarketplaceApps(debouncedFilterQuery, selectedCategoryId, favoriteApps, isFavoriteAppsLoaded); } = useMarketplaceApps(debouncedFilterQuery, selectedCategoryId, favoriteApps, isFavoriteAppsLoaded);
...@@ -101,10 +111,16 @@ export default function useMarketplace() { ...@@ -101,10 +111,16 @@ export default function useMarketplace() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ isPlaceholderData ]); }, [ isPlaceholderData ]);
// React.useEffect(() => {
// const isValidDefaultDisplayType = Object.values(MarketplaceDisplayType).includes(defaultDisplayType as MarketplaceDisplayType);
// isValidDefaultDisplayType && setSelectedDisplayType(defaultDisplayType);
// }, [ ]); // eslint-disable-line react-hooks/exhaustive-deps
React.useEffect(() => { React.useEffect(() => {
const query = _pickBy({ const query = _pickBy({
category: selectedCategoryId === MarketplaceCategory.ALL ? undefined : selectedCategoryId, category: selectedCategoryId === MarketplaceCategory.ALL ? undefined : selectedCategoryId,
filter: debouncedFilterQuery, filter: debouncedFilterQuery,
view: selectedDisplayType === MarketplaceDisplayType.DEFAULT ? undefined : selectedDisplayType,
}, Boolean); }, Boolean);
if (debouncedFilterQuery.length > 0) { if (debouncedFilterQuery.length > 0) {
...@@ -119,7 +135,7 @@ export default function useMarketplace() { ...@@ -119,7 +135,7 @@ export default function useMarketplace() {
// omit router in the deps because router.push() somehow modifies it // omit router in the deps because router.push() somehow modifies it
// and we get infinite re-renders then // and we get infinite re-renders then
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ debouncedFilterQuery, selectedCategoryId ]); }, [ debouncedFilterQuery, selectedCategoryId, selectedDisplayType ]);
return React.useMemo(() => ({ return React.useMemo(() => ({
selectedCategoryId, selectedCategoryId,
...@@ -143,6 +159,8 @@ export default function useMarketplace() { ...@@ -143,6 +159,8 @@ export default function useMarketplace() {
isCategoriesPlaceholderData, isCategoriesPlaceholderData,
showContractList, showContractList,
contractListModalType, contractListModalType,
selectedDisplayType,
onDisplayTypeChange: handleDisplayTypeChange,
}), [ }), [
selectedCategoryId, selectedCategoryId,
categories, categories,
...@@ -164,5 +182,7 @@ export default function useMarketplace() { ...@@ -164,5 +182,7 @@ export default function useMarketplace() {
isCategoriesPlaceholderData, isCategoriesPlaceholderData,
showContractList, showContractList,
contractListModalType, contractListModalType,
selectedDisplayType,
handleDisplayTypeChange,
]); ]);
} }
import { Box, Menu, MenuButton, MenuItem, MenuList, Flex, IconButton } from '@chakra-ui/react'; import { Box, Menu, MenuButton, MenuItem, MenuList, Flex, IconButton, Skeleton } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import { MarketplaceCategory } from 'types/client/marketplace'; import { MarketplaceCategory, MarketplaceDisplayType } from 'types/client/marketplace';
import type { TabItem } from 'ui/shared/Tabs/types'; import type { TabItem } from 'ui/shared/Tabs/types';
import config from 'configs/app'; import config from 'configs/app';
import { useAppContext } from 'lib/contexts/app';
import * as cookies from 'lib/cookies';
import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError'; import throwOnResourceLoadError from 'lib/errors/throwOnResourceLoadError';
import useFeatureValue from 'lib/growthbook/useFeatureValue'; import useFeatureValue from 'lib/growthbook/useFeatureValue';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
...@@ -46,8 +44,6 @@ if (feature.isEnabled) { ...@@ -46,8 +44,6 @@ if (feature.isEnabled) {
} }
} }
type MarketplaceDisplayType = 'default' | 'scores';
const Marketplace = () => { const Marketplace = () => {
const { const {
isPlaceholderData, isPlaceholderData,
...@@ -71,10 +67,12 @@ const Marketplace = () => { ...@@ -71,10 +67,12 @@ const Marketplace = () => {
isCategoriesPlaceholderData, isCategoriesPlaceholderData,
showContractList, showContractList,
contractListModalType, contractListModalType,
selectedDisplayType,
onDisplayTypeChange,
} = useMarketplace(); } = useMarketplace();
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const { value: isExperiment } = useFeatureValue('security_score_exp', false); const { value: isExperiment } = useFeatureValue('security_score_exp', true);
const categoryTabs = React.useMemo(() => { const categoryTabs = React.useMemo(() => {
const tabs: Array<TabItem> = categories.map(category => ({ const tabs: Array<TabItem> = categories.map(category => ({
...@@ -110,14 +108,6 @@ const Marketplace = () => { ...@@ -110,14 +108,6 @@ const Marketplace = () => {
onCategoryChange(categoryTabs[index].id); onCategoryChange(categoryTabs[index].id);
}, [ categoryTabs, onCategoryChange ]); }, [ categoryTabs, onCategoryChange ]);
const displayTypeCookie = cookies.get(cookies.NAMES.MARKETPLACE_DISPLAY_TYPE, useAppContext().cookies);
const [ displayType, setDisplayType ] = React.useState<MarketplaceDisplayType>(displayTypeCookie === 'default' ? 'default' : 'scores');
const handleNFTsDisplayTypeChange = React.useCallback((val: MarketplaceDisplayType) => {
cookies.set(cookies.NAMES.MARKETPLACE_DISPLAY_TYPE, val);
setDisplayType(val);
}, []);
const handleAppClick = React.useCallback((event: MouseEvent, id: string) => { const handleAppClick = React.useCallback((event: MouseEvent, id: string) => {
const isShown = window.localStorage.getItem('marketplace-disclaimer-shown'); const isShown = window.localStorage.getItem('marketplace-disclaimer-shown');
if (!isShown) { if (!isShown) {
...@@ -184,41 +174,43 @@ const Marketplace = () => { ...@@ -184,41 +174,43 @@ const Marketplace = () => {
<Flex direction={{ base: 'column', lg: 'row' }} mb={{ base: 4, lg: 6 }} gap={{ base: 4, lg: 3 }}> <Flex direction={{ base: 'column', lg: 'row' }} mb={{ base: 4, lg: 6 }} gap={{ base: 4, lg: 3 }}>
{ isExperiment && ( { isExperiment && (
<RadioButtonGroup<MarketplaceDisplayType> <Skeleton isLoaded={ !isPlaceholderData }>
onChange={ handleNFTsDisplayTypeChange } <RadioButtonGroup<MarketplaceDisplayType>
defaultValue={ displayType } onChange={ onDisplayTypeChange }
name="type" defaultValue={ selectedDisplayType }
options={ [ name="type"
{ options={ [
title: 'Discovery', {
value: 'default', title: 'Discovery',
icon: 'apps_xs', value: MarketplaceDisplayType.DEFAULT,
onlyIcon: false, icon: 'apps_xs',
}, onlyIcon: false,
{ },
title: 'Apps scores', {
value: 'scores', title: 'Apps scores',
icon: 'apps_list', value: MarketplaceDisplayType.SCORES,
onlyIcon: false, icon: 'apps_list',
contentAfter: ( onlyIcon: false,
<Flex contentAfter: (
alignItems="center" <Flex
h={ 3 } alignItems="center"
bg="red.400" h={ 3 }
borderRadius="2px" bg="red.400"
fontSize="10px" borderRadius="2px"
fontWeight="500" fontSize="10px"
color="white" fontWeight="500"
px="2px" color="white"
ml={ 1 } px="2px"
> ml={ 1 }
{ isMobile ? <IconSvg name="beta" boxSize={ 2 }/> : 'beta' } >
</Flex> { isMobile ? <IconSvg name="beta" boxSize={ 2 }/> : 'beta' }
), </Flex>
}, ),
] } },
autoWidth ] }
/> autoWidth
/>
</Skeleton>
) } ) }
<FilterInput <FilterInput
initialValue={ filterQuery } initialValue={ filterQuery }
...@@ -230,7 +222,7 @@ const Marketplace = () => { ...@@ -230,7 +222,7 @@ const Marketplace = () => {
/> />
</Flex> </Flex>
{ (displayType === 'scores' && isExperiment) ? ( { (selectedDisplayType === MarketplaceDisplayType.SCORES && isExperiment) ? (
<MarketplaceListWithScores <MarketplaceListWithScores
apps={ displayedApps } apps={ displayedApps }
showAppInfo={ showAppInfo } showAppInfo={ showAppInfo }
......
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