Commit 845f1e73 authored by tom's avatar tom

use string type for category

parent 39f47a6e
export type MarketplaceCategoriesIds = 'all' | 'favorites' | 'defi' | 'exchanges' | 'finance' | 'games' | 'marketplaces' | 'nft' |
'security' | 'social' | 'tools' | 'yieldFarming';
export type MarketplaceCategory = { id: MarketplaceCategoriesIds; name: string }
export type AppItemPreview = { export type AppItemPreview = {
id: string; id: string;
external?: boolean; external?: boolean;
title: string; title: string;
logo: string; logo: string;
shortDescription: string; shortDescription: string;
categories: Array<MarketplaceCategoriesIds>; categories: Array<string>;
url: string; url: string;
} }
...@@ -21,3 +16,8 @@ export type AppItemOverview = AppItemPreview & { ...@@ -21,3 +16,8 @@ export type AppItemOverview = AppItemPreview & {
telegram?: string; telegram?: string;
github?: string; github?: string;
} }
export enum AppCategory {
ALL = 'All apps',
FAVORITES = 'Favorites',
}
...@@ -4,7 +4,7 @@ import { ...@@ -4,7 +4,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { AppItemOverview, MarketplaceCategoriesIds } from 'types/client/apps'; import type { AppItemOverview } from 'types/client/apps';
import linkIcon from 'icons/link.svg'; import linkIcon from 'icons/link.svg';
import ghIcon from 'icons/social/git.svg'; import ghIcon from 'icons/social/git.svg';
...@@ -155,7 +155,7 @@ const AppModal = ({ ...@@ -155,7 +155,7 @@ const AppModal = ({
</Heading> </Heading>
<Box marginBottom={ 2 }> <Box marginBottom={ 2 }>
{ categories.map((category: MarketplaceCategoriesIds) => ( { categories.map((category) => (
<Tag <Tag
colorScheme="blue" colorScheme="blue"
marginRight={ 2 } marginRight={ 2 }
......
import { Box, Button, Icon, Menu, MenuButton, MenuList } from '@chakra-ui/react'; import { Box, Button, Icon, Menu, MenuButton, MenuList } from '@chakra-ui/react';
import React from 'react'; import React from 'react';
import type { MarketplaceCategoriesIds, MarketplaceCategory } from 'types/client/apps'; import { AppCategory } from 'types/client/apps';
import eastMiniArrowIcon from 'icons/arrows/east-mini.svg'; import eastMiniArrowIcon from 'icons/arrows/east-mini.svg';
import CategoriesMenuItem from './CategoriesMenuItem'; import CategoriesMenuItem from './CategoriesMenuItem';
type Props = { type Props = {
categories: Array<MarketplaceCategoriesIds>; categories: Array<string>;
selectedCategoryId: MarketplaceCategoriesIds; selectedCategoryId: string;
onSelect: (category: MarketplaceCategoriesIds) => void; onSelect: (category: string) => void;
} }
const CategoriesMenu = ({ selectedCategoryId, onSelect, categories }: Props) => { const CategoriesMenu = ({ selectedCategoryId, onSelect, categories }: Props) => {
const options = React.useMemo(() => ([ const options = React.useMemo(() => ([
{ id: 'Favorites', name: 'Favorites' }, AppCategory.FAVORITES,
{ id: 'App apps', name: 'App apps' }, AppCategory.ALL,
...categories.map((category) => ({ id: category, name: category })), ...categories,
]), [ categories ]); ]), [ categories ]);
const selectedCategory = options.find(category => category.id === selectedCategoryId);
return ( return (
<Menu> <Menu>
...@@ -37,17 +36,16 @@ const CategoriesMenu = ({ selectedCategoryId, onSelect, categories }: Props) => ...@@ -37,17 +36,16 @@ const CategoriesMenu = ({ selectedCategoryId, onSelect, categories }: Props) =>
display="flex" display="flex"
alignItems="center" alignItems="center"
> >
{ selectedCategory?.name } { selectedCategoryId }
<Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/> <Icon transform="rotate(-90deg)" ml={{ base: 'auto', sm: 1 }} as={ eastMiniArrowIcon } w={ 5 } h={ 5 }/>
</Box> </Box>
</MenuButton> </MenuButton>
<MenuList zIndex={ 3 }> <MenuList zIndex={ 3 }>
{ options.map((category: MarketplaceCategory) => ( { options.map((category: string) => (
<CategoriesMenuItem <CategoriesMenuItem
key={ category.id } key={ category }
id={ category.id } id={ category }
name={ category.name }
onClick={ onSelect } onClick={ onSelect }
/> />
)) } )) }
......
...@@ -2,21 +2,20 @@ import { Icon, MenuItem } from '@chakra-ui/react'; ...@@ -2,21 +2,20 @@ import { Icon, MenuItem } from '@chakra-ui/react';
import type { FunctionComponent, SVGAttributes } from 'react'; import type { FunctionComponent, SVGAttributes } from 'react';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import type { MarketplaceCategoriesIds } from 'types/client/apps'; import { AppCategory } from 'types/client/apps';
import starFilledIcon from 'icons/star_filled.svg'; import starFilledIcon from 'icons/star_filled.svg';
type Props = { type Props = {
id: MarketplaceCategoriesIds; id: string;
name: string; onClick: (category: string) => void;
onClick: (category: MarketplaceCategoriesIds) => void;
} }
const ICONS = { const ICONS: Record<string, FunctionComponent<SVGAttributes<SVGElement>>> = {
favorites: starFilledIcon, [AppCategory.FAVORITES]: starFilledIcon,
} as { [key in MarketplaceCategoriesIds]: FunctionComponent<SVGAttributes<SVGElement>> }; };
const CategoriesMenuItem = ({ id, name, onClick }: Props) => { const CategoriesMenuItem = ({ id, onClick }: Props) => {
const handleSelection = useCallback(() => { const handleSelection = useCallback(() => {
onClick(id); onClick(id);
}, [ id, onClick ]); }, [ id, onClick ]);
...@@ -28,11 +27,8 @@ const CategoriesMenuItem = ({ id, name, onClick }: Props) => { ...@@ -28,11 +27,8 @@ const CategoriesMenuItem = ({ id, name, onClick }: Props) => {
display="flex" display="flex"
alignItems="center" alignItems="center"
> >
{ id in ICONS && ( { id in ICONS && <Icon mr={ 3 } as={ ICONS[id] } w={ 4 } h={ 4 } color="blackAlpha.800"/> }
<Icon mr={ 3 } as={ ICONS[id] } w={ 4 } h={ 4 } color="blackAlpha.800"/> { id }
) }
{ name }
</MenuItem> </MenuItem>
); );
}; };
......
import type { MarketplaceCategoriesIds } from 'types/client/apps';
export const APP_CATEGORIES: {[key in MarketplaceCategoriesIds]: string} = {
favorites: 'Favorites',
all: 'All apps',
defi: 'DeFi',
exchanges: 'Exchanges',
finance: 'Finance',
games: 'Games',
marketplaces: 'Marketplaces',
nft: 'NFT',
security: 'Security',
social: 'Social',
tools: 'Tools',
yieldFarming: 'Yield farming',
};
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import _unique from 'lodash/uniq';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import type { AppItemOverview, MarketplaceCategoriesIds } from 'types/client/apps'; import type { AppItemOverview } from 'types/client/apps';
import { AppCategory } from 'types/client/apps';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import type { ResourceError } from 'lib/api/resources'; import type { ResourceError } from 'lib/api/resources';
...@@ -22,15 +24,15 @@ function isAppNameMatches(q: string, app: AppItemOverview) { ...@@ -22,15 +24,15 @@ function isAppNameMatches(q: string, app: AppItemOverview) {
return app.title.toLowerCase().includes(q.toLowerCase()); return app.title.toLowerCase().includes(q.toLowerCase());
} }
function isAppCategoryMatches(category: MarketplaceCategoriesIds, app: AppItemOverview, favoriteApps: Array<string>) { function isAppCategoryMatches(category: string, app: AppItemOverview, favoriteApps: Array<string>) {
return category === 'all' || return category === AppCategory.ALL ||
(category === 'favorites' && favoriteApps.includes(app.id)) || (category === AppCategory.FAVORITES && favoriteApps.includes(app.id)) ||
app.categories.includes(category); app.categories.includes(category);
} }
export default function useMarketplaceApps() { export default function useMarketplaceApps() {
const [ selectedAppId, setSelectedAppId ] = useState<string | null>(null); const [ selectedAppId, setSelectedAppId ] = useState<string | null>(null);
const [ selectedCategoryId, setSelectedCategoryId ] = useState<MarketplaceCategoriesIds>('all'); const [ selectedCategoryId, setSelectedCategoryId ] = useState<string>(AppCategory.ALL);
const [ filterQuery, setFilterQuery ] = useState(''); const [ filterQuery, setFilterQuery ] = useState('');
const [ favoriteApps, setFavoriteApps ] = useState<Array<string>>([]); const [ favoriteApps, setFavoriteApps ] = useState<Array<string>>([]);
...@@ -63,7 +65,7 @@ export default function useMarketplaceApps() { ...@@ -63,7 +65,7 @@ export default function useMarketplaceApps() {
const debouncedFilterQuery = useDebounce(filterQuery, 500); const debouncedFilterQuery = useDebounce(filterQuery, 500);
const clearSelectedAppId = useCallback(() => setSelectedAppId(null), []); const clearSelectedAppId = useCallback(() => setSelectedAppId(null), []);
const handleCategoryChange = useCallback((newCategory: MarketplaceCategoriesIds) => { const handleCategoryChange = useCallback((newCategory: string) => {
setSelectedCategoryId(newCategory); setSelectedCategoryId(newCategory);
}, []); }, []);
...@@ -72,7 +74,7 @@ export default function useMarketplaceApps() { ...@@ -72,7 +74,7 @@ export default function useMarketplaceApps() {
}, [ selectedCategoryId, data, debouncedFilterQuery, favoriteApps ]); }, [ selectedCategoryId, data, debouncedFilterQuery, favoriteApps ]);
const categories = React.useMemo(() => { const categories = React.useMemo(() => {
return data?.map(app => app.categories).flat() || []; return _unique(data?.map(app => app.categories).flat()) || [];
}, [ data ]); }, [ data ]);
useEffect(() => { useEffect(() => {
......
...@@ -27,7 +27,7 @@ const MarketplaceApp = () => { ...@@ -27,7 +27,7 @@ const MarketplaceApp = () => {
const id = getQueryParamString(router.query.id); const id = getQueryParamString(router.query.id);
const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, AppItemOverview>( const { isLoading, isError, error, data } = useQuery<unknown, ResourceError<unknown>, AppItemOverview>(
[ 'marketplace-apps' ], [ 'marketplace-apps', id ],
async() => { async() => {
const result = await apiFetch<Array<AppItemOverview>, unknown>(appConfig.marketplaceConfigUrl || ''); const result = await apiFetch<Array<AppItemOverview>, unknown>(appConfig.marketplaceConfigUrl || '');
if (!Array.isArray(result)) { if (!Array.isArray(result)) {
......
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