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
0c270cc6
Commit
0c270cc6
authored
Sep 25, 2022
by
Yuri Mikhin
Committed by
Yuri Mikhin
Sep 26, 2022
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add favorites to the marketplace.
parent
425a0225
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
112 additions
and
29 deletions
+112
-29
star_filled.svg
icons/star_filled.svg
+3
-0
star_outline.svg
icons/star_outline.svg
+1
-1
AppCard.tsx
ui/apps/AppCard.tsx
+44
-6
AppList.tsx
ui/apps/AppList.tsx
+45
-2
AppModal.tsx
ui/apps/AppModal.tsx
+14
-15
Apps.tsx
ui/pages/Apps.tsx
+5
-5
No files found.
icons/star_filled.svg
0 → 100644
View file @
0c270cc6
<svg
viewBox=
"0 0 18 18"
fill=
"none"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
d=
"M13.76 17.333a.603.603 0 0 1-.294-.075L9 14.798l-4.467 2.46a.606.606 0 0 1-.663-.051.657.657 0 0 1-.213-.285.69.69 0 0 1-.038-.36l.854-5.21-3.616-3.69a.69.69 0 0 1-.16-.677.663.663 0 0 1 .194-.301c.09-.08.2-.131.316-.149l4.994-.76 2.234-4.74a.65.65 0 0 1 .232-.269.61.61 0 0 1 .666 0c.1.065.18.158.233.269l2.233 4.74 4.994.76c.117.018.226.07.316.149a.66.66 0 0 1 .193.3.69.69 0 0 1-.16.678l-3.614 3.69.853 5.21a.692.692 0 0 1-.14.537.634.634 0 0 1-.216.173.605.605 0 0 1-.265.061Z"
fill=
"currentColor"
/>
</svg>
icons/star_outline.svg
View file @
0c270cc6
<svg
viewBox=
"0 0 18 18"
fill=
"currentColor"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M13.76 17.333a.604.604 0 0 1-.294-.075l.293.075Zm.004 0a.625.625 0 0 0 .477-.234.671.671 0 0 0 .14-.538l-.853-5.21 3.615-3.689a.69.69 0 0 0 .16-.677.663.663 0 0 0-.194-.301.617.617 0 0 0-.316-.149l-4.884-.743a.208.208 0 0 1-.157-.117l-2.186-4.64a.65.65 0 0 0-.233-.269.61.61 0 0 0-.666 0 .65.65 0 0 0-.232.269l-2.186 4.64a.208.208 0 0 1-.158.117l-4.884.743a.618.618 0 0 0-.316.149.663.663 0 0 0-.193.3.69.69 0 0 0 .16.678l3.54 3.614a.208.208 0 0 1 .058.18l-.837 5.105a.69.69 0 0 0 .038.36.657.657 0 0 0 .213.286.613.613 0 0 0 .663.05L8.9 14.854a.208.208 0 0 1 .2 0l4.366 2.405m-7.795-2.915c-.028.172.154.3.307.216L8.9 12.95a.208.208 0 0 1 .2 0l2.923 1.61a.208.208 0 0 0 .306-.216l-.566-3.452a.208.208 0 0 1 .057-.18l2.486-2.536a.208.208 0 0 0-.118-.351l-3.408-.519a.208.208 0 0 1-.157-.117L9.189 4.145a.208.208 0 0 0-.377 0L7.378 7.19a.208.208 0 0 1-.158.117l-3.408.519a.208.208 0 0 0-.117.351l2.485 2.537a.208.208 0 0 1 .057.18l-.566 3.45Zm8.093 2.99h-.003.003Z"
fill=
"#4A5568"
/>
<path
fill-rule=
"evenodd"
clip-rule=
"evenodd"
d=
"M13.76 17.333a.604.604 0 0 1-.294-.075l.293.075Zm.004 0a.625.625 0 0 0 .477-.234.671.671 0 0 0 .14-.538l-.853-5.21 3.615-3.689a.69.69 0 0 0 .16-.677.663.663 0 0 0-.194-.301.617.617 0 0 0-.316-.149l-4.884-.743a.208.208 0 0 1-.157-.117l-2.186-4.64a.65.65 0 0 0-.233-.269.61.61 0 0 0-.666 0 .65.65 0 0 0-.232.269l-2.186 4.64a.208.208 0 0 1-.158.117l-4.884.743a.618.618 0 0 0-.316.149.663.663 0 0 0-.193.3.69.69 0 0 0 .16.678l3.54 3.614a.208.208 0 0 1 .058.18l-.837 5.105a.69.69 0 0 0 .038.36.657.657 0 0 0 .213.286.613.613 0 0 0 .663.05L8.9 14.854a.208.208 0 0 1 .2 0l4.366 2.405m-7.795-2.915c-.028.172.154.3.307.216L8.9 12.95a.208.208 0 0 1 .2 0l2.923 1.61a.208.208 0 0 0 .306-.216l-.566-3.452a.208.208 0 0 1 .057-.18l2.486-2.536a.208.208 0 0 0-.118-.351l-3.408-.519a.208.208 0 0 1-.157-.117L9.189 4.145a.208.208 0 0 0-.377 0L7.378 7.19a.208.208 0 0 1-.158.117l-3.408.519a.208.208 0 0 0-.117.351l2.485 2.537a.208.208 0 0 1 .057.18l-.566 3.45Zm8.093 2.99h-.003.003Z"
/>
</svg>
ui/apps/AppCard.tsx
View file @
0c270cc6
import
{
Box
,
Heading
,
Icon
,
Image
,
Link
,
LinkBox
,
LinkOverlay
,
Text
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
{
Box
,
Heading
,
Icon
,
I
conButton
,
I
mage
,
Link
,
LinkBox
,
LinkOverlay
,
Text
,
useColorModeValue
}
from
'
@chakra-ui/react
'
;
import
type
{
MouseEvent
}
from
'
react
'
;
import
React
,
{
useCallback
}
from
'
react
'
;
import
type
{
AppItemPreview
}
from
'
types/client/apps
'
;
import
northEastIcon
from
'
icons/arrows/north-east.svg
'
;
import
starFilledIcon
from
'
icons/star_filled.svg
'
;
import
starOutlineIcon
from
'
icons/star_outline.svg
'
;
interface
Props
extends
AppItemPreview
{
onInfoClick
:
(
id
:
string
)
=>
void
;
isFavorite
:
boolean
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
)
=>
void
;
}
const
AppCard
=
({
id
,
title
,
logo
,
shortDescription
,
categories
,
onInfoClick
}:
Props
)
=>
{
const
AppCard
=
({
id
,
title
,
logo
,
shortDescription
,
categories
,
onInfoClick
,
isFavorite
,
onFavoriteClick
,
}:
Props
)
=>
{
const
categoriesLabel
=
categories
.
map
(
c
=>
c
.
name
).
join
(
'
,
'
);
const
handleInfoClick
=
useCallback
((
event
:
MouseEvent
)
=>
{
...
...
@@ -18,12 +31,23 @@ const AppCard = ({ id, title, logo, shortDescription, categories, onInfoClick }:
onInfoClick
(
id
);
},
[
onInfoClick
,
id
]);
const
handleFavoriteClick
=
useCallback
(()
=>
{
onFavoriteClick
(
id
,
isFavorite
);
},
[
onFavoriteClick
,
id
,
isFavorite
]);
return
(
<
LinkBox
_hover=
{
{
boxShadow
:
'
md
'
,
}
}
_focusWithin=
{
{
boxShadow
:
'
md
'
,
}
}
borderRadius=
"md"
height=
"100%"
padding=
{
{
base
:
3
,
sm
:
'
20px
'
}
}
boxShadow=
{
`0 0 0 1px ${ useColorModeValue('var(--chakra-colors-gray-200)', 'var(--chakra-colors-gray-600)') }`
}
border=
"1px"
borderColor=
{
useColorModeValue
(
'
gray.200
'
,
'
gray.600
'
)
}
>
<
Box
display=
{
{
base
:
'
grid
'
,
sm
:
'
block
'
}
}
...
...
@@ -54,9 +78,6 @@ const AppCard = ({ id, title, logo, shortDescription, categories, onInfoClick }:
>
<
LinkOverlay
href=
"#"
_hover=
{
{
textDecoration
:
'
underline
'
,
}
}
>
{
title
}
</
LinkOverlay
>
...
...
@@ -104,6 +125,23 @@ const AppCard = ({ id, title, logo, shortDescription, categories, onInfoClick }:
/>
</
Link
>
</
Box
>
<
IconButton
position=
"absolute"
right=
{
{
base
:
3
,
sm
:
'
20px
'
}
}
top=
{
{
base
:
3
,
sm
:
'
20px
'
}
}
aria
-
label=
"Mark as favorite"
title=
"Mark as favorite"
variant=
"ghost"
colorScheme=
"gray"
w=
{
9
}
h=
{
8
}
onClick=
{
handleFavoriteClick
}
icon=
{
isFavorite
?
<
Icon
as=
{
starFilledIcon
}
w=
{
4
}
h=
{
4
}
color=
"yellow.400"
/>
:
<
Icon
as=
{
starOutlineIcon
}
w=
{
4
}
h=
{
4
}
color=
"gray.300"
/>
}
/>
</
Box
>
</
LinkBox
>
);
...
...
ui/apps/AppList.tsx
View file @
0c270cc6
import
{
Grid
,
GridItem
,
VisuallyHidden
,
Heading
}
from
'
@chakra-ui/react
'
;
import
React
from
'
react
'
;
import
React
,
{
useCallback
,
useEffect
,
useState
}
from
'
react
'
;
import
type
{
AppItemPreview
}
from
'
types/client/apps
'
;
...
...
@@ -7,12 +7,44 @@ import { apos } from 'lib/html-entities';
import
AppCard
from
'
ui/apps/AppCard
'
;
import
EmptySearchResult
from
'
ui/apps/EmptySearchResult
'
;
import
AppModal
from
'
./AppModal
'
;
type
Props
=
{
apps
:
Array
<
AppItemPreview
>
;
onAppClick
:
(
id
:
string
)
=>
void
;
displayedAppId
:
string
|
null
;
onModalClose
:
()
=>
void
;
}
function
getFavoriteApps
()
{
try
{
return
JSON
.
parse
(
localStorage
.
getItem
(
'
favoriteApps
'
)
||
'
[]
'
);
}
catch
(
e
)
{
return
[];
}
}
const
AppList
=
({
apps
,
onAppClick
}:
Props
)
=>
{
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
(
<>
<
VisuallyHidden
>
...
...
@@ -39,6 +71,8 @@ const AppList = ({ apps, onAppClick }: Props) => {
logo=
{
app
.
logo
}
shortDescription=
{
app
.
shortDescription
}
categories=
{
app
.
categories
}
isFavorite=
{
favoriteApps
.
includes
(
app
.
id
)
}
onFavoriteClick=
{
handleFavoriteClick
}
/>
</
GridItem
>
))
}
...
...
@@ -46,6 +80,15 @@ const AppList = ({ apps, onAppClick }: Props) => {
)
:
(
<
EmptySearchResult
text=
{
`Couldn${ apos }t find an app that matches your filter query.`
}
/>
)
}
{
displayedAppId
&&
(
<
AppModal
id=
{
displayedAppId
}
onClose=
{
onModalClose
}
isFavorite=
{
favoriteApps
.
includes
(
displayedAppId
)
}
onFavoriteClick=
{
handleFavoriteClick
}
/>
)
}
</>
);
};
...
...
ui/apps/AppModal.tsx
View file @
0c270cc6
import
{
LinkIcon
,
StarIcon
}
from
'
@chakra-ui/icons
'
;
import
{
LinkIcon
}
from
'
@chakra-ui/icons
'
;
import
{
Box
,
Button
,
Heading
,
Icon
,
IconButton
,
Image
,
Link
,
List
,
Modal
,
ModalBody
,
ModalCloseButton
,
ModalContent
,
ModalFooter
,
ModalHeader
,
ModalOverlay
,
Tag
,
Text
,
...
...
@@ -12,26 +12,23 @@ import { TEMPORARY_DEMO_APPS } from 'data/apps';
import
ghIcon
from
'
icons/social/git.svg
'
;
import
tgIcon
from
'
icons/social/telega.svg
'
;
import
twIcon
from
'
icons/social/tweet.svg
'
;
import
starFilledIcon
from
'
icons/star_filled.svg
'
;
import
starOutlineIcon
from
'
icons/star_outline.svg
'
;
import
{
nbsp
}
from
'
lib/html-entities
'
;
type
Props
=
{
id
:
string
|
null
;
id
:
string
;
onClose
:
()
=>
void
;
isFavorite
:
boolean
;
onFavoriteClick
:
(
id
:
string
,
isFavorite
:
boolean
)
=>
void
;
}
const
AppModal
=
({
id
,
onClose
,
isFavorite
,
onFavoriteClick
,
}:
Props
)
=>
{
const
handleFavorite
=
useCallback
(()
=>
{
// TODO: implement
},
[]);
if
(
!
id
)
{
return
null
;
}
const
{
title
,
author
,
...
...
@@ -45,8 +42,6 @@ const AppModal = ({
categories
,
}
=
TEMPORARY_DEMO_APPS
.
find
(
app
=>
app
.
id
===
id
)
as
AppItemOverview
;
const
isFavorite
=
false
;
const
socialLinks
=
[
Boolean
(
telegram
)
&&
{
icon
:
tgIcon
,
...
...
@@ -62,6 +57,10 @@ const AppModal = ({
},
].
filter
(
Boolean
)
as
Array
<
{
icon
:
FunctionComponent
;
url
:
string
}
>
;
const
handleFavoriteClick
=
useCallback
(()
=>
{
onFavoriteClick
(
id
,
isFavorite
);
},
[
onFavoriteClick
,
id
,
isFavorite
]);
return
(
<
Modal
isOpen=
{
Boolean
(
id
)
}
...
...
@@ -135,10 +134,10 @@ const AppModal = ({
colorScheme=
"gray"
w=
{
9
}
h=
{
8
}
onClick=
{
handleFavorite
}
onClick=
{
handleFavorite
Click
}
icon=
{
isFavorite
?
<
Icon
as=
{
Star
Icon
}
w=
{
4
}
h=
{
4
}
color=
"yellow.400"
/>
:
<
Icon
as=
{
starOutlineIcon
}
w=
{
4
}
h=
{
4
}
/>
}
<
Icon
as=
{
starFilled
Icon
}
w=
{
4
}
h=
{
4
}
color=
"yellow.400"
/>
:
<
Icon
as=
{
starOutlineIcon
}
w=
{
4
}
h=
{
4
}
color=
"gray.300"
/>
}
/>
</
Box
>
</
Box
>
...
...
ui/pages/Apps.tsx
View file @
0c270cc6
...
...
@@ -5,7 +5,6 @@ import type { AppItemOverview } from 'types/client/apps';
import
{
TEMPORARY_DEMO_APPS
}
from
'
data/apps
'
;
import
AppList
from
'
ui/apps/AppList
'
;
import
AppModal
from
'
ui/apps/AppModal
'
;
import
FilterInput
from
'
ui/shared/FilterInput
'
;
const
defaultDisplayedApps
=
[
...
TEMPORARY_DEMO_APPS
]
...
...
@@ -34,10 +33,11 @@ const Apps = () => {
return
(
<>
<
FilterInput
onChange=
{
debounceFilterApps
}
marginBottom=
{
{
base
:
'
4
'
,
lg
:
'
6
'
}
}
placeholder=
"Find app"
/>
<
AppList
apps=
{
displayedApps
}
onAppClick=
{
showAppInfo
}
/>
<
AppModal
id=
{
displayedAppId
}
onClose=
{
clearDisplayedAppId
}
<
AppList
apps=
{
displayedApps
}
onAppClick=
{
showAppInfo
}
displayedAppId=
{
displayedAppId
}
onModalClose=
{
clearDisplayedAppId
}
/>
</>
);
...
...
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