Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
af982cd9
Commit
af982cd9
authored
Oct 06, 2022
by
Yuri Mikhin
Committed by
Yuri Mikhin
Oct 06, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add marketplace categories.
parent
1b704fec
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
257 additions
and
82 deletions
+257
-82
apps.ts
types/client/apps.ts
+8
-2
AppList.tsx
ui/apps/AppList.tsx
+7
-33
AppListSkeleton.tsx
ui/apps/AppListSkeleton.tsx
+3
-1
AppModal.tsx
ui/apps/AppModal.tsx
+2
-2
CategoriesMenu.tsx
ui/apps/CategoriesMenu.tsx
+59
-0
CategoriesMenuItem.tsx
ui/apps/CategoriesMenuItem.tsx
+27
-0
constants.ts
ui/apps/constants.ts
+4
-2
useMarkeplaceApps.tsx
ui/apps/useMarkeplaceApps.tsx
+117
-0
Apps.tsx
ui/pages/Apps.tsx
+30
-42
No files found.
types/client/apps.ts
View file @
af982cd9
export
enum
MarketplaceCategoryNames
{
export
enum
MarketplaceCategoryId
{
'
all
'
,
'
favorites
'
,
'
defi
'
,
'
defi
'
,
'
exchanges
'
,
'
exchanges
'
,
'
finance
'
,
'
finance
'
,
...
@@ -11,12 +13,16 @@ export enum MarketplaceCategoryNames {
...
@@ -11,12 +13,16 @@ export enum MarketplaceCategoryNames {
'
yieldFarming
'
,
'
yieldFarming
'
,
}
}
export
type
MarketplaceCategoriesIds
=
keyof
typeof
MarketplaceCategoryId
;
export
type
MarketplaceCategory
=
{
id
:
MarketplaceCategoriesIds
;
name
:
string
}
export
type
AppItemPreview
=
{
export
type
AppItemPreview
=
{
id
:
string
;
id
:
string
;
title
:
string
;
title
:
string
;
logo
:
string
;
logo
:
string
;
shortDescription
:
string
;
shortDescription
:
string
;
categories
:
Array
<
keyof
typeof
MarketplaceCategoryName
s
>
;
categories
:
Array
<
MarketplaceCategoriesId
s
>
;
}
}
export
type
AppItemOverview
=
AppItemPreview
&
{
export
type
AppItemOverview
=
AppItemPreview
&
{
...
...
ui/apps/AppList.tsx
View file @
af982cd9
import
{
Grid
,
GridItem
,
VisuallyHidden
,
Heading
}
from
'
@chakra-ui/react
'
;
import
{
Grid
,
GridItem
,
Heading
,
VisuallyHidden
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
;
import
React
from
'
react
'
;
import
type
{
AppItemPreview
}
from
'
types/client/apps
'
;
import
type
{
AppItemPreview
}
from
'
types/client/apps
'
;
...
@@ -14,37 +14,11 @@ type Props = {
...
@@ -14,37 +14,11 @@ type Props = {
onAppClick
:
(
id
:
string
)
=>
void
;
onAppClick
:
(
id
:
string
)
=>
void
;
displayedAppId
:
string
|
null
;
displayedAppId
:
string
|
null
;
onModalClose
:
()
=>
void
;
onModalClose
:
()
=>
void
;
favoriteApps
:
Array
<
string
>
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
)
=>
void
;
}
}
function
getFavoriteApps
()
{
const
AppList
=
({
apps
,
onAppClick
,
displayedAppId
,
onModalClose
,
favoriteApps
,
onFavoriteClick
}:
Props
)
=>
{
try
{
return
JSON
.
parse
(
localStorage
.
getItem
(
'
favoriteApps
'
)
||
'
[]
'
);
}
catch
(
e
)
{
return
[];
}
}
const
AppList
=
({
apps
,
onAppClick
,
displayedAppId
,
onModalClose
}:
Props
)
=>
{
const
[
favoriteApps
,
setFavoriteApps
]
=
useState
<
Array
<
string
>>
([]);
const
handleFavoriteClick
=
useCallback
((
id
:
string
,
isFavorite
:
boolean
)
=>
{
const
favoriteApps
=
getFavoriteApps
();
if
(
isFavorite
)
{
const
result
=
favoriteApps
.
filter
((
appId
:
string
)
=>
appId
!==
id
);
setFavoriteApps
(
result
);
localStorage
.
setItem
(
'
favoriteApps
'
,
JSON
.
stringify
(
result
));
}
else
{
favoriteApps
.
push
(
id
);
localStorage
.
setItem
(
'
favoriteApps
'
,
JSON
.
stringify
(
favoriteApps
));
setFavoriteApps
(
favoriteApps
);
}
},
[
]);
useEffect
(()
=>
{
setFavoriteApps
(
getFavoriteApps
());
},
[
]);
return
(
return
(
<>
<>
<
VisuallyHidden
>
<
VisuallyHidden
>
...
@@ -72,7 +46,7 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose }: Props) => {
...
@@ -72,7 +46,7 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose }: Props) => {
shortDescription=
{
app
.
shortDescription
}
shortDescription=
{
app
.
shortDescription
}
categories=
{
app
.
categories
}
categories=
{
app
.
categories
}
isFavorite=
{
favoriteApps
.
includes
(
app
.
id
)
}
isFavorite=
{
favoriteApps
.
includes
(
app
.
id
)
}
onFavoriteClick=
{
handle
FavoriteClick
}
onFavoriteClick=
{
on
FavoriteClick
}
/>
/>
</
GridItem
>
</
GridItem
>
))
}
))
}
...
@@ -86,7 +60,7 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose }: Props) => {
...
@@ -86,7 +60,7 @@ const AppList = ({ apps, onAppClick, displayedAppId, onModalClose }: Props) => {
id=
{
displayedAppId
}
id=
{
displayedAppId
}
onClose=
{
onModalClose
}
onClose=
{
onModalClose
}
isFavorite=
{
favoriteApps
.
includes
(
displayedAppId
)
}
isFavorite=
{
favoriteApps
.
includes
(
displayedAppId
)
}
onFavoriteClick=
{
handle
FavoriteClick
}
onFavoriteClick=
{
on
FavoriteClick
}
/>
/>
)
}
)
}
</>
</>
...
...
ui/apps/AppListSkeleton.tsx
View file @
af982cd9
...
@@ -5,7 +5,7 @@ import { AppCardSkeleton } from 'ui/apps/AppCardSkeleton';
...
@@ -5,7 +5,7 @@ import { AppCardSkeleton } from 'ui/apps/AppCardSkeleton';
const
applicationStubs
=
[
...
Array
(
12
)
];
const
applicationStubs
=
[
...
Array
(
12
)
];
export
const
AppListSkeleton
=
()
=>
{
const
AppListSkeleton
=
()
=>
{
return
(
return
(
<
Grid
<
Grid
templateColumns=
{
{
templateColumns=
{
{
...
@@ -25,3 +25,5 @@ export const AppListSkeleton = () => {
...
@@ -25,3 +25,5 @@ export const AppListSkeleton = () => {
</
Grid
>
</
Grid
>
);
);
};
};
export
default
AppListSkeleton
;
ui/apps/AppModal.tsx
View file @
af982cd9
...
@@ -5,7 +5,7 @@ import {
...
@@ -5,7 +5,7 @@ import {
import
NextLink
from
'
next/link
'
;
import
NextLink
from
'
next/link
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
AppItemOverview
,
MarketplaceCategor
yName
s
}
from
'
types/client/apps
'
;
import
type
{
AppItemOverview
,
MarketplaceCategor
iesId
s
}
from
'
types/client/apps
'
;
import
marketplaceApps
from
'
data/marketplaceApps.json
'
;
import
marketplaceApps
from
'
data/marketplaceApps.json
'
;
import
linkIcon
from
'
icons/link.svg
'
;
import
linkIcon
from
'
icons/link.svg
'
;
...
@@ -161,7 +161,7 @@ const AppModal = ({
...
@@ -161,7 +161,7 @@ const AppModal = ({
</
Heading
>
</
Heading
>
<
Box
marginBottom=
{
2
}
>
<
Box
marginBottom=
{
2
}
>
{
categories
.
map
((
category
:
keyof
typeof
MarketplaceCategoryName
s
)
=>
APP_CATEGORIES
[
category
]
&&
(
{
categories
.
map
((
category
:
MarketplaceCategoriesId
s
)
=>
APP_CATEGORIES
[
category
]
&&
(
<
Tag
<
Tag
colorScheme=
"blue"
colorScheme=
"blue"
marginRight=
{
2
}
marginRight=
{
2
}
...
...
ui/apps/CategoriesMenu.tsx
0 → 100644
View file @
af982cd9
import
{
Box
,
Button
,
Icon
,
Menu
,
MenuButton
,
MenuList
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
type
{
MarketplaceCategoriesIds
,
MarketplaceCategory
}
from
'
types/client/apps
'
;
import
eastMiniArrowIcon
from
'
icons/arrows/east-mini.svg
'
;
import
CategoriesMenuItem
from
'
./CategoriesMenuItem
'
;
import
{
APP_CATEGORIES
}
from
'
./constants
'
;
const
categoriesList
=
Object
.
keys
(
APP_CATEGORIES
).
map
((
id
:
string
)
=>
({
id
:
id
,
name
:
APP_CATEGORIES
[
id
as
MarketplaceCategoriesIds
],
}))
as
Array
<
MarketplaceCategory
>
;
type
Props
=
{
selectedCategoryId
:
MarketplaceCategoriesIds
;
onSelect
:
(
category
:
MarketplaceCategoriesIds
)
=>
void
;
}
const
CategoriesMenu
=
({
selectedCategoryId
,
onSelect
}:
Props
)
=>
{
const
selectedCategory
=
categoriesList
.
find
(
category
=>
category
.
id
===
selectedCategoryId
);
return
(
<
Menu
>
<
MenuButton
as=
{
Button
}
mb=
{
{
base
:
2
,
sm
:
0
}
}
mr=
{
{
base
:
0
,
sm
:
2
}
}
size=
"md"
variant=
"outline"
colorScheme=
"gray"
flexShrink=
{
0
}
>
<
Box
as=
"span"
display=
"flex"
alignItems=
"center"
>
{
selectedCategory
?
selectedCategory
.
name
:
'
All
'
}
<
Icon
transform=
"rotate(-90deg)"
ml=
{
{
base
:
'
auto
'
,
sm
:
1
}
}
as=
{
eastMiniArrowIcon
}
w=
{
5
}
h=
{
5
}
/>
</
Box
>
</
MenuButton
>
<
MenuList
zIndex=
{
3
}
>
{
categoriesList
.
map
((
category
:
MarketplaceCategory
)
=>
(
<
CategoriesMenuItem
key=
{
category
.
id
}
id=
{
category
.
id
}
name=
{
category
.
name
}
onClick=
{
onSelect
}
/>
))
}
</
MenuList
>
</
Menu
>
);
};
export
default
React
.
memo
(
CategoriesMenu
);
ui/apps/CategoriesMenuItem.tsx
0 → 100644
View file @
af982cd9
import
{
MenuItem
}
from
'
@chakra-ui/react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
MarketplaceCategoriesIds
}
from
'
types/client/apps
'
;
type
Props
=
{
id
:
MarketplaceCategoriesIds
;
name
:
string
;
onClick
:
(
category
:
MarketplaceCategoriesIds
)
=>
void
;
}
const
CategoriesMenuItem
=
({
id
,
name
,
onClick
}:
Props
)
=>
{
const
handleSelection
=
useCallback
(()
=>
{
onClick
(
id
);
},
[
id
,
onClick
]);
return
(
<
MenuItem
key=
{
id
}
onClick=
{
handleSelection
}
>
{
name
}
</
MenuItem
>
);
};
export
default
CategoriesMenuItem
;
ui/apps/constants.ts
View file @
af982cd9
import
type
{
MarketplaceCategor
yName
s
}
from
'
types/client/apps
'
;
import
type
{
MarketplaceCategor
iesId
s
}
from
'
types/client/apps
'
;
export
const
APP_CATEGORIES
:
{[
key
in
keyof
typeof
MarketplaceCategoryNames
]:
string
}
=
{
export
const
APP_CATEGORIES
:
{[
key
in
MarketplaceCategoriesIds
]:
string
}
=
{
all
:
'
All
'
,
favorites
:
'
Favorites
'
,
defi
:
'
DeFi
'
,
defi
:
'
DeFi
'
,
exchanges
:
'
Exchanges
'
,
exchanges
:
'
Exchanges
'
,
finance
:
'
Finance
'
,
finance
:
'
Finance
'
,
...
...
ui/apps/useMarkeplaceApps.tsx
0 → 100644
View file @
af982cd9
import
debounce
from
'
lodash/debounce
'
;
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
;
import
type
{
AppItemOverview
,
MarketplaceCategoriesIds
}
from
'
types/client/apps
'
;
import
marketplaceApps
from
'
data/marketplaceApps.json
'
;
import
useNetwork
from
'
lib/hooks/useNetwork
'
;
const
favoriteAppsLocalStorageKey
=
'
favoriteApps
'
;
function
getFavoriteApps
()
{
try
{
return
JSON
.
parse
(
localStorage
.
getItem
(
favoriteAppsLocalStorageKey
)
||
'
[]
'
);
}
catch
(
e
)
{
return
[];
}
}
function
isAppNameMatches
(
q
:
string
,
app
:
AppItemOverview
)
{
return
app
.
title
.
toLowerCase
().
includes
(
q
.
toLowerCase
());
}
function
isAppCategoryMatches
(
category
:
MarketplaceCategoriesIds
,
app
:
AppItemOverview
,
favoriteApps
:
Array
<
string
>
)
{
return
category
===
'
all
'
||
(
category
===
'
favorites
'
&&
favoriteApps
.
includes
(
app
.
id
))
||
app
.
categories
.
includes
(
category
);
}
export
default
function
useMarketplaceApps
()
{
const
selectedNetwork
=
useNetwork
();
const
[
isLoading
,
setIsLoading
]
=
useState
(
true
);
const
[
defaultAppList
,
setDefaultAppList
]
=
useState
<
Array
<
AppItemOverview
>>
();
const
[
displayedApps
,
setDisplayedApps
]
=
useState
<
Array
<
AppItemOverview
>>
([]);
const
[
displayedAppId
,
setDisplayedAppId
]
=
useState
<
string
|
null
>
(
null
);
const
[
category
,
setCategory
]
=
useState
<
MarketplaceCategoriesIds
>
(
'
all
'
);
const
[
filterQuery
,
setFilterQuery
]
=
useState
(
''
);
const
[
favoriteApps
,
setFavoriteApps
]
=
useState
<
Array
<
string
>>
([]);
const
handleFavoriteClick
=
useCallback
((
id
:
string
,
isFavorite
:
boolean
)
=>
{
const
favoriteApps
=
getFavoriteApps
();
if
(
isFavorite
)
{
const
result
=
favoriteApps
.
filter
((
appId
:
string
)
=>
appId
!==
id
);
setFavoriteApps
(
result
);
localStorage
.
setItem
(
favoriteAppsLocalStorageKey
,
JSON
.
stringify
(
result
));
}
else
{
favoriteApps
.
push
(
id
);
localStorage
.
setItem
(
favoriteAppsLocalStorageKey
,
JSON
.
stringify
(
favoriteApps
));
setFavoriteApps
(
favoriteApps
);
}
},
[
]);
const
showAppInfo
=
useCallback
((
id
:
string
)
=>
{
setDisplayedAppId
(
id
);
},
[]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const
debounceFilterApps
=
useCallback
(
debounce
(
q
=>
setFilterQuery
(
q
),
500
),
[]);
const
clearDisplayedAppId
=
useCallback
(()
=>
setDisplayedAppId
(
null
),
[]);
const
filterApps
=
useCallback
((
q
:
string
,
category
:
MarketplaceCategoriesIds
)
=>
{
const
apps
=
defaultAppList
?.
filter
(
app
=>
{
return
isAppNameMatches
(
q
,
app
)
&&
isAppCategoryMatches
(
category
,
app
,
favoriteApps
);
});
setDisplayedApps
(
apps
||
[]);
},
[
defaultAppList
,
favoriteApps
]);
const
handleCategoryChange
=
useCallback
((
newCategory
:
MarketplaceCategoriesIds
)
=>
{
setCategory
(
newCategory
);
},
[]);
useEffect
(()
=>
{
setFavoriteApps
(
getFavoriteApps
());
},
[
]);
useEffect
(()
=>
{
filterApps
(
filterQuery
,
category
);
},
[
filterQuery
,
category
,
filterApps
]);
useEffect
(()
=>
{
if
(
!
selectedNetwork
)
{
return
;
}
const
defaultDisplayedApps
=
[
...
marketplaceApps
]
.
filter
(
item
=>
item
.
chainId
===
selectedNetwork
?.
chainId
)
.
sort
((
a
,
b
)
=>
a
.
title
.
localeCompare
(
b
.
title
));
setDefaultAppList
(
defaultDisplayedApps
);
setDisplayedApps
(
defaultDisplayedApps
);
setIsLoading
(
false
);
},
[
selectedNetwork
]);
return
React
.
useMemo
(()
=>
({
category
,
handleCategoryChange
,
debounceFilterApps
,
isLoading
,
displayedApps
,
showAppInfo
,
displayedAppId
,
clearDisplayedAppId
,
favoriteApps
,
handleFavoriteClick
,
}),
[
category
,
clearDisplayedAppId
,
debounceFilterApps
,
displayedAppId
,
displayedApps
,
favoriteApps
,
handleCategoryChange
,
handleFavoriteClick
,
isLoading
,
showAppInfo
,
]);
}
ui/pages/Apps.tsx
View file @
af982cd9
import
{
Icon
,
Link
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Icon
,
Link
}
from
'
@chakra-ui/react
'
;
import
debounce
from
'
lodash/debounce
'
;
import
React
from
'
react
'
;
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
;
import
type
{
AppItemOverview
}
from
'
types/client/apps
'
;
import
marketplaceApps
from
'
data/marketplaceApps.json
'
;
import
PlusIcon
from
'
icons/plus.svg
'
;
import
PlusIcon
from
'
icons/plus.svg
'
;
import
useNetwork
from
'
lib/hooks/useNetwork
'
;
import
AppList
from
'
ui/apps/AppList
'
;
import
AppList
from
'
ui/apps/AppList
'
;
import
AppListSkeleton
from
'
ui/apps/AppListSkeleton
'
;
import
CategoriesMenu
from
'
ui/apps/CategoriesMenu
'
;
import
FilterInput
from
'
ui/shared/FilterInput
'
;
import
FilterInput
from
'
ui/shared/FilterInput
'
;
import
{
AppListSkeleton
}
from
'
../apps/AppListSkeleton
'
;
import
useMarketplaceApps
from
'
../apps/useMarkeplaceApps
'
;
const
Apps
=
()
=>
{
const
Apps
=
()
=>
{
const
selectedNetwork
=
useNetwork
();
const
{
const
[
isLoading
,
setIsLoading
]
=
useState
(
true
);
isLoading
,
const
[
defaultAppList
,
setDefaultAppList
]
=
useState
<
Array
<
AppItemOverview
>>
();
category
,
const
[
displayedApps
,
setDisplayedApps
]
=
useState
<
Array
<
AppItemOverview
>>
([]);
handleCategoryChange
,
const
[
displayedAppId
,
setDisplayedAppId
]
=
useState
<
string
|
null
>
(
null
);
debounceFilterApps
,
showAppInfo
,
const
showAppInfo
=
useCallback
((
id
:
string
)
=>
{
displayedApps
,
setDisplayedAppId
(
id
);
displayedAppId
,
},
[]);
clearDisplayedAppId
,
// eslint-disable-next-line react-hooks/exhaustive-deps
favoriteApps
,
const
debounceFilterApps
=
useCallback
(
debounce
(
q
=>
filterApps
(
q
),
500
),
[
defaultAppList
]);
handleFavoriteClick
,
const
clearDisplayedAppId
=
useCallback
(()
=>
setDisplayedAppId
(
null
),
[]);
}
=
useMarketplaceApps
();
function
filterApps
(
q
:
string
)
{
const
apps
=
defaultAppList
?.
filter
(
app
=>
app
.
title
.
toLowerCase
().
includes
(
q
.
toLowerCase
()));
setDisplayedApps
(
apps
||
[]);
}
useEffect
(()
=>
{
if
(
!
selectedNetwork
)
{
return
;
}
const
defaultDisplayedApps
=
[
...
marketplaceApps
]
.
filter
(
item
=>
item
.
chainId
===
selectedNetwork
?.
chainId
)
.
sort
((
a
,
b
)
=>
a
.
title
.
localeCompare
(
b
.
title
));
setDefaultAppList
(
defaultDisplayedApps
);
setDisplayedApps
(
defaultDisplayedApps
);
setIsLoading
(
false
);
},
[
selectedNetwork
]);
return
(
return
(
<>
<>
<
FilterInput
onChange=
{
debounceFilterApps
}
marginBottom=
{
{
base
:
'
4
'
,
lg
:
'
6
'
}
}
placeholder=
"Find app"
/>
<
Box
display=
"flex"
flexDirection=
{
{
base
:
'
column
'
,
sm
:
'
row
'
}
}
>
<
CategoriesMenu
selectedCategoryId=
{
category
}
onSelect=
{
handleCategoryChange
}
/>
<
FilterInput
onChange=
{
debounceFilterApps
}
marginBottom=
{
{
base
:
'
4
'
,
lg
:
'
6
'
}
}
placeholder=
"Find app"
/>
</
Box
>
{
isLoading
?
<
AppListSkeleton
/>
:
(
{
isLoading
?
<
AppListSkeleton
/>
:
(
<
AppList
<
AppList
...
@@ -57,6 +43,8 @@ const Apps = () => {
...
@@ -57,6 +43,8 @@ const Apps = () => {
onAppClick=
{
showAppInfo
}
onAppClick=
{
showAppInfo
}
displayedAppId=
{
displayedAppId
}
displayedAppId=
{
displayedAppId
}
onModalClose=
{
clearDisplayedAppId
}
onModalClose=
{
clearDisplayedAppId
}
favoriteApps=
{
favoriteApps
}
onFavoriteClick=
{
handleFavoriteClick
}
/>
/>
)
}
)
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment