diff --git a/src/components/Icon/NotificationBellActive.tsx b/src/components/Icon/NotificationBellActive.tsx index 990295141..8dfdcda86 100644 --- a/src/components/Icon/NotificationBellActive.tsx +++ b/src/components/Icon/NotificationBellActive.tsx @@ -1,5 +1,3 @@ -import React from 'react' - function NotificationBellActive({ className, size = 32 }: { className?: string; size?: number }) { return ( diff --git a/src/components/Layout/Navigation.tsx b/src/components/Layout/Navigation.tsx index 383dfe42c..9a08042f1 100644 --- a/src/components/Layout/Navigation.tsx +++ b/src/components/Layout/Navigation.tsx @@ -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' @@ -118,7 +118,7 @@ const Navigation = ({ activeTab }: NavigationProps) => { - {user && } + {user && } diff --git a/src/components/Notifications/Notifications.css b/src/components/Notifications/Notifications.css new file mode 100644 index 000000000..9ab0b4a58 --- /dev/null +++ b/src/components/Notifications/Notifications.css @@ -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)); +} diff --git a/src/components/Notifications/Notifications.tsx b/src/components/Notifications/Notifications.tsx new file mode 100644 index 000000000..89af85e2c --- /dev/null +++ b/src/components/Notifications/Notifications.tsx @@ -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 ( + <> + + setOpen(false)} /> + + ) +} diff --git a/src/components/NotificationFeed.css b/src/components/Notifications/NotificationsFeed.css similarity index 72% rename from src/components/NotificationFeed.css rename to src/components/Notifications/NotificationsFeed.css index 9ce243994..ce5326614 100644 --- a/src/components/NotificationFeed.css +++ b/src/components/Notifications/NotificationsFeed.css @@ -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; @@ -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; @@ -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; @@ -83,6 +90,6 @@ height: 100%; } -.NotificationFeed__LoadMoreButtonContainer { +.NotificationsFeed__LoadMoreButtonContainer { padding: 16px; } diff --git a/src/components/NotificationFeed.tsx b/src/components/Notifications/NotificationsFeed.tsx similarity index 52% rename from src/components/NotificationFeed.tsx rename to src/components/Notifications/NotificationsFeed.tsx index a7858500f..5d0420c68 100644 --- a/src/components/NotificationFeed.tsx +++ b/src/components/Notifications/NotificationsFeed.tsx @@ -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([]) - useClickOutside('.NotificationFeed__Content', isOpen, () => setOpen(false)) + useClickOutside('.NotificationsFeed', isOpen, onClose) const { data: subscriptions, @@ -54,6 +56,7 @@ export default function NotificationFeed() { () => !!subscriptions?.find((item: { channel: string }) => isSameAddress(item.channel, CHANNEL_ADDRESS)), [subscriptions] ) + const { data: userNotifications, isLoading: isLoadingNotifications, @@ -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 ( - <> - -
-
- - {t('navigation.notifications.title')} - - {isSubscribed && ( - - )} -
- {!isSubscribed && ( -
- - {t(`navigation.notifications.${unsubscribedKey}.title`)} - {t(`navigation.notifications.${unsubscribedKey}.description`)} - -
+
+
+ + {t('navigation.notifications.title')} + + {isSubscribed && ( + )} - {isSubscribed && !isLoadingNotifications && !hasNotifications && ( -
- - - {t('navigation.notifications.empty')} - -
- )} - {showNotifications && ( -
-
- {filteredNotifications?.map((notification) => ( - - ))} +
+ {!isLoading && ( + <> + {!isSubscribed && ( +
+ + {t(`navigation.notifications.${unsubscribedKey}.title`)} + {t(`navigation.notifications.${unsubscribedKey}.description`)} +
- {showLoadMoreButton && ( -
- - {t('navigation.notifications.load_more')} - + )} + {isSubscribed && !isLoadingNotifications && !hasNotifications && ( +
+ + + {t('navigation.notifications.empty')} + +
+ )} + {showNotifications && ( +
+
+ {filteredNotifications?.map((notification) => ( + + ))}
- )} -
- )} - {(isLoadingSubscriptions || - isRefetchingSubscriptions || - isRefetchingNotifications || - (isSubscribed && isLoadingNotifications)) && } -
- + {showLoadMoreButton && ( +
+ + {t('navigation.notifications.load_more')} + +
+ )} +
+ )} + + )} + {isLoading && } +
) } diff --git a/src/intl/en.json b/src/intl/en.json index b2232f064..d46b2b236 100644 --- a/src/intl/en.json +++ b/src/intl/en.json @@ -52,6 +52,7 @@ } }, "notifications": { + "button_label": "Notifications menu", "title": "Notifications", "opt_out": "Opt out", "unsubscribed": { diff --git a/src/pages/notifications.tsx b/src/pages/notifications.tsx index 9725a0b4a..c688dea68 100644 --- a/src/pages/notifications.tsx +++ b/src/pages/notifications.tsx @@ -54,7 +54,7 @@ export default function DebugPage() { return (
- Send notification + Send announcement notification setNotificationType(Number(value))}