Skip to content

Commit

Permalink
feat: improve notification list styles
Browse files Browse the repository at this point in the history
  • Loading branch information
andyesp committed Sep 27, 2023
1 parent 796522e commit 6c1db53
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 108 deletions.
2 changes: 0 additions & 2 deletions src/components/Icon/NotificationBellActive.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react'

function NotificationBellActive({ className, size = 32 }: { className?: string; size?: number }) {
return (
<svg
Expand Down
2 changes: 0 additions & 2 deletions src/components/Icon/NotificationBellInactive.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react'

function NotificationBellInactive({ size = '32' }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} fill="none" viewBox="0 0 36 36">
Expand Down
4 changes: 2 additions & 2 deletions src/components/Layout/Navigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import useIsProfileValidated from '../../hooks/useIsProfileValidated'
import locations from '../../utils/locations'
import Link from '../Common/Typography/Link'
import Dot from '../Icon/Dot'
import NotificationFeed from '../NotificationFeed'
import Notifications from '../Notifications/Notifications'
import SearchInput from '../Search/SearchInput'

import './Navigation.css'
Expand Down Expand Up @@ -118,7 +118,7 @@ const Navigation = ({ activeTab }: NavigationProps) => {
</Tabs.Left>
<Tabs.Right>
<SearchInput />
{user && <NotificationFeed />}
{user && <Notifications />}
</Tabs.Right>
</Tabs>
</div>
Expand Down
12 changes: 12 additions & 0 deletions src/components/Notifications/Notifications.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.Notifications__Button {
background: none;
border: none;
padding: 0;
margin: 0;
transition: filter 0.2s ease;
cursor: pointer;
}

.Notifications__Button--active {
filter: drop-shadow(0 1px 8px var(--alpha-black-300));
}
28 changes: 28 additions & 0 deletions src/components/Notifications/Notifications.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useState } from 'react'

import classNames from 'classnames'

import useFormatMessage from '../../hooks/useFormatMessage'
import NotificationBellActive from '../Icon/NotificationBellActive'
import NotificationBellInactive from '../Icon/NotificationBellInactive'

import './Notifications.css'
import NotificationsFeed from './NotificationsFeed'

export default function Notifications() {
const t = useFormatMessage()
const [isOpen, setOpen] = useState(false)

return (
<>
<button
className={classNames('Notifications__Button', isOpen && 'Notifications__Button--active')}
onClick={() => setOpen((prev) => !prev)}
aria-label={t('navigation.notifications.button_label')}
>
{isOpen ? <NotificationBellActive /> : <NotificationBellInactive />}
</button>
<NotificationsFeed isOpen={isOpen} onClose={() => setOpen(false)} />
</>
)
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,8 @@
.NotificationFeed__Button {
background: none;
border: none;
padding: 0;
margin: 0;
transition: filter 0.2s ease;
cursor: pointer;
}

.NotificationFeed__Button--active {
filter: drop-shadow(0 1px 8px var(--alpha-black-300));
}

.NotificationFeed__Content {
.NotificationsFeed {
min-width: 346px;
min-height: 370px;
max-height: 385px;
height: 100;
padding-bottom: 16px;
overflow-y: auto;
pointer-events: none;
opacity: 0;
Expand All @@ -34,24 +20,37 @@
overscroll-behavior-y: contain;
}

.NotificationFeed__Content--visible {
.NotificationsFeed--visible {
pointer-events: visible;
opacity: 1;
}

.NotificationFeed__Header {
.NotificationsFeed__Button {
background: none;
border: none;
padding: 0;
margin: 0;
transition: filter 0.2s ease;
cursor: pointer;
}

.NotificationsFeed__Button--active {
filter: drop-shadow(0 1px 8px var(--alpha-black-300));
}

.NotificationsFeed__Header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px;
}

.NotificationFeed__Title {
.NotificationsFeed__Title {
margin: 0;
text-transform: uppercase;
}

.NotificationFeed__OptOut {
.NotificationsFeed__OptOut {
margin: 0;
background: none;
border: none;
Expand All @@ -62,18 +61,26 @@
cursor: pointer;
}

.NotificationFeed__EmptyView {
.NotificationsFeed__EmptyView {
gap: 16px;
}

.NotificationFeed__List {
.NotificationsFeed__ListContainer {
display: flex;
flex-direction: column;
gap: 10px;
height: 100%;
justify-content: space-between;
}

.NotificationsFeed__List {
display: flex;
flex-direction: column;
gap: 16px;
padding-bottom: 16px;
}

.NotificationFeed__EmptyView,
.NotificationFeed__UnsubscribedView {
.NotificationsFeed__EmptyView,
.NotificationsFeed__UnsubscribedView {
display: flex;
flex-direction: column;
padding: 0 16px;
Expand All @@ -83,6 +90,6 @@
height: 100%;
}

.NotificationFeed__LoadMoreButtonContainer {
.NotificationsFeed__LoadMoreButtonContainer {
padding: 16px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,36 @@ import useAuthContext from 'decentraland-gatsby/dist/context/Auth/useAuthContext
import { Button } from 'decentraland-ui/dist/components/Button/Button'
import { Loader } from 'decentraland-ui/dist/components/Loader/Loader'

import { Governance } from '../clients/Governance'
import { isSameAddress } from '../entities/Snapshot/utils'
import { DEFAULT_QUERY_STALE_TIME } from '../hooks/constants'
import { useClickOutside } from '../hooks/useClickOutside'
import useFormatMessage from '../hooks/useFormatMessage'
import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../utils/notifications'

import FullWidthButton from './Common/FullWidthButton'
import Heading from './Common/Typography/Heading'
import Text from './Common/Typography/Text'
import NotificationBellActive from './Icon/NotificationBellActive'
import NotificationBellInactive from './Icon/NotificationBellInactive'
import PeaceCircle from './Icon/PeaceCircle'
import SignGray from './Icon/SignGray'
import NotificationItem from './Notifications/NotificationItem'

import './NotificationFeed.css'
import { Governance } from '../../clients/Governance'
import { isSameAddress } from '../../entities/Snapshot/utils'
import { DEFAULT_QUERY_STALE_TIME } from '../../hooks/constants'
import { useClickOutside } from '../../hooks/useClickOutside'
import useFormatMessage from '../../hooks/useFormatMessage'
import { CHAIN_ID, CHANNEL_ADDRESS, ENV, Notification, getCaipAddress } from '../../utils/notifications'
import FullWidthButton from '../Common/FullWidthButton'
import Heading from '../Common/Typography/Heading'
import Text from '../Common/Typography/Text'
import NotificationBellInactive from '../Icon/NotificationBellInactive'
import PeaceCircle from '../Icon/PeaceCircle'
import SignGray from '../Icon/SignGray'

import NotificationItem from './NotificationItem'
import './NotificationsFeed.css'

const NOTIFICATIONS_PER_PAGE = 5

export default function NotificationFeed() {
interface Props {
isOpen: boolean
onClose: () => void
}

export default function NotificationsFeed({ isOpen, onClose }: Props) {
const t = useFormatMessage()
const [isOpen, setOpen] = useState(false)
const [isSubscribing, setIsSubscribing] = useState(false)
const [user, userState] = useAuthContext()
const [filteredNotifications, setFilteredNotifications] = useState<Notification[]>([])

useClickOutside('.NotificationFeed__Content', isOpen, () => setOpen(false))
useClickOutside('.NotificationsFeed', isOpen, onClose)

const {
data: subscriptions,
Expand All @@ -54,6 +56,7 @@ export default function NotificationFeed() {
() => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, CHANNEL_ADDRESS)),
[subscriptions]
)

const {
data: userNotifications,
isLoading: isLoadingNotifications,
Expand Down Expand Up @@ -122,66 +125,63 @@ export default function NotificationFeed() {
const showLoadMoreButton = filteredNotifications.length !== userNotifications?.length
const unsubscribedKey = isSubscribing ? 'subscribing' : 'unsubscribed'
const UnsubscribedIcon = isSubscribing ? SignGray : NotificationBellInactive
const isLoading =
isLoadingSubscriptions ||
isRefetchingSubscriptions ||
isRefetchingNotifications ||
(isSubscribed && isLoadingNotifications)

return (
<>
<button
className={classNames('NotificationFeed__Button', isOpen && 'NotificationFeed__Button--active')}
onClick={() => setOpen((prev) => !prev)}
aria-label="Open notifications"
>
{isOpen ? <NotificationBellActive /> : <NotificationBellInactive />}
</button>
<div className={classNames('NotificationFeed__Content', isOpen && 'NotificationFeed__Content--visible')}>
<div className="NotificationFeed__Header">
<Text color="secondary" size="sm" className="NotificationFeed__Title">
{t('navigation.notifications.title')}
</Text>
{isSubscribed && (
<button className="NotificationFeed__OptOut" onClick={handleUnsubscribeUserToChannel}>
{t('navigation.notifications.opt_out')}
</button>
)}
</div>
{!isSubscribed && (
<div className="NotificationFeed__UnsubscribedView">
<UnsubscribedIcon size="124" />
<Heading size="sm">{t(`navigation.notifications.${unsubscribedKey}.title`)}</Heading>
<Text>{t(`navigation.notifications.${unsubscribedKey}.description`)}</Text>
<Button size="small" primary disabled={isSubscribing} onClick={handleSubscribeUserToChannel}>
{t(`navigation.notifications.${unsubscribedKey}.button`)}
</Button>
</div>
<div className={classNames('NotificationsFeed', isOpen && 'NotificationsFeed--visible')}>
<div className="NotificationsFeed__Header">
<Text color="secondary" size="sm" className="NotificationsFeed__Title">
{t('navigation.notifications.title')}
</Text>
{isSubscribed && (
<button className="NotificationsFeed__OptOut" onClick={handleUnsubscribeUserToChannel}>
{t('navigation.notifications.opt_out')}
</button>
)}
{isSubscribed && !isLoadingNotifications && !hasNotifications && (
<div className="NotificationFeed__EmptyView">
<PeaceCircle />
<Text color="secondary" weight="medium">
{t('navigation.notifications.empty')}
</Text>
</div>
)}
{showNotifications && (
<div>
<div className="NotificationFeed__List">
{filteredNotifications?.map((notification) => (
<NotificationItem key={notification.payload_id} notification={notification} />
))}
</div>
{!isLoading && (
<>
{!isSubscribed && (
<div className="NotificationsFeed__UnsubscribedView">
<UnsubscribedIcon size="124" />
<Heading size="sm">{t(`navigation.notifications.${unsubscribedKey}.title`)}</Heading>
<Text>{t(`navigation.notifications.${unsubscribedKey}.description`)}</Text>
<Button size="small" primary disabled={isSubscribing} onClick={handleSubscribeUserToChannel}>
{t(`navigation.notifications.${unsubscribedKey}.button`)}
</Button>
</div>
{showLoadMoreButton && (
<div className="NotificationFeed__LoadMoreButtonContainer">
<FullWidthButton onClick={handleLoadMoreClick}>
{t('navigation.notifications.load_more')}
</FullWidthButton>
)}
{isSubscribed && !isLoadingNotifications && !hasNotifications && (
<div className="NotificationsFeed__EmptyView">
<PeaceCircle />
<Text color="secondary" weight="medium">
{t('navigation.notifications.empty')}
</Text>
</div>
)}
{showNotifications && (
<div className="NotificationsFeed__ListContainer">
<div className="NotificationsFeed__List">
{filteredNotifications?.map((notification) => (
<NotificationItem key={notification.payload_id} notification={notification} />
))}
</div>
)}
</div>
)}
{(isLoadingSubscriptions ||
isRefetchingSubscriptions ||
isRefetchingNotifications ||
(isSubscribed && isLoadingNotifications)) && <Loader active />}
</div>
</>
{showLoadMoreButton && (
<div className="NotificationsFeed__LoadMoreButtonContainer">
<FullWidthButton onClick={handleLoadMoreClick}>
{t('navigation.notifications.load_more')}
</FullWidthButton>
</div>
)}
</div>
)}
</>
)}
{isLoading && <Loader active />}
</div>
)
}
1 change: 1 addition & 0 deletions src/intl/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
}
},
"notifications": {
"button_label": "Notifications menu",
"title": "Notifications",
"opt_out": "Opt out",
"unsubscribed": {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/notifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default function DebugPage() {
return (
<Container className="DebugPage">
<form onSubmit={handleSendNotification}>
<Heading size="md">Send notification</Heading>
<Heading size="md">Send announcement notification</Heading>
<SelectField
value={notificationType}
onChange={(_, { value }) => setNotificationType(Number(value))}
Expand Down

0 comments on commit 6c1db53

Please sign in to comment.