From 7eb94331d97d4e070cf3ff538cd4089d53f4ce07 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 10 Mar 2023 18:26:17 +0600 Subject: [PATCH] notifications: added list to profile --- .../common/InlineUsername/index.tsx | 20 ++++ .../common/InlineUsername/styles.module.scss | 6 + .../notifications/NotificationBadge/index.tsx | 77 +++++++++++++ .../NotificationBadge/styles.module.scss | 33 ++++++ .../NotificationBubble/index.tsx | 47 -------- .../NotificationBubble/styles.module.scss | 106 ------------------ .../NotificationMessage/index.tsx | 32 ------ .../ProfileSidebarNotes/styles.module.scss | 7 -- .../ProfileSidebarNotifications/index.tsx | 27 +++++ .../notifications/NotificationList/index.tsx | 34 ++++++ .../NotificationList/styles.module.scss | 41 +++++++ .../profile/ProfileSidebarMenu/index.tsx | 5 + .../sidebars/ProfileSidebar/index.tsx | 4 +- src/utils/dom.ts | 10 +- 14 files changed, 253 insertions(+), 196 deletions(-) create mode 100644 src/components/common/InlineUsername/index.tsx create mode 100644 src/components/common/InlineUsername/styles.module.scss create mode 100644 src/components/notifications/NotificationBadge/index.tsx create mode 100644 src/components/notifications/NotificationBadge/styles.module.scss delete mode 100644 src/components/notifications/NotificationBubble/index.tsx delete mode 100644 src/components/notifications/NotificationBubble/styles.module.scss delete mode 100644 src/components/notifications/NotificationMessage/index.tsx delete mode 100644 src/components/profile/ProfileSidebarNotes/styles.module.scss create mode 100644 src/components/profile/ProfileSidebarNotifications/index.tsx create mode 100644 src/containers/notifications/NotificationList/index.tsx create mode 100644 src/containers/notifications/NotificationList/styles.module.scss diff --git a/src/components/common/InlineUsername/index.tsx b/src/components/common/InlineUsername/index.tsx new file mode 100644 index 00000000..1bfb3124 --- /dev/null +++ b/src/components/common/InlineUsername/index.tsx @@ -0,0 +1,20 @@ +import React, { FC } from 'react'; + +import { useColorFromString } from '~/hooks/color/useColorFromString'; + +import styles from './styles.module.scss'; + +interface InlineUsernameProps { + children: string; +} + +const InlineUsername: FC = ({ children }) => { + const backgroundColor = useColorFromString(children); + return ( + + ~{children} + + ); +}; + +export { InlineUsername }; diff --git a/src/components/common/InlineUsername/styles.module.scss b/src/components/common/InlineUsername/styles.module.scss new file mode 100644 index 00000000..552c701a --- /dev/null +++ b/src/components/common/InlineUsername/styles.module.scss @@ -0,0 +1,6 @@ +.username { + font-size: 0.9em; + padding: 0 2px; + text-transform: lowercase; + border-radius: 0.2em; +} diff --git a/src/components/notifications/NotificationBadge/index.tsx b/src/components/notifications/NotificationBadge/index.tsx new file mode 100644 index 00000000..20c32a29 --- /dev/null +++ b/src/components/notifications/NotificationBadge/index.tsx @@ -0,0 +1,77 @@ +import React, { FC } from 'react'; + +import { Anchor } from '~/components/common/Anchor'; +import { InlineUsername } from '~/components/common/InlineUsername'; +import { Square } from '~/components/common/Square'; +import { Card } from '~/components/containers/Card'; +import { FlowRecentItem } from '~/components/flow/FlowRecentItem'; +import { NotificationItem, NotificationType } from '~/types/notifications'; +import { formatText, getPrettyDate, getURLFromString } from '~/utils/dom'; + +import styles from './styles.module.scss'; + +interface NotificationBadgeProps { + item: NotificationItem; +} + +const getTitle = (item: NotificationItem) => { + if (!item.user.username) { + return ''; + } + + switch (item.type) { + case NotificationType.Comment: + return ( + + {item.user.username} пишет: + + ); + case NotificationType.Node: + return ( + + Новый пост от {item.user.username}: + + ); + } +}; + +const getContent = (item: NotificationItem) => { + switch (item.type) { + case NotificationType.Comment: + return ( +
+ ); + case NotificationType.Node: + return ( +
+ ); + } +}; + +const getIcon = (item: NotificationItem) => { + return ; +}; + +const NotificationBadge: FC = ({ item }) => ( + +
+
{getIcon(item)}
+ +
+ {getTitle(item)} +
{getContent(item)}
+
{getPrettyDate(item.created_at)}
+
+
+
+); + +export { NotificationBadge }; diff --git a/src/components/notifications/NotificationBadge/styles.module.scss b/src/components/notifications/NotificationBadge/styles.module.scss new file mode 100644 index 00000000..8468237e --- /dev/null +++ b/src/components/notifications/NotificationBadge/styles.module.scss @@ -0,0 +1,33 @@ +@import 'src/styles/variables'; + +.link { + text-decoration: none; + color: inherit; +} + +.message { + font: $font_14_regular; + line-height: 1.3em; + padding: $gap/2 $gap/2 $gap/4 $gap/2; + min-height: calc(1.3em * 3 + $gap); + display: grid; + grid-template-columns: 40px auto; + column-gap: $gap; +} + +.text { + @include clamp(2, 14px); + text-overflow: ellipsis; +} + +.title { + font: $font_14_semibold; + margin-bottom: $gap / 2; +} + +.time { + font: $font_10_regular; + text-align: right; + margin-top: 2px; + color: $gray_75; +} diff --git a/src/components/notifications/NotificationBubble/index.tsx b/src/components/notifications/NotificationBubble/index.tsx deleted file mode 100644 index 0ef0d796..00000000 --- a/src/components/notifications/NotificationBubble/index.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import React, { createElement, FC } from 'react'; - -import { Icon } from '~/components/input/Icon'; -import { useRandomPhrase } from '~/constants/phrases'; -import { INotification, NOTIFICATION_TYPES } from '~/types'; - -import { NotificationMessage } from '../NotificationMessage'; - -import styles from './styles.module.scss'; - -interface IProps { - notifications: INotification[]; - onClick: (notification: INotification) => void; -} - -const NOTIFICATION_RENDERERS = { - [NOTIFICATION_TYPES.message]: NotificationMessage, -}; - -const NotificationBubble: FC = ({ notifications, onClick }) => { - const placeholder = useRandomPhrase('NOTHING_HERE'); - - return ( -
-
- {notifications.length === 0 && ( -
- -
{placeholder}
-
- )} - {notifications.length > 0 && - notifications - .filter(notification => notification.type && NOTIFICATION_RENDERERS[notification.type]) - .map(notification => - createElement(NOTIFICATION_RENDERERS[notification.type], { - notification, - onClick, - key: notification.content.id, - }) - )} -
-
- ); -}; - -export { NotificationBubble }; diff --git a/src/components/notifications/NotificationBubble/styles.module.scss b/src/components/notifications/NotificationBubble/styles.module.scss deleted file mode 100644 index d351ef4d..00000000 --- a/src/components/notifications/NotificationBubble/styles.module.scss +++ /dev/null @@ -1,106 +0,0 @@ -@import 'src/styles/variables'; - -$notification_color: $content_bg_dark; - -@keyframes appear { - 0% { - opacity: 0; - } - 100% { - opacity: 1; - } -} - -.wrap { - position: absolute; - background: $notification_color; - top: 42px; - left: 50%; - transform: translate(-50%, 0); - border-radius: $radius; - animation: appear 0.25s forwards; - z-index: 2; - - &::before { - content: ' '; - width: 0; - height: 0; - border-style: solid; - border-width: 0 0 16px 16px; - border-color: transparent transparent $notification_color transparent; - position: absolute; - left: 50%; - top: -16px; - transform: translate(-20px, 0); - } -} - -.list { - width: 300px; - max-width: 100vw; - min-width: 0; - max-height: 400px; - overflow: auto; -} - -.item { - display: flex; - align-items: stretch; - justify-content: stretch; - flex-direction: column; - padding: $gap; - min-width: 0; - cursor: pointer; - - svg { - fill: white; - margin-right: $gap; - } -} - -.item_head { - display: flex; - align-items: center; - justify-content: flex-start; - flex-direction: row; -} - -.item_title { - flex: 1; - white-space: nowrap; - font: $font_14_semibold; - overflow: hidden; - text-overflow: ellipsis; - // text-transform: none; -} - -.item_text { - font: $font_14_regular; - max-height: 2.4em; - padding-left: 30px; - overflow: hidden; -} - -.placeholder { - height: 200px; - display: flex; - align-items: center; - justify-content: center; - flex-direction: column; - text-transform: uppercase; - font: $font_16_semibold; - box-sizing: border-box; - padding: 80px; - text-align: center; - line-height: 1.6em; - - svg { - width: 120px; - height: 120px; - opacity: 0.05; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - } -} diff --git a/src/components/notifications/NotificationMessage/index.tsx b/src/components/notifications/NotificationMessage/index.tsx deleted file mode 100644 index 760d9390..00000000 --- a/src/components/notifications/NotificationMessage/index.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import React, { FC, useCallback } from 'react'; - -import { Icon } from '~/components/input/Icon'; -import styles from '~/components/notifications/NotificationBubble/styles.module.scss'; -import { IMessageNotification, INotification } from '~/types'; - -interface IProps { - notification: IMessageNotification; - onClick: (notification: INotification) => void; -} - -const NotificationMessage: FC = ({ - notification, - notification: { - content: { text, from }, - }, - onClick, -}) => { - const onMouseDown = useCallback(() => onClick(notification), [onClick, notification]); - - return ( -
-
- -
Сообщение от ~{from?.username}:
-
-
{text}
-
- ); -}; - -export { NotificationMessage }; diff --git a/src/components/profile/ProfileSidebarNotes/styles.module.scss b/src/components/profile/ProfileSidebarNotes/styles.module.scss deleted file mode 100644 index 078e0081..00000000 --- a/src/components/profile/ProfileSidebarNotes/styles.module.scss +++ /dev/null @@ -1,7 +0,0 @@ -@import "src/styles/variables"; - -.scroller { - flex: 1; - overflow: auto; - padding: $gap; -} diff --git a/src/components/profile/ProfileSidebarNotifications/index.tsx b/src/components/profile/ProfileSidebarNotifications/index.tsx new file mode 100644 index 00000000..07fc114c --- /dev/null +++ b/src/components/profile/ProfileSidebarNotifications/index.tsx @@ -0,0 +1,27 @@ +import { VFC } from 'react'; + +import { useStackContext } from '~/components/sidebar/SidebarStack'; +import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard'; + +import { NotificationList } from '../../../containers/notifications/NotificationList/index'; + +interface ProfileSidebarNotificationsProps {} + +const ProfileSidebarNotifications: VFC< + ProfileSidebarNotificationsProps +> = () => { + const { closeAllTabs } = useStackContext(); + + return ( + + + + ); +}; + +export { ProfileSidebarNotifications }; diff --git a/src/containers/notifications/NotificationList/index.tsx b/src/containers/notifications/NotificationList/index.tsx new file mode 100644 index 00000000..cd38bd74 --- /dev/null +++ b/src/containers/notifications/NotificationList/index.tsx @@ -0,0 +1,34 @@ +import React, { FC } from 'react'; + +import { LoaderCircle } from '~/components/input/LoaderCircle'; +import { NotificationBadge } from '~/components/notifications/NotificationBadge'; +import { useNotificationsList } from '~/hooks/notifications/useNotificationsList'; + +import styles from './styles.module.scss'; + +interface NotificationListProps {} + +const NotificationList: FC = () => { + const { isLoading, items } = useNotificationsList(); + + if (isLoading) { + return ; + } + + return ( +
+ {/*
HEAD
*/} +
+
+ {items?.map((item) => ( +
+ +
+ ))} +
+
+
+ ); +}; + +export { NotificationList }; diff --git a/src/containers/notifications/NotificationList/styles.module.scss b/src/containers/notifications/NotificationList/styles.module.scss new file mode 100644 index 00000000..594c5406 --- /dev/null +++ b/src/containers/notifications/NotificationList/styles.module.scss @@ -0,0 +1,41 @@ +@import 'src/styles/variables'; + +.grid { + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + z-index: 4; + height: 100%; +} + +.head { + @include row_shadow; + + width: 100%; + padding: $gap; +} + +.list { + @include row_shadow; + + overflow-y: auto; + flex: 1 1; + overflow: auto; + width: 100%; +} + +.items { + display: grid; + grid-auto-flow: row; +} + +.item { + @include row_shadow; + padding-right: $gap; + transition: background-color 0.25s; + + &:hover { + background-color: $content_bg_lighter; + } +} diff --git a/src/containers/profile/ProfileSidebarMenu/index.tsx b/src/containers/profile/ProfileSidebarMenu/index.tsx index 4cf8ae07..a61962d1 100644 --- a/src/containers/profile/ProfileSidebarMenu/index.tsx +++ b/src/containers/profile/ProfileSidebarMenu/index.tsx @@ -2,6 +2,7 @@ import React, { useCallback, VFC } from 'react'; import classNames from 'classnames'; +import { Superpower } from '~/components/boris/Superpower'; import { Filler } from '~/components/containers/Filler'; import { Group } from '~/components/containers/Group'; import { Zone } from '~/components/containers/Zone'; @@ -45,6 +46,10 @@ const ProfileSidebarMenu: VFC = ({ onClose }) => { setActiveTab(1)}> + Уведомления + + + setActiveTab(2)}> Заметки diff --git a/src/containers/sidebars/ProfileSidebar/index.tsx b/src/containers/sidebars/ProfileSidebar/index.tsx index 05ffaeb3..54c21597 100644 --- a/src/containers/sidebars/ProfileSidebar/index.tsx +++ b/src/containers/sidebars/ProfileSidebar/index.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, VFC } from 'react'; import { CoverBackdrop } from '~/components/containers/CoverBackdrop'; import { ProfileSidebarNotes } from '~/components/profile/ProfileSidebarNotes'; +import { ProfileSidebarNotifications } from '~/components/profile/ProfileSidebarNotifications'; import { ProfileSidebarSettings } from '~/components/profile/ProfileSidebarSettings'; import { SidebarStack } from '~/components/sidebar/SidebarStack'; import { SidebarStackCard } from '~/components/sidebar/SidebarStackCard'; @@ -13,7 +14,7 @@ import { useUser } from '~/hooks/auth/useUser'; import type { SidebarComponentProps } from '~/types/sidebar'; import { isNil } from '~/utils/ramda'; -const tabs = ['profile', 'bookmarks'] as const; +const tabs = ['profile', 'notifications', 'bookmarks'] as const; type TabName = typeof tabs[number]; interface SettingsSidebarProps @@ -71,6 +72,7 @@ const SettingsSidebar: VFC = ({ + diff --git a/src/utils/dom.ts b/src/utils/dom.ts index 1bed443a..91098e8c 100644 --- a/src/utils/dom.ts +++ b/src/utils/dom.ts @@ -88,10 +88,8 @@ export const getURL = ( return file?.url ? getURLFromString(file.url, size) : ''; }; -export const formatText = pipe( - formatTextSanitizeYoutube, +export const formatTextWithoutImages = pipe( formatTextComments, - formatTextTodos, formatExclamations, formatTextDash, formatTextMarkdown, @@ -99,6 +97,12 @@ export const formatText = pipe( formatTextClickableUsernames, ); +export const formatText = pipe( + formatTextSanitizeYoutube, + formatTextTodos, + formatTextWithoutImages, +); + export const formatTextParagraphs = (text: string): string => (text && formatText(text)) || '';