From ba1ece28b95951d2c6ce7b3b83d73627b957d8a8 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Mon, 6 Oct 2025 02:37:42 +0300 Subject: [PATCH 1/3] feat: implement notification list component --- .../src/assets/icons/loader.component.svg | 3 + .../icons/notifications-bell.component.svg | 9 ++ .../assets/icons/smiley-face.component.svg | 9 ++ .../EmptyState.module.scss | 7 ++ .../NotificationsCenter/EmptyState.tsx | 19 +++ .../NotificationListItem.tsx | 20 ++- .../NotificationsCenter/Notifications.tsx | 84 +++++++++++++ .../NotificationsAllClear.tsx | 20 --- .../NotificationsBell.module.scss | 5 + .../NotificationsCenter/NotificationsBell.tsx | 9 +- .../NotificationsBellContainer.tsx | 19 ++- .../NotificationsCenter.module.scss | 24 ++++ .../NotificationsCenter.tsx | 118 ++++++++++-------- .../NotificationsCenterContainer.tsx | 23 +--- .../NotificationsDropDown.module.scss | 16 +++ .../NotificationsDropDown.tsx | 60 +++++---- .../NotificationsList.module.scss | 71 +++++++++++ .../NotificationsCenter/NotificationsList.tsx | 98 +++++++++++++++ .../src/hooks/useNotificationsCenter.ts | 25 +++- .../src/routes/ExtensionRoutes.tsx | 4 +- .../browser-extension-wallet/en.json | 2 + 21 files changed, 506 insertions(+), 139 deletions(-) create mode 100644 apps/browser-extension-wallet/src/assets/icons/loader.component.svg create mode 100644 apps/browser-extension-wallet/src/assets/icons/notifications-bell.component.svg create mode 100644 apps/browser-extension-wallet/src/assets/icons/smiley-face.component.svg create mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss create mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx create mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx delete mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.tsx create mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.module.scss create mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx diff --git a/apps/browser-extension-wallet/src/assets/icons/loader.component.svg b/apps/browser-extension-wallet/src/assets/icons/loader.component.svg new file mode 100644 index 000000000..3cea0311b --- /dev/null +++ b/apps/browser-extension-wallet/src/assets/icons/loader.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/browser-extension-wallet/src/assets/icons/notifications-bell.component.svg b/apps/browser-extension-wallet/src/assets/icons/notifications-bell.component.svg new file mode 100644 index 000000000..45f77ec9e --- /dev/null +++ b/apps/browser-extension-wallet/src/assets/icons/notifications-bell.component.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/browser-extension-wallet/src/assets/icons/smiley-face.component.svg b/apps/browser-extension-wallet/src/assets/icons/smiley-face.component.svg new file mode 100644 index 000000000..35595ae19 --- /dev/null +++ b/apps/browser-extension-wallet/src/assets/icons/smiley-face.component.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss b/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss new file mode 100644 index 000000000..270d1ae4d --- /dev/null +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss @@ -0,0 +1,7 @@ +@import '../../../../../packages/core/src/ui/styles/theme.scss'; +@import '../../../../../packages/common/src/ui/styles/theme.scss'; + +.icon { + width: size_unit(10.5); + height: size_unit(10.5); +} diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx new file mode 100644 index 000000000..1b092654f --- /dev/null +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { Flex, Text } from '@input-output-hk/lace-ui-toolkit'; +import { useTranslation } from 'react-i18next'; +import styles from './EmptyState.module.scss'; +import SmileyFaceIcon from '../../assets/icons/smiley-face.component.svg'; + +export const EmptyState = (): React.ReactElement => { + const { t } = useTranslation(); + + return ( + + + + {t('notificationsCenter.emptyState.title')} + {t('notificationsCenter.emptyState.description')} + + + ); +}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx index b08f7b888..5a933cfec 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx @@ -5,17 +5,32 @@ import TrashOutlineComponent from '../../assets/icons/browser-view/trash-icon.co import { useTranslation } from 'react-i18next'; import classnames from 'classnames'; +export type LaceMessage = { + id: string; + topic: string; + title: string; + body: string; + format: string; +}; + +export type LaceNotification = { + message: LaceMessage; + read?: boolean; +}; + export interface NotificationListItemProps { + id: string; title: string; isRead?: boolean; popupView?: boolean; publisher: string; - onRemove?: () => void; + onRemove?: (id: string) => void; onClick: () => void; withBorder?: boolean; } export const NotificationListItem = ({ + id, title, isRead = false, popupView = false, @@ -28,7 +43,7 @@ export const NotificationListItem = ({ const handleRemove = (e: React.MouseEvent) => { e.stopPropagation(); - onRemove(); + onRemove?.(id); }; const PublisherTextComponent = popupView ? Text.Label : Text.Body.Small; @@ -55,6 +70,7 @@ export const NotificationListItem = ({ onClick={onClick} className={classnames(styles.container, withBorder && styles.withBorder)} p="$20" + w="$fill" > diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx new file mode 100644 index 000000000..b6019628d --- /dev/null +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx @@ -0,0 +1,84 @@ +/* eslint-disable unicorn/no-useless-undefined */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, promise/catch-or-return, sonarjs/cognitive-complexity, no-magic-numbers, unicorn/no-null */ +import React, { useState } from 'react'; +import { ContentLayout } from '@src/components/Layout'; +import { useTranslation } from 'react-i18next'; +import { WarningModal } from '@src/views/browser-view/components'; +import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; +import { NotificationsList } from './NotificationsList'; +import { EmptyState } from './EmptyState'; +import { Box, Flex } from '@input-output-hk/lace-ui-toolkit'; +import { NavigationButton } from '@lace/common'; +import { useHistory } from 'react-router'; +import { SectionTitle } from '@components/Layout/SectionTitle'; + +export const NotificationsCenter = (): React.ReactElement => { + const { t } = useTranslation(); + const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); + const { notifications, loadMore, remove, unreadNotifications, isLoading } = useNotificationsCenter(); + const [notificationIdToRemove, setNotificationIdToRemove] = useState(); + const history = useHistory(); + + const onShowRemoveNotificationModal = (id: string) => { + setNotificationIdToRemove(id); + setIsRemoveNotificationModalVisible(true); + }; + + const onHideRemoveNotificationModal = () => { + setNotificationIdToRemove(undefined); + setIsRemoveNotificationModalVisible(false); + }; + + const isInitialLoad = typeof notifications === 'undefined'; + + return ( + + history.goBack()} /> + {t('notificationsCenter.title')} + + } + sideText={`(${unreadNotifications})`} + data-testid="notifications-center-title" + /> + } + isLoading={isInitialLoad} + > +
+ {notifications?.length > 0 ? ( + + ) : ( + + + + )} +
+ { + remove(notificationIdToRemove); + onHideRemoveNotificationModal(); + }} + isPopupView + /> + + ); +}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.tsx deleted file mode 100644 index 31815dabe..000000000 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; - -import { Flex, Text } from '@input-output-hk/lace-ui-toolkit'; - -import styles from './NotificationsAllClear.module.scss'; - -import HappyFaceIcon from '@lace/core/src/ui/assets/icons/happy-face.component.svg'; - -export const NotificationsAllClear = (): React.ReactElement => { - const { t } = useTranslation(); - - return ( - - - {t('notificationsCenter.allClear.title')} - {t('notificationsCenter.allClear.description')} - - ); -}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss index 636e74c9e..4c0905678 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss @@ -25,3 +25,8 @@ padding: size_unit(1) size_unit(2.75) !important; position: relative; } + +.icon { + width: size_unit(2); + height: size_unit(2); +} diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx index 7778f2aa4..8f7d107b7 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx @@ -4,10 +4,11 @@ import { Button } from '@lace/common'; import styles from './NotificationsBell.module.scss'; -import NotificationBellIcon from '@lace/core/src/ui/assets/icons/notifications-bell.component.svg'; +import NotificationBellIcon from '../../assets/icons/notifications-bell.component.svg'; -// eslint-disable-next-line no-magic-numbers -const formatNotificationCount = (count: number) => (count < 10 ? count.toString() : '9+'); +const MAX_NOTIFICATION_COUNT = 9; +const formatNotificationCount = (count: number) => + count < MAX_NOTIFICATION_COUNT ? count.toString() : `${MAX_NOTIFICATION_COUNT}+`; export interface NotificationsBellProps { onClick: () => void; @@ -16,7 +17,7 @@ export interface NotificationsBellProps { export const NotificationsBell = ({ onClick, unreadNotifications }: NotificationsBellProps): React.ReactElement => ( ); diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx index 5fceb5dbf..beaa7f75d 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx @@ -15,10 +15,14 @@ export interface NotificationsCenterContainerProps { export const NotificationsBellContainer = ({ popupView }: NotificationsCenterContainerProps): React.ReactElement => { const { isNotificationsCenterEnabled } = useNotificationsCenterConfig(); - const { markAsRead, notifications, unreadNotifications } = useNotificationsCenter(); + const { unreadNotifications } = useNotificationsCenter(); const history = useHistory(); const [isOpen, setIsOpen] = useState(false); + const handleDropdownState = (openDropdown: boolean) => { + setIsOpen(openDropdown); + }; + const handleViewAll = () => { setIsOpen(false); history.push(walletRoutePaths.notifications); @@ -27,18 +31,9 @@ export const NotificationsBellContainer = ({ popupView }: NotificationsCenterCon return ( isNotificationsCenterEnabled && ( ( - markAsRead()} - onMarkAsRead={(id: string) => markAsRead(id)} - onViewAll={handleViewAll} - popupView={popupView} - unreadNotifications={unreadNotifications} - /> - )} + onOpenChange={handleDropdownState} + dropdownRender={() => } placement="bottomRight" trigger={['click']} > diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss index 7feec7c01..8dd669bc8 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss @@ -4,3 +4,27 @@ .button { max-width: size_unit(25); } + +.sectionTitle { + margin-bottom: size_unit(0) !important; +} + +.scrollContainer { + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-track { + background: var(--bg-color-secondary, #f5f5f5); + border-radius: 3px; + } + + &::-webkit-scrollbar-thumb { + background: var(--text-color-tertiary, #ccc); + border-radius: 3px; + + &:hover { + background: var(--text-color-secondary, #999); + } + } +} diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx index 4adaafbf4..257bec4b9 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx @@ -1,37 +1,37 @@ +/* eslint-disable unicorn/no-useless-undefined */ import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Box, Flex } from '@input-output-hk/lace-ui-toolkit'; import { Button, NavigationButton } from '@lace/common'; import { SectionTitle } from '@components/Layout/SectionTitle'; -import { LaceNotification, NotificationsTopic } from '@src/types/notifications-center'; +import { NotificationsList } from './NotificationsList'; +import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; import styles from './NotificationsCenter.module.scss'; import { WarningModal } from '@src/views/browser-view/components/WarningModal/WarningModal'; +import { LACE_APP_ID } from '@src/utils/constants'; +import { EmptyState } from './EmptyState'; +import { useHistory } from 'react-router'; -export interface NotificationsCenterProps { - notifications: LaceNotification[]; - onBack: () => void; - onMarkAllAsRead: () => void; - onMarkAsRead: (id: string) => void; - popupView?: boolean; - topics: NotificationsTopic[]; - unreadNotifications: number; -} - -export const NotificationsCenter = ({ - onBack, - onMarkAllAsRead, - popupView, - unreadNotifications -}: NotificationsCenterProps): React.ReactElement => { +export const NotificationsCenter = (): React.ReactElement => { const { t } = useTranslation(); + const history = useHistory(); const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); + const { notifications, loadMore, markAsRead, remove, unreadNotifications, isLoading } = useNotificationsCenter(); + const [notificationIdToRemove, setNotificationIdToRemove] = useState(); + + const onBack = () => { + history.goBack(); + }; + + const onShowRemoveNotificationModal = (id: string) => { + setNotificationIdToRemove(id); + setIsRemoveNotificationModalVisible(true); + }; - const onRemoveNotification = () => { - // TODO: implement remove notification - // eslint-disable-next-line no-console - console.log('remove notification'); + const onHideRemoveNotificationModal = () => { + setNotificationIdToRemove(undefined); setIsRemoveNotificationModalVisible(false); }; @@ -41,39 +41,57 @@ export const NotificationsCenter = ({ visible={isRemoveNotificationModalVisible} header={t('notificationsCenter.removeNotification')} content={t('notificationsCenter.removeNotification.description')} - onCancel={() => setIsRemoveNotificationModalVisible(false)} + onCancel={onHideRemoveNotificationModal} cancelLabel={t('notificationsCenter.removeNotification.cancel')} confirmLabel={t('notificationsCenter.removeNotification.confirm')} - onConfirm={onRemoveNotification} - isPopupView={popupView} + onConfirm={() => { + remove(notificationIdToRemove); + onHideRemoveNotificationModal(); + }} /> - - - - 0 ? `(${unreadNotifications})` : undefined} - title={ - - - {t('notificationsCenter.title')} - - } - /> +
+ + + + + + {t('notificationsCenter.title')} + + } + /> + + {unreadNotifications > 0 && ( + + )} + + + {notifications?.length > 0 ? ( + + ) : ( + + - {!popupView && ( - - )} - - Notifications Center (Placeholder content) - + )} +
); }; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx index f25f2eea6..f9c9e37c0 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx @@ -1,26 +1,5 @@ import React from 'react'; -import { useHistory } from 'react-router'; - -import { useWalletStore } from '@stores'; -import { APP_MODE_POPUP } from '@src/utils/constants'; -import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; import { NotificationsCenter } from './NotificationsCenter'; -export const NotificationsCenterContainer = (): React.ReactElement => { - const history = useHistory(); - const { walletUI } = useWalletStore(); - const { markAsRead, notifications, topics, unreadNotifications } = useNotificationsCenter(); - - return ( - history.goBack()} - onMarkAllAsRead={() => markAsRead()} - onMarkAsRead={(id: string) => void markAsRead(id)} - popupView={walletUI.appMode === APP_MODE_POPUP} - topics={topics} - unreadNotifications={unreadNotifications} - /> - ); -}; +export const NotificationsCenterContainer = (): React.ReactElement => ; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.module.scss b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.module.scss index 4a9000417..99a6a3143 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.module.scss +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.module.scss @@ -2,6 +2,7 @@ @import '../../../../../packages/common/src/ui/styles/theme.scss'; @import '../../../../../packages/common/src/ui/styles/abstracts/mixins'; + .container { background-color: var(--bg-color-container, #ffffff) !important; border: 1px var(--light-mode-light-grey) solid !important; @@ -12,6 +13,12 @@ padding: size_unit(1) !important; width: 310px; + &.isEmpty { + display: flex; + justify-content: center; + align-items: center; + } + &.popupView { left: size_unit(-4); } @@ -23,3 +30,12 @@ overflow-y: scroll; @include scroll-bar-style; } + +.notificationsListContainer { + background-color: var(--bg-color-container, #ffffff) !important; + border-radius: 0 !important; +} + +.btn { + cursor: pointer; +} diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx index f88847ace..807a8da6f 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx @@ -3,48 +3,58 @@ import classnames from 'classnames'; import { useTranslation } from 'react-i18next'; import { Menu } from 'antd'; -import { Divider, Flex, Text } from '@input-output-hk/lace-ui-toolkit'; +import { Box, Divider, Flex, Text } from '@input-output-hk/lace-ui-toolkit'; -import { LaceNotification } from '@src/types/notifications-center'; - -import { NotificationsAllClear } from './NotificationsAllClear'; +import { EmptyState } from './EmptyState'; +import { NotificationsList } from './NotificationsList'; +import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; import styles from './NotificationsDropDown.module.scss'; export interface NotificationsDropDownProps { - notifications: LaceNotification[]; - onMarkAllAsRead: () => void; - onMarkAsRead: (id: string) => void; onViewAll: () => void; popupView?: boolean; - unreadNotifications: number; } -export const NotificationsDropDown = ({ - notifications, - onMarkAllAsRead, - onViewAll, - popupView, - unreadNotifications -}: NotificationsDropDownProps): React.ReactElement => { +export const NotificationsDropDown = ({ onViewAll, popupView }: NotificationsDropDownProps): React.ReactElement => { const { t } = useTranslation(); + const { notifications, loadMore, isLoading, markAsRead, unreadNotifications } = useNotificationsCenter(); return ( -
- {notifications.length > 0 ?
Placeholder content
: } +
+ {notifications?.length > 0 ? ( + + ) : ( + + )}
- - - {t(`notificationsCenter.${notifications.length > 0 ? 'viewAll' : 'manageSubscriptions'}`)} - - - {unreadNotifications > 0 && ( - - {t('notificationsCenter.markAllAsRead')} + + + {t(`notificationsCenter.${notifications?.length > 0 ? 'viewAll' : 'manageSubscriptions'}`)} + + {unreadNotifications > 0 && ( + markAsRead()} p="$8"> + + {t('notificationsCenter.markAllAsRead')} + + )}
diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.module.scss b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.module.scss new file mode 100644 index 000000000..58e93091b --- /dev/null +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.module.scss @@ -0,0 +1,71 @@ +@import '../../../../../packages/core/src/ui/styles/theme.scss'; +@import '../../../../../packages/common/src/ui/styles/theme.scss'; + +.infitineScroll { + background: var(--bg-color-body, #ffffff); + &::-webkit-scrollbar { + width: 0 !important; + } +} + +.notificationsContainer { + background: var(--bg-color-body, #ffffff); + padding: 0 !important; + border-radius: 12px; + margin-bottom: 30px !important; + @media (max-width: $breakpoint-popup) { + margin-bottom: 0; + } + :global(.ant-list-header) { + border: none !important; + padding: 0 !important; + } +} + + +.listItem { + border-bottom: none !important; + padding-bottom: 8px !important; + padding-top: 8px !important; + + &.withBorder { + border-bottom: 1px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)) !important; + &:last-child { + border-bottom: none !important; + } + } + + &:first-child { + padding-top: 0 !important; + } + + &:last-child { + padding-bottom: 0 !important; + } +} + +:global(.ant-list-split) :local(.listItemWrapper) { + border-bottom: none !important; + padding: size_unit(2) size_unit(1); + margin-left: -#{size_unit(1)}; + margin-right: -#{size_unit(1)}; + &:hover { + background: var(--dark-mode-mid-grey, var(--data-light-grey, #f9f9f9)); + border-radius: 16px; + } +} + +@keyframes rotating { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.loader { + animation: rotating 2s linear infinite; + width: 18px; + height: 18px; +} diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx new file mode 100644 index 000000000..75ab7512f --- /dev/null +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx @@ -0,0 +1,98 @@ +/* eslint-disable unicorn/no-null */ +import { List, Skeleton } from 'antd'; +import cn from 'classnames'; +import React, { useState, useCallback, useMemo } from 'react'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import { NotificationListItem, NotificationListItemProps } from './NotificationListItem'; +import styles from './NotificationsList.module.scss'; +import isNumber from 'lodash/isNumber'; +import Loader from '../../assets/icons/loader.component.svg'; +import { Flex } from '@input-output-hk/lace-ui-toolkit'; + +const ESTIMATED_MIN_ITEM_HEIGHT = 96; + +export const useItemsPageSize = (estimatedItemHeight = ESTIMATED_MIN_ITEM_HEIGHT): number => { + // workaround for bug in react-infinite-scroll-component + // related to not loading more elements if the height of the container is less than the height of the window + // see: https://github.com/ankeetmaini/react-infinite-scroll-component/issues/380 + // ticket for proper fix on our end: https://input-output.atlassian.net/browse/LW-8986 + // initialWindowHeight state needed to ensure that page size remains the same if window is resized + const [initialWindowHeight] = useState(window.innerHeight); + // eslint-disable-next-line no-magic-numbers + return Math.max(5, Math.floor(initialWindowHeight / estimatedItemHeight)); +}; + +export interface NotificationsListProps { + className?: string; + notifications: NotificationListItemProps[]; + scrollableTarget: string; + endMessage?: React.ReactNode; + dataLength: number; + popupView?: boolean; + loadMore: () => void; + hasMore?: boolean; + onRemove?: (id: string) => void; + isLoading?: boolean; + withBorder?: boolean; + withDivider?: boolean; +} + +export const NotificationsList = ({ + className, + notifications, + scrollableTarget, + endMessage, + dataLength, + popupView, + loadMore, + hasMore = true, + onRemove, + isLoading = false, + withBorder = true, + withDivider +}: NotificationsListProps): React.ReactElement => { + const next = useCallback(() => { + loadMore(); + }, [loadMore]); + + const loader = useMemo( + () => + isLoading ? ( + + + + ) : null, + [isLoading] + ); + + return ( + + {!isNumber(notifications.length) ? ( + + ) : ( + ( + + + + )} + /> + )} + + ); +}; diff --git a/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts b/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts index 7b5bcf511..63c2418c9 100644 --- a/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts +++ b/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts @@ -31,13 +31,34 @@ export const useNotificationsCenter = () => { [notifications] ); + const mappedNotifications = useMemo( + () => + notifications?.map((template) => ({ + id: template.message.id, + title: template.message.title, + publisher: template.message.topic, + isRead: template.read, + onClick: () => { + // eslint-disable-next-line no-console + console.log(`Clicked notification ${template.message.id}`); + } + })), + [notifications] + ); + return { - notifications, + notifications: mappedNotifications, unreadNotifications, topics, subscribe, unsubscribe, markAsRead, - remove + remove, + // TODO: Implement loadMore and isLoading + loadMore: () => { + // eslint-disable-next-line no-console + console.log('loadMore'); + }, + isLoading: false }; }; diff --git a/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx b/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx index c93020dbb..20ea6161b 100644 --- a/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx +++ b/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx @@ -15,7 +15,7 @@ import { NftDetail, Nfts } from '@src/features/nfts'; import { useWalletStore } from '@stores'; import { config } from '@src/config'; import { Voting } from '@src/features/voting-beta/components'; -import { NotificationsCenterContainer } from '@src/components/NotificationsCenter'; +import { NotificationsCenter } from '@src/components/NotificationsCenter/Notifications'; import { useNotificationsCenterConfig } from '@hooks/useNotificationsCenterConfig'; const { GOV_TOOLS_URLS } = config(); @@ -34,7 +34,7 @@ export const ExtensionRoutes = (): React.ReactElement => { {isNotificationsCenterEnabled && ( - + )} {!isSharedWallet && } diff --git a/packages/translation/src/lib/translations/browser-extension-wallet/en.json b/packages/translation/src/lib/translations/browser-extension-wallet/en.json index d8c9541d7..de110382e 100644 --- a/packages/translation/src/lib/translations/browser-extension-wallet/en.json +++ b/packages/translation/src/lib/translations/browser-extension-wallet/en.json @@ -794,6 +794,8 @@ "notificationsCenter.removeNotification.description": "Are you sure you wish to remove this notification? it will not be possible to revert this action", "notificationsCenter.removeNotification.confirm": "Remove", "notificationsCenter.removeNotification.cancel": "Cancel", + "notificationsCenter.emptyState.title": "All clear", + "notificationsCenter.emptyState.description": "You don't have any notifications currently", "poolDetails.delegate": "Delegate to this pool", "poolDetails.sectionTitle": "Pool detail", "qrInfo.publicKey": "Show public key", From c2961c2a7ea38724d53a050f9432c11bcbd294fd Mon Sep 17 00:00:00 2001 From: vetalcore Date: Tue, 7 Oct 2025 11:42:51 +0300 Subject: [PATCH 2/3] fixup! feat: implement notification list component --- .../NotificationsCenter/Notifications.tsx | 4 +-- .../NotificationsCenter/NotificationsBell.tsx | 2 +- .../NotificationsCenter.tsx | 4 +-- .../NotificationsDropDown.tsx | 4 +-- .../NotificationsCenter/NotificationsList.tsx | 25 +++++++++++++------ .../src/hooks/useNotificationsCenter.ts | 25 ++----------------- .../ui/assets/icons/happy-face.component.svg | 9 ------- .../icons/notifications-bell.component.svg | 9 ------- .../browser-extension-wallet/en.json | 2 -- 9 files changed, 24 insertions(+), 60 deletions(-) delete mode 100644 packages/core/src/ui/assets/icons/happy-face.component.svg delete mode 100644 packages/core/src/ui/assets/icons/notifications-bell.component.svg diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx index b6019628d..ad0610904 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx @@ -15,7 +15,7 @@ import { SectionTitle } from '@components/Layout/SectionTitle'; export const NotificationsCenter = (): React.ReactElement => { const { t } = useTranslation(); const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); - const { notifications, loadMore, remove, unreadNotifications, isLoading } = useNotificationsCenter(); + const { notifications, remove, unreadNotifications } = useNotificationsCenter(); const [notificationIdToRemove, setNotificationIdToRemove] = useState(); const history = useHistory(); @@ -51,9 +51,7 @@ export const NotificationsCenter = (): React.ReactElement => {
{notifications?.length > 0 ? ( - count < MAX_NOTIFICATION_COUNT ? count.toString() : `${MAX_NOTIFICATION_COUNT}+`; + count <= MAX_NOTIFICATION_COUNT ? count.toString() : `${MAX_NOTIFICATION_COUNT}+`; export interface NotificationsBellProps { onClick: () => void; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx index 257bec4b9..6acfff467 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx @@ -18,7 +18,7 @@ export const NotificationsCenter = (): React.ReactElement => { const { t } = useTranslation(); const history = useHistory(); const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); - const { notifications, loadMore, markAsRead, remove, unreadNotifications, isLoading } = useNotificationsCenter(); + const { notifications, markAsRead, remove, unreadNotifications } = useNotificationsCenter(); const [notificationIdToRemove, setNotificationIdToRemove] = useState(); const onBack = () => { @@ -82,9 +82,7 @@ export const NotificationsCenter = (): React.ReactElement => { notifications={notifications} scrollableTarget={LACE_APP_ID} dataLength={notifications.length} - loadMore={loadMore} onRemove={onShowRemoveNotificationModal} - isLoading={isLoading} /> ) : ( diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx index 807a8da6f..c44fbcce6 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsDropDown.tsx @@ -18,7 +18,7 @@ export interface NotificationsDropDownProps { export const NotificationsDropDown = ({ onViewAll, popupView }: NotificationsDropDownProps): React.ReactElement => { const { t } = useTranslation(); - const { notifications, loadMore, isLoading, markAsRead, unreadNotifications } = useNotificationsCenter(); + const { notifications, markAsRead, unreadNotifications } = useNotificationsCenter(); return ( @@ -32,8 +32,6 @@ export const NotificationsDropDown = ({ onViewAll, popupView }: NotificationsDro notifications={notifications} scrollableTarget="notifications-dropdown-content" dataLength={notifications.length} - loadMore={loadMore} - isLoading={isLoading} withBorder={false} withDivider popupView diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx index 75ab7512f..57bc08513 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx +++ b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsList.tsx @@ -3,11 +3,12 @@ import { List, Skeleton } from 'antd'; import cn from 'classnames'; import React, { useState, useCallback, useMemo } from 'react'; import InfiniteScroll from 'react-infinite-scroll-component'; -import { NotificationListItem, NotificationListItemProps } from './NotificationListItem'; +import { NotificationListItem } from './NotificationListItem'; import styles from './NotificationsList.module.scss'; import isNumber from 'lodash/isNumber'; import Loader from '../../assets/icons/loader.component.svg'; import { Flex } from '@input-output-hk/lace-ui-toolkit'; +import { LaceNotification } from '@src/types/notifications-center'; const ESTIMATED_MIN_ITEM_HEIGHT = 96; @@ -24,13 +25,14 @@ export const useItemsPageSize = (estimatedItemHeight = ESTIMATED_MIN_ITEM_HEIGHT export interface NotificationsListProps { className?: string; - notifications: NotificationListItemProps[]; + notifications: LaceNotification[]; scrollableTarget: string; endMessage?: React.ReactNode; dataLength: number; popupView?: boolean; - loadMore: () => void; + loadMore?: () => void; hasMore?: boolean; + onClick?: (id: string) => void; onRemove?: (id: string) => void; isLoading?: boolean; withBorder?: boolean; @@ -45,14 +47,15 @@ export const NotificationsList = ({ dataLength, popupView, loadMore, - hasMore = true, + onClick, + hasMore = false, onRemove, isLoading = false, withBorder = true, withDivider }: NotificationsListProps): React.ReactElement => { const next = useCallback(() => { - loadMore(); + loadMore?.(); }, [loadMore]); const loader = useMemo( @@ -83,12 +86,20 @@ export const NotificationsList = ({ data-testid="notifications-list" itemLayout="horizontal" dataSource={notifications} - renderItem={(props: NotificationListItemProps) => ( + renderItem={(props: LaceNotification) => ( - + onClick?.(props.message.id)} + /> )} /> diff --git a/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts b/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts index 63c2418c9..7b5bcf511 100644 --- a/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts +++ b/apps/browser-extension-wallet/src/hooks/useNotificationsCenter.ts @@ -31,34 +31,13 @@ export const useNotificationsCenter = () => { [notifications] ); - const mappedNotifications = useMemo( - () => - notifications?.map((template) => ({ - id: template.message.id, - title: template.message.title, - publisher: template.message.topic, - isRead: template.read, - onClick: () => { - // eslint-disable-next-line no-console - console.log(`Clicked notification ${template.message.id}`); - } - })), - [notifications] - ); - return { - notifications: mappedNotifications, + notifications, unreadNotifications, topics, subscribe, unsubscribe, markAsRead, - remove, - // TODO: Implement loadMore and isLoading - loadMore: () => { - // eslint-disable-next-line no-console - console.log('loadMore'); - }, - isLoading: false + remove }; }; diff --git a/packages/core/src/ui/assets/icons/happy-face.component.svg b/packages/core/src/ui/assets/icons/happy-face.component.svg deleted file mode 100644 index db65b6e56..000000000 --- a/packages/core/src/ui/assets/icons/happy-face.component.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/core/src/ui/assets/icons/notifications-bell.component.svg b/packages/core/src/ui/assets/icons/notifications-bell.component.svg deleted file mode 100644 index 5be1a0d39..000000000 --- a/packages/core/src/ui/assets/icons/notifications-bell.component.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/packages/translation/src/lib/translations/browser-extension-wallet/en.json b/packages/translation/src/lib/translations/browser-extension-wallet/en.json index de110382e..351c3e91d 100644 --- a/packages/translation/src/lib/translations/browser-extension-wallet/en.json +++ b/packages/translation/src/lib/translations/browser-extension-wallet/en.json @@ -783,8 +783,6 @@ "multiWallet.errorDialog.notDetectedError.trezorDescription": "Please make sure your device is unlocked.", "multiWallet.popupHwAccountEnable": "Hardware wallets require the <0>expanded view to enable accounts", "multiWallet.walletAlreadyExists": "Wallet already exists", - "notificationsCenter.allClear.title": "All clear", - "notificationsCenter.allClear.description": "You don't have any notifications currently", "notificationsCenter.manageSubscriptions": "Manage subscriptions", "notificationsCenter.markAllAsRead": "Mark all as read", "notificationsCenter.title": "Notifications", From 87356f93347e99dfcde6811a6aa2188921e5d403 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Tue, 7 Oct 2025 13:23:55 +0300 Subject: [PATCH 3/3] fixup! feat: implement notification list component --- .../src/components/MainMenu/MainHeader.tsx | 2 +- .../NotificationsCenter.tsx | 95 --------------- .../NotificationsCenterContainer.tsx | 5 - .../NotificationsCenterLayout.tsx | 28 ----- .../components/NotificationsCenter/index.ts | 3 - .../EmptyState.module.scss | 0 .../notifications-center}/EmptyState.tsx | 0 .../NotificationDetails.tsx | 38 ++++++ .../NotificationListItem.module.scss | 0 .../NotificationListItem.tsx | 0 .../NotificationsAllClear.module.scss | 0 .../NotificationsBell.module.scss | 0 .../NotificationsBell.tsx | 0 .../NotificationsBellContainer.tsx | 4 +- .../NotificationsCenter.tsx} | 6 +- .../NotificationsDropDown.module.scss | 0 .../NotificationsDropDown.tsx | 15 ++- .../NotificationsList.module.scss | 0 .../NotificationsList.tsx | 1 + .../features/notifications-center/index.ts | 3 + .../src/routes/ExtensionRoutes.tsx | 5 +- .../src/routes/wallet-paths.ts | 1 + .../components/MainMenu/MainHeader.tsx | 2 +- .../components/Layout/SidePanel.tsx | 2 +- .../components/SideMenu/SideMenu.tsx | 2 +- .../NotificationDetails.tsx | 49 ++++++++ .../NotificationsCenter.module.scss | 4 +- .../NotificationsCenter.tsx | 110 ++++++++++++++++++ .../features/notifications-center/index.ts | 2 + .../src/views/browser-view/routes/index.tsx | 10 +- 30 files changed, 243 insertions(+), 144 deletions(-) delete mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx delete mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx delete mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterLayout.tsx delete mode 100644 apps/browser-extension-wallet/src/components/NotificationsCenter/index.ts rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/EmptyState.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/EmptyState.tsx (100%) create mode 100644 apps/browser-extension-wallet/src/features/notifications-center/NotificationDetails.tsx rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationListItem.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationListItem.tsx (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsAllClear.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsBell.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsBell.tsx (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsBellContainer.tsx (89%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter/Notifications.tsx => features/notifications-center/NotificationsCenter.tsx} (95%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsDropDown.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsDropDown.tsx (84%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsList.module.scss (100%) rename apps/browser-extension-wallet/src/{components/NotificationsCenter => features/notifications-center}/NotificationsList.tsx (99%) create mode 100644 apps/browser-extension-wallet/src/features/notifications-center/index.ts create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationDetails.tsx rename apps/browser-extension-wallet/src/{components/NotificationsCenter => views/browser-view/features/notifications-center}/NotificationsCenter.module.scss (76%) create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/index.ts diff --git a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx index eb30aeb53..40e008d5f 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx +++ b/apps/browser-extension-wallet/src/components/MainMenu/MainHeader.tsx @@ -13,7 +13,7 @@ import { BrowserViewSections } from '@lib/scripts/types'; import { NetworkPill } from '@components/NetworkPill'; import { useAnalyticsContext } from '@providers'; import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; -import { NotificationsBellContainer } from '@components/NotificationsCenter'; +import { NotificationsBellContainer } from '@src/features/notifications-center'; export const MainHeader = (): React.ReactElement => { const { t } = useTranslation(); diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx deleted file mode 100644 index 6acfff467..000000000 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* eslint-disable unicorn/no-useless-undefined */ -import React, { useState } from 'react'; -import { useTranslation } from 'react-i18next'; -import { Box, Flex } from '@input-output-hk/lace-ui-toolkit'; - -import { Button, NavigationButton } from '@lace/common'; -import { SectionTitle } from '@components/Layout/SectionTitle'; -import { NotificationsList } from './NotificationsList'; -import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; - -import styles from './NotificationsCenter.module.scss'; -import { WarningModal } from '@src/views/browser-view/components/WarningModal/WarningModal'; -import { LACE_APP_ID } from '@src/utils/constants'; -import { EmptyState } from './EmptyState'; -import { useHistory } from 'react-router'; - -export const NotificationsCenter = (): React.ReactElement => { - const { t } = useTranslation(); - const history = useHistory(); - const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); - const { notifications, markAsRead, remove, unreadNotifications } = useNotificationsCenter(); - const [notificationIdToRemove, setNotificationIdToRemove] = useState(); - - const onBack = () => { - history.goBack(); - }; - - const onShowRemoveNotificationModal = (id: string) => { - setNotificationIdToRemove(id); - setIsRemoveNotificationModalVisible(true); - }; - - const onHideRemoveNotificationModal = () => { - setNotificationIdToRemove(undefined); - setIsRemoveNotificationModalVisible(false); - }; - - return ( - <> - { - remove(notificationIdToRemove); - onHideRemoveNotificationModal(); - }} - /> -
- - - - - - {t('notificationsCenter.title')} - - } - /> - - {unreadNotifications > 0 && ( - - )} - - - {notifications?.length > 0 ? ( - - ) : ( - - - - )} -
- - ); -}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx deleted file mode 100644 index f9c9e37c0..000000000 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterContainer.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react'; - -import { NotificationsCenter } from './NotificationsCenter'; - -export const NotificationsCenterContainer = (): React.ReactElement => ; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterLayout.tsx b/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterLayout.tsx deleted file mode 100644 index e5d605348..000000000 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenterLayout.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React from 'react'; - -import { Layout, SectionLayout } from '@src/views/browser-view/components/Layout'; -import { EducationalList } from '@src/views/browser-view/components'; -import { useTranslation } from 'react-i18next'; -import { getEducationalList } from '@src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList'; - -import { NotificationsCenterContainer } from './NotificationsCenterContainer'; - -export const NotificationsCenterLayout = (): React.ReactElement => { - const { t } = useTranslation(); - - const educationalItems = getEducationalList(t); - - return ( - <> - - - } - > - - - - - ); -}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/index.ts b/apps/browser-extension-wallet/src/components/NotificationsCenter/index.ts deleted file mode 100644 index 4fd2a8cf0..000000000 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './NotificationsBellContainer'; -export * from './NotificationsCenterContainer'; -export * from './NotificationsCenterLayout'; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss b/apps/browser-extension-wallet/src/features/notifications-center/EmptyState.module.scss similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.module.scss rename to apps/browser-extension-wallet/src/features/notifications-center/EmptyState.module.scss diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx b/apps/browser-extension-wallet/src/features/notifications-center/EmptyState.tsx similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/EmptyState.tsx rename to apps/browser-extension-wallet/src/features/notifications-center/EmptyState.tsx diff --git a/apps/browser-extension-wallet/src/features/notifications-center/NotificationDetails.tsx b/apps/browser-extension-wallet/src/features/notifications-center/NotificationDetails.tsx new file mode 100644 index 000000000..87249bec3 --- /dev/null +++ b/apps/browser-extension-wallet/src/features/notifications-center/NotificationDetails.tsx @@ -0,0 +1,38 @@ +/* eslint-disable unicorn/no-useless-undefined */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types, promise/catch-or-return, sonarjs/cognitive-complexity, no-magic-numbers, unicorn/no-null */ +import React from 'react'; +import { ContentLayout } from '@src/components/Layout'; +import { useTranslation } from 'react-i18next'; +import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; +import { Flex } from '@input-output-hk/lace-ui-toolkit'; +import { NavigationButton } from '@lace/common'; +import { useHistory } from 'react-router'; +import { SectionTitle } from '@components/Layout/SectionTitle'; + +export const NotificationDetails = (): React.ReactElement => { + const { t } = useTranslation(); + const { unreadNotifications } = useNotificationsCenter(); + const history = useHistory(); + + return ( + + history.goBack()} /> + {t('notificationsCenter.title')} + + } + sideText={`(${unreadNotifications})`} + data-testid="notifications-details-title" + /> + } + > +
+

Notification Details

+
+
+ ); +}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.module.scss b/apps/browser-extension-wallet/src/features/notifications-center/NotificationListItem.module.scss similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.module.scss rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationListItem.module.scss diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx b/apps/browser-extension-wallet/src/features/notifications-center/NotificationListItem.tsx similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationListItem.tsx rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationListItem.tsx diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.module.scss b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsAllClear.module.scss similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsAllClear.module.scss rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationsAllClear.module.scss diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsBell.module.scss similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.module.scss rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationsBell.module.scss diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsBell.tsx similarity index 100% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBell.tsx rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationsBell.tsx diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsBellContainer.tsx similarity index 89% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationsBellContainer.tsx index beaa7f75d..0d7f71b91 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsBellContainer.tsx +++ b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsBellContainer.tsx @@ -33,7 +33,9 @@ export const NotificationsBellContainer = ({ popupView }: NotificationsCenterCon } + dropdownRender={() => ( + setIsOpen(false)} /> + )} placement="bottomRight" trigger={['click']} > diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsCenter.tsx similarity index 95% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx rename to apps/browser-extension-wallet/src/features/notifications-center/NotificationsCenter.tsx index ad0610904..a089a925e 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/Notifications.tsx +++ b/apps/browser-extension-wallet/src/features/notifications-center/NotificationsCenter.tsx @@ -29,6 +29,10 @@ export const NotificationsCenter = (): React.ReactElement => { setIsRemoveNotificationModalVisible(false); }; + const onGoToNotification = (id: string) => { + history.push(`/notification/${id}`); + }; + const isInitialLoad = typeof notifications === 'undefined'; return ( @@ -51,7 +55,7 @@ export const NotificationsCenter = (): React.ReactElement => {
{notifications?.length > 0 ? ( void; popupView?: boolean; + onClose: () => void; } -export const NotificationsDropDown = ({ onViewAll, popupView }: NotificationsDropDownProps): React.ReactElement => { +export const NotificationsDropDown = ({ + onViewAll, + popupView, + onClose +}: NotificationsDropDownProps): React.ReactElement => { const { t } = useTranslation(); const { notifications, markAsRead, unreadNotifications } = useNotificationsCenter(); + const history = useHistory(); + + const onGoToNotification = (id: string) => { + history.push(`/notification/${id}`); + onClose(); + }; return ( @@ -28,6 +40,7 @@ export const NotificationsDropDown = ({ onViewAll, popupView }: NotificationsDro > {notifications?.length > 0 ? ( onClick?.(props.message.id)} /> diff --git a/apps/browser-extension-wallet/src/features/notifications-center/index.ts b/apps/browser-extension-wallet/src/features/notifications-center/index.ts new file mode 100644 index 000000000..f306791ba --- /dev/null +++ b/apps/browser-extension-wallet/src/features/notifications-center/index.ts @@ -0,0 +1,3 @@ +export * from './NotificationsBellContainer'; +export * from './NotificationsCenter'; +export * from './NotificationDetails'; diff --git a/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx b/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx index 20ea6161b..4790404be 100644 --- a/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx +++ b/apps/browser-extension-wallet/src/routes/ExtensionRoutes.tsx @@ -15,7 +15,7 @@ import { NftDetail, Nfts } from '@src/features/nfts'; import { useWalletStore } from '@stores'; import { config } from '@src/config'; import { Voting } from '@src/features/voting-beta/components'; -import { NotificationsCenter } from '@src/components/NotificationsCenter/Notifications'; +import { NotificationsCenter, NotificationDetails } from '@src/features/notifications-center'; import { useNotificationsCenterConfig } from '@hooks/useNotificationsCenterConfig'; const { GOV_TOOLS_URLS } = config(); @@ -36,6 +36,9 @@ export const ExtensionRoutes = (): React.ReactElement => { {isNotificationsCenterEnabled && ( )} + {isNotificationsCenterEnabled && ( + + )} {!isSharedWallet && } diff --git a/apps/browser-extension-wallet/src/routes/wallet-paths.ts b/apps/browser-extension-wallet/src/routes/wallet-paths.ts index 275968f56..a95e7d765 100644 --- a/apps/browser-extension-wallet/src/routes/wallet-paths.ts +++ b/apps/browser-extension-wallet/src/routes/wallet-paths.ts @@ -6,6 +6,7 @@ export const walletRoutePaths = { delegate: '/delegate', earn: '/earn', notifications: '/notifications', + notification: '/notification/:id', nftDetail: '/nft/:id', nfts: '/nfts', assets: '/assets', diff --git a/apps/browser-extension-wallet/src/views/bitcoin-mode/components/MainMenu/MainHeader.tsx b/apps/browser-extension-wallet/src/views/bitcoin-mode/components/MainMenu/MainHeader.tsx index a38ec54af..b22682e6f 100644 --- a/apps/browser-extension-wallet/src/views/bitcoin-mode/components/MainMenu/MainHeader.tsx +++ b/apps/browser-extension-wallet/src/views/bitcoin-mode/components/MainMenu/MainHeader.tsx @@ -13,7 +13,7 @@ import { useAnalyticsContext, useBackgroundServiceAPIContext, useTheme } from '@ import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; import { useTranslation } from 'react-i18next'; import { BrowserViewSections } from '@lib/scripts/types'; -import { NotificationsBellContainer } from '@components/NotificationsCenter/NotificationsBellContainer'; +import { NotificationsBellContainer } from '@src/features/notifications-center/NotificationsBellContainer'; export const MainHeader = (): React.ReactElement => { const { t } = useTranslation(); diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/Layout/SidePanel.tsx b/apps/browser-extension-wallet/src/views/browser-view/components/Layout/SidePanel.tsx index 103e4dd1f..0089988e5 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/components/Layout/SidePanel.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/components/Layout/SidePanel.tsx @@ -9,7 +9,7 @@ import styles from './SectionLayout.modules.scss'; import { SidePanelButton } from '../SidePanelButton/SidePanelButton'; import { CollapsiblePanelContainer } from '../CollapsiblePanelContainer/CollapsiblePanelContainer'; import { BREAKPOINT_SMALL } from '@src/styles/constants'; -import { NotificationsBellContainer } from '@components/NotificationsCenter'; +import { NotificationsBellContainer } from '@src/features/notifications-center'; export const CONTENT_ID = 'content'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/SideMenu/SideMenu.tsx b/apps/browser-extension-wallet/src/views/browser-view/components/SideMenu/SideMenu.tsx index d50b7a11c..9e5666c7e 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/components/SideMenu/SideMenu.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/components/SideMenu/SideMenu.tsx @@ -33,7 +33,7 @@ export const SideMenu = (): React.ReactElement => { const [currentHoveredItem, setCurrentHoveredItem] = useState(); - const [tab, setTab] = useState(isPathAvailable(pathname) ? pathname : routes.assets); + const [tab, setTab] = useState(isPathAvailable(pathname) ? pathname : undefined); useEffect(() => { const unregisterListener = listen((location) => { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationDetails.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationDetails.tsx new file mode 100644 index 000000000..885c1a5d2 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationDetails.tsx @@ -0,0 +1,49 @@ +import React from 'react'; + +import { Layout, SectionLayout } from '@src/views/browser-view/components/Layout'; +import { EducationalList } from '@src/views/browser-view/components'; +import { useTranslation } from 'react-i18next'; +import { getEducationalList } from '@src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList'; + +import { Box, Flex } from '@input-output-hk/lace-ui-toolkit'; +import { SectionTitle } from '@components/Layout/SectionTitle'; +import { NavigationButton } from '@lace/common'; +import { useHistory } from 'react-router'; +import styles from './NotificationsCenter.module.scss'; + +export const NotificationDetails = (): React.ReactElement => { + const { t } = useTranslation(); + const history = useHistory(); + const educationalItems = getEducationalList(t); + + const onBack = () => { + history.goBack(); + }; + + return ( + <> + + + } + > + + + + {t('notificationsCenter.title')} + + } + /> + +
+

Notification Details

+
+
+
+ + ); +}; diff --git a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.module.scss similarity index 76% rename from apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss rename to apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.module.scss index 8dd669bc8..8e3eccf9f 100644 --- a/apps/browser-extension-wallet/src/components/NotificationsCenter/NotificationsCenter.module.scss +++ b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.module.scss @@ -1,5 +1,5 @@ -@import '../../../../../packages/core/src/ui/styles/theme.scss'; -@import '../../../../../packages/common/src/ui/styles/theme.scss'; +@import '../../../../../../../packages/core/src/ui/styles/theme.scss'; +@import '../../../../../../../packages/common/src/ui/styles/theme.scss'; .button { max-width: size_unit(25); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.tsx new file mode 100644 index 000000000..6f8cb1fc1 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/NotificationsCenter.tsx @@ -0,0 +1,110 @@ +/* eslint-disable unicorn/no-useless-undefined */ +import React, { useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Box, Flex } from '@input-output-hk/lace-ui-toolkit'; + +import { Button, NavigationButton } from '@lace/common'; +import { SectionTitle } from '@components/Layout/SectionTitle'; +import { NotificationsList } from '@src/features/notifications-center/NotificationsList'; +import { useNotificationsCenter } from '@hooks/useNotificationsCenter'; + +import styles from './NotificationsCenter.module.scss'; +import { WarningModal } from '@src/views/browser-view/components/WarningModal/WarningModal'; +import { LACE_APP_ID } from '@src/utils/constants'; +import { EmptyState } from '@src/features/notifications-center/EmptyState'; +import { useHistory } from 'react-router'; +import { Layout, SectionLayout } from '../../components/Layout'; +import { EducationalList } from '../../components'; +import { getEducationalList } from '@src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList'; + +export const NotificationsCenter = (): React.ReactElement => { + const { t } = useTranslation(); + const history = useHistory(); + const educationalItems = getEducationalList(t); + const [isRemoveNotificationModalVisible, setIsRemoveNotificationModalVisible] = useState(false); + const { notifications, markAsRead, remove, unreadNotifications } = useNotificationsCenter(); + const [notificationIdToRemove, setNotificationIdToRemove] = useState(); + + const onBack = () => { + history.goBack(); + }; + + const onShowRemoveNotificationModal = (id: string) => { + setNotificationIdToRemove(id); + setIsRemoveNotificationModalVisible(true); + }; + + const onHideRemoveNotificationModal = () => { + setNotificationIdToRemove(undefined); + setIsRemoveNotificationModalVisible(false); + }; + + const goToNotification = (id: string) => { + history.push(`/notification/${id}`); + }; + + return ( + + + } + > + { + remove(notificationIdToRemove); + onHideRemoveNotificationModal(); + }} + /> +
+ + + + + + {t('notificationsCenter.title')} + + } + /> + + {unreadNotifications > 0 && ( + + )} + + + {notifications?.length > 0 ? ( + + ) : ( + + + + )} +
+
+
+ ); +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/index.ts b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/index.ts new file mode 100644 index 000000000..bc8695d3c --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/notifications-center/index.ts @@ -0,0 +1,2 @@ +export * from './NotificationsCenter'; +export * from './NotificationDetails'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx b/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx index b727234c7..009117b76 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/routes/index.tsx @@ -47,8 +47,8 @@ import { catchAndBrandExtensionApiError } from '@utils/catch-and-brand-extension import { removePreloaderIfExists } from '@utils/remove-reloader-if-exists'; import { ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY } from '@providers/AnalyticsProvider/config'; import { EnhancedAnalyticsOptInStatus } from '@providers/AnalyticsProvider/analyticsTracker'; -import { NotificationsCenterLayout } from '@components/NotificationsCenter'; import { useNotificationsCenterConfig } from '@hooks/useNotificationsCenterConfig'; +import { NotificationDetails, NotificationsCenter } from '../features/notifications-center'; export const defaultRoutes: RouteMap = [ { @@ -89,7 +89,11 @@ export const defaultRoutes: RouteMap = [ }, { path: routes.notifications, - component: NotificationsCenterLayout + component: NotificationsCenter + }, + { + path: routes.notification, + component: NotificationDetails } ]; @@ -154,7 +158,7 @@ export const BrowserViewRoutes = ({ routesMap = defaultRoutes }: { routesMap?: R const availableRoutes = routesMap.filter((route) => { if (route.path === routes.staking && isSharedWallet) return false; if (route.path === routes.voting && !isVotingCenterEnabled) return false; - if (route.path === routes.notifications && !isNotificationsCenterEnabled) return false; + if ([routes.notifications, routes.notification].includes(route.path) && !isNotificationsCenterEnabled) return false; return true; });