diff --git a/src/components/Nav/Notifications.tsx b/src/components/Nav/Notifications.tsx
deleted file mode 100644
index 4a53736d..00000000
--- a/src/components/Nav/Notifications.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import { Portal } from 'solid-js/web'
-import { useWarningsStore } from '../../stores/ui'
-import { createMemo, For, Show } from 'solid-js'
-
-export default () => {
- const { warnings } = useWarningsStore()
-
- const notSeen = createMemo(() => warnings().filter((warning) => !warning.seen))
-
- return (
-
0}>
-
-
- {(warning) => - {warning.body}
}
-
-
-
- )
-}
diff --git a/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss b/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss
new file mode 100644
index 00000000..284af187
--- /dev/null
+++ b/src/components/NotificationsPanel/NotificationView/NotificationView.module.scss
@@ -0,0 +1,32 @@
+.NotificationView {
+ display: flex;
+ align-items: center;
+ height: 72px;
+ margin-left: -16px;
+ border-radius: 16px;
+ padding: 16px;
+ background-color: var(--yellow-50);
+ // TODO: check markup
+ font-size: 15px;
+ // font-weight: 700;
+ line-height: 20px;
+ cursor: pointer;
+ transition: background-color 100ms;
+
+ &.seen {
+ background-color: transparent;
+ }
+
+ &:hover {
+ background-color: var(--gray-100);
+ }
+}
+
+.userpic {
+ margin-right: 15px;
+}
+
+.timeContainer {
+ margin-left: auto;
+ padding-left: 16px;
+}
diff --git a/src/components/NotificationsPanel/NotificationView/NotificationView.tsx b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx
new file mode 100644
index 00000000..397572fb
--- /dev/null
+++ b/src/components/NotificationsPanel/NotificationView/NotificationView.tsx
@@ -0,0 +1,128 @@
+import { clsx } from 'clsx'
+import styles from './NotificationView.module.scss'
+import type { Notification } from '../../../graphql/types.gen'
+import { formatDate } from '../../../utils'
+import { createMemo, createSignal, onMount, Show } from 'solid-js'
+import { NotificationType } from '../../../graphql/types.gen'
+import { openPage } from '@nanostores/router'
+import { router } from '../../../stores/router'
+import { useNotifications } from '../../../context/notifications'
+import { Userpic } from '../../Author/Userpic'
+import { useLocalize } from '../../../context/localize'
+import notifications from '../../../graphql/query/notifications'
+
+type Props = {
+ notification: Notification
+ onClick: () => void
+ class?: string
+}
+
+type NotificationData = {
+ shout: {
+ slug: string
+ title: string
+ }
+ users: {
+ id: number
+ name: string
+ slug: string
+ userpic: string
+ }[]
+}
+
+export const NotificationView = (props: Props) => {
+ const {
+ actions: { markNotificationAsRead }
+ } = useNotifications()
+
+ const { t } = useLocalize()
+
+ const [data, setData] = createSignal
(null)
+
+ onMount(() => {
+ setTimeout(() => setData(JSON.parse(props.notification.data)))
+ })
+
+ const lastUser = createMemo(() => {
+ if (!data()) {
+ return null
+ }
+
+ return data().users[data().users.length - 1]
+ })
+
+ const content = createMemo(() => {
+ if (!data()) {
+ return null
+ }
+
+ let shoutTitle = ''
+ let i = 0
+ const shoutTitleWords = data().shout.title.split(' ')
+
+ while (shoutTitle.length <= 30 && i < shoutTitleWords.length) {
+ shoutTitle += shoutTitleWords[i] + ' '
+ i++
+ }
+
+ if (shoutTitle.length < data().shout.title.length) {
+ shoutTitle += '...'
+ }
+
+ switch (props.notification.type) {
+ case NotificationType.NewComment: {
+ return t('NewCommentNotificationText', {
+ commentsCount: props.notification.occurrences,
+ shoutTitle,
+ lastCommenterName: lastUser().name,
+ restUsersCount: data().users.length - 1
+ })
+ }
+ case NotificationType.NewReply: {
+ return t('NewReplyNotificationText', {
+ commentsCount: props.notification.occurrences,
+ shoutTitle,
+ lastCommenterName: lastUser().name,
+ restUsersCount: data().users.length - 1
+ })
+ }
+ }
+ })
+
+ const handleClick = () => {
+ if (!props.notification.seen) {
+ markNotificationAsRead(props.notification)
+ }
+
+ openPage(router, 'article', { slug: data().shout.slug })
+ props.onClick()
+
+ // switch (props.notification.type) {
+ // case NotificationType.NewComment: {
+ // openPage(router, 'article', { slug: data().shout.slug })
+ // break
+ // }
+ // case NotificationType.NewReply: {
+ // openPage(router, 'article', { slug: data().shout.slug })
+ // break
+ // }
+ // }
+ }
+
+ return (
+
+
+
+
{content()}
+
+ {/*{formatDate(new Date(props.notification.createdAt), { month: 'numeric' })}*/}
+
+
+
+ )
+}
diff --git a/src/components/NotificationsPanel/NotificationView/index.ts b/src/components/NotificationsPanel/NotificationView/index.ts
new file mode 100644
index 00000000..59dcafc6
--- /dev/null
+++ b/src/components/NotificationsPanel/NotificationView/index.ts
@@ -0,0 +1 @@
+export { NotificationView } from './NotificationView'
diff --git a/src/components/NotificationsPanel/NotificationsPanel.module.scss b/src/components/NotificationsPanel/NotificationsPanel.module.scss
new file mode 100644
index 00000000..a1b18dd0
--- /dev/null
+++ b/src/components/NotificationsPanel/NotificationsPanel.module.scss
@@ -0,0 +1,66 @@
+$transition-duration: 200ms;
+
+.container {
+ display: flex;
+ align-items: stretch;
+ justify-content: flex-end;
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: 0;
+ z-index: 10000;
+ background-color: rgb(0 0 0 / 0%);
+ overflow: hidden;
+ transition:
+ background-color $transition-duration,
+ width 0ms linear $transition-duration;
+
+ .panel {
+ position: relative;
+ background-color: #fff;
+ width: 700px;
+ padding: 48px 96px 96px 48px;
+ transform: translateX(100%);
+ transition: transform $transition-duration;
+ overflow-y: auto;
+ }
+
+ &.isOpened {
+ width: 100%;
+ background-color: rgb(0 0 0 / 60%);
+ transition:
+ background-color $transition-duration,
+ width 0ms;
+
+ .panel {
+ transform: translateX(0);
+ }
+ }
+}
+
+.title {
+ // TODO: check markup
+ color: var(--black-500, #141414);
+ font-size: 32px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: 36px;
+ margin-bottom: 32px;
+}
+
+.closeButton {
+ position: absolute;
+ top: 0;
+ right: 0;
+ padding: 20px;
+ cursor: pointer;
+}
+
+.notificationView + .notificationView {
+ margin-top: 8px;
+}
+
+.emptyMessageContainer {
+ text-align: center;
+}
diff --git a/src/components/NotificationsPanel/NotificationsPanel.tsx b/src/components/NotificationsPanel/NotificationsPanel.tsx
new file mode 100644
index 00000000..ddfc66b2
--- /dev/null
+++ b/src/components/NotificationsPanel/NotificationsPanel.tsx
@@ -0,0 +1,70 @@
+import { clsx } from 'clsx'
+import styles from './NotificationsPanel.module.scss'
+import { useEscKeyDownHandler } from '../../utils/useEscKeyDownHandler'
+import { useOutsideClickHandler } from '../../utils/useOutsideClickHandler'
+import { useLocalize } from '../../context/localize'
+import { Icon } from '../_shared/Icon'
+import { createEffect, For, onCleanup, onMount } from 'solid-js'
+import { useNotifications } from '../../context/notifications'
+import { NotificationView } from './NotificationView'
+
+type Props = {
+ isOpen: boolean
+ onClose: () => void
+}
+
+export const NotificationsPanel = (props: Props) => {
+ const { t } = useLocalize()
+ const { sortedNotifications } = useNotifications()
+ const handleHide = () => {
+ props.onClose()
+ }
+
+ const panelRef: { current: HTMLDivElement } = {
+ current: null
+ }
+
+ useOutsideClickHandler({
+ containerRef: panelRef,
+ predicate: () => props.isOpen,
+ handler: () => handleHide()
+ })
+
+ createEffect(() => {
+ document.body.classList.toggle('fixed', props.isOpen)
+ })
+
+ useEscKeyDownHandler(handleHide)
+
+ const handleNotificationViewClick = () => {
+ handleHide()
+ }
+
+ return (
+
+
(panelRef.current = el)} class={styles.panel}>
+
+ {/*TODO: check markup (hover)*/}
+
+
+
{t('Notifications')}
+
{t('No notifications, yet')}}
+ >
+ {(notification) => (
+
+ )}
+
+
+
+ )
+}
diff --git a/src/components/NotificationsPanel/index.ts b/src/components/NotificationsPanel/index.ts
new file mode 100644
index 00000000..5d134561
--- /dev/null
+++ b/src/components/NotificationsPanel/index.ts
@@ -0,0 +1 @@
+export { NotificationsPanel } from './NotificationsPanel'
diff --git a/src/components/Views/Author/Author.tsx b/src/components/Views/Author/Author.tsx
index 4c4399af..027d260d 100644
--- a/src/components/Views/Author/Author.tsx
+++ b/src/components/Views/Author/Author.tsx
@@ -34,7 +34,7 @@ export const AuthorView = (props: Props) => {
const { sortedArticles } = useArticlesStore({ shouts: props.shouts })
const { authorEntities } = useAuthorsStore({ authors: [props.author] })
- const { page } = useRouter()
+ const { page: getPage } = useRouter()
const { user } = useSession()
const author = createMemo(() => authorEntities()[props.authorSlug])
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
@@ -104,14 +104,14 @@ export const AuthorView = (props: Props) => {
// return t('Top recent')
// })
- const shouts = createMemo