grouped-notifications
This commit is contained in:
parent
43b3de572b
commit
d11a50c2a3
|
@ -1,11 +1,10 @@
|
||||||
import { getPagePath, openPage } from '@nanostores/router'
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { createEffect, For, Show } from 'solid-js'
|
import { For, Show } from 'solid-js'
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { useNotifications } from '../../../context/notifications'
|
import { useNotifications } from '../../../context/notifications'
|
||||||
import { Reaction } from '../../../graphql/schema/core.gen'
|
import { NotificationGroup as Group } from '../../../graphql/schema/notifier.gen'
|
||||||
import { Notification } from '../../../graphql/schema/notifier.gen'
|
|
||||||
import { useRouter, router } from '../../../stores/router'
|
import { useRouter, router } from '../../../stores/router'
|
||||||
import { GroupAvatar } from '../../_shared/GroupAvatar'
|
import { GroupAvatar } from '../../_shared/GroupAvatar'
|
||||||
import { TimeAgo } from '../../_shared/TimeAgo'
|
import { TimeAgo } from '../../_shared/TimeAgo'
|
||||||
|
@ -14,7 +13,7 @@ import { ArticlePageSearchParams } from '../../Article/FullArticle'
|
||||||
import styles from './NotificationView.module.scss'
|
import styles from './NotificationView.module.scss'
|
||||||
|
|
||||||
type NotificationGroupProps = {
|
type NotificationGroupProps = {
|
||||||
notifications: Notification[]
|
notifications: Group[]
|
||||||
onClick: () => void
|
onClick: () => void
|
||||||
dateTimeFormat: 'ago' | 'time' | 'date'
|
dateTimeFormat: 'ago' | 'time' | 'date'
|
||||||
class?: string
|
class?: string
|
||||||
|
@ -41,101 +40,23 @@ const getTitle = (title: string) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const reactionsCaption = (threadId: string) =>
|
const reactionsCaption = (threadId: string) =>
|
||||||
threadId.includes('__') ? 'Some new replies to your comment' : 'Some new comments to your publication'
|
threadId.includes('::') ? 'Some new replies to your comment' : 'Some new comments to your publication'
|
||||||
|
|
||||||
export const NotificationGroup = (props: NotificationGroupProps) => {
|
export const NotificationGroup = (props: NotificationGroupProps) => {
|
||||||
const { t, formatTime, formatDate } = useLocalize()
|
const { t, formatTime, formatDate } = useLocalize()
|
||||||
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
||||||
const {
|
const {
|
||||||
actions: { hideNotificationsPanel, markNotificationAsRead },
|
actions: { hideNotificationsPanel, markSeenThread },
|
||||||
} = useNotifications()
|
} = useNotifications()
|
||||||
const threads = new Map<string, Reaction[]>()
|
|
||||||
const notificationsByThread = new Map<string, Notification[]>()
|
|
||||||
|
|
||||||
const handleClick = (threadId: string) => {
|
const handleClick = (threadId: string) => {
|
||||||
props.onClick()
|
props.onClick()
|
||||||
|
|
||||||
notificationsByThread.get(threadId).forEach((n: Notification) => {
|
markSeenThread(threadId)
|
||||||
if (!n.seen) markNotificationAsRead(n)
|
const [slug, commentId] = threadId.split('::')
|
||||||
})
|
openPage(router, 'article', { slug })
|
||||||
|
if (commentId) changeSearchParam({ commentId })
|
||||||
const threadParts = threadId.replace('::seen', '').split('__')
|
|
||||||
openPage(router, 'article', { slug: threadParts[0] })
|
|
||||||
if (threadParts.length > 1) {
|
|
||||||
changeSearchParam({ commentId: threadParts[1] })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createEffect(() => {
|
|
||||||
const threadsLatest = {}
|
|
||||||
|
|
||||||
// count occurencies and sort reactions by threads
|
|
||||||
props.notifications.forEach((n: Notification) => {
|
|
||||||
const reaction = JSON.parse(n.payload)
|
|
||||||
const slug = reaction.shout.slug
|
|
||||||
const timestamp = reaction.created_at
|
|
||||||
// threadId never shows up and looks like <slug>-<reaction_id>
|
|
||||||
const threadId = slug + (reaction.reply_to ? `__${reaction.reply_to}` : '') + (n.seen ? `::seen` : '')
|
|
||||||
const rrr = threads.get(threadId) || []
|
|
||||||
const nnn = notificationsByThread.get(threadId) || []
|
|
||||||
switch (n.entity) {
|
|
||||||
case 'reaction': {
|
|
||||||
switch (n.action) {
|
|
||||||
case 'create': {
|
|
||||||
rrr.push(reaction)
|
|
||||||
threads.set(threadId, rrr)
|
|
||||||
nnn.push(n)
|
|
||||||
notificationsByThread.set(threadId, nnn)
|
|
||||||
if (!(threadId in threadsLatest)) threadsLatest[threadId] = timestamp
|
|
||||||
else if (timestamp > threadsLatest) threadsLatest[threadId] = timestamp
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'delete': {
|
|
||||||
// TODO: remove reaction from thread, update storage
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'update': {
|
|
||||||
// TODO: ignore for thread, update in storage
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// No default
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'shout': {
|
|
||||||
// TODO: handle notification about the
|
|
||||||
// new shout from subscribed author, topic
|
|
||||||
// or community (means all for one community)
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'follower': {
|
|
||||||
// TODO: handle new follower notification
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// bypass chat and messages SSE
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// sort reactions and threads by created_at
|
|
||||||
Object.entries(threads)
|
|
||||||
.sort(([ak, _av], [bk, _bv]) => threadsLatest[bk] - threadsLatest[ak])
|
|
||||||
.forEach(([threadId, reaction], _idx, _arr) => {
|
|
||||||
const rrr = threads.get(threadId) || []
|
|
||||||
if (!rrr.includes(reaction)) {
|
|
||||||
const updatedReactions: Reaction[] = [...rrr, reaction].sort(
|
|
||||||
(a, b) => b.created_at - a.created_at,
|
|
||||||
)
|
|
||||||
threads.set(threadId, updatedReactions)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const handleLinkClick = (event: MouseEvent | TouchEvent) => {
|
const handleLinkClick = (event: MouseEvent | TouchEvent) => {
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
hideNotificationsPanel()
|
hideNotificationsPanel()
|
||||||
|
@ -143,46 +64,39 @@ export const NotificationGroup = (props: NotificationGroupProps) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<For each={[...threads.entries()]}>
|
<For each={props.notifications}>
|
||||||
{([threadId, reactions], _index) => (
|
{(n: Group) => (
|
||||||
<>
|
<>
|
||||||
{t(reactionsCaption(threadId), { commentsCount: reactions.length })}{' '}
|
{t(reactionsCaption(n.id), { commentsCount: n.reactions.length })}{' '}
|
||||||
<div
|
<div
|
||||||
class={clsx(styles.NotificationView, props.class, {
|
class={clsx(styles.NotificationView, props.class, { [styles.seen]: n.seen })}
|
||||||
[styles.seen]: threadId.endsWith('::seen'),
|
onClick={(_) => handleClick(n.id)}
|
||||||
})}
|
|
||||||
onClick={(_) => handleClick(threadId)}
|
|
||||||
>
|
>
|
||||||
<div class={styles.userpic}>
|
<div class={styles.userpic}>
|
||||||
<GroupAvatar authors={reactions.map((r: Reaction) => r.created_by)} />
|
<GroupAvatar authors={n.authors} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a
|
<a href={getPagePath(router, 'article', { slug: n.shout.slug })} onClick={handleLinkClick}>
|
||||||
href={getPagePath(router, 'article', { slug: reactions[-1].shout.slug })}
|
{getTitle(n.shout.title)}
|
||||||
onClick={handleLinkClick}
|
|
||||||
>
|
|
||||||
{getTitle(reactions[-1].shout.title)}
|
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
{t('from')}{' '}
|
{t('from')}{' '}
|
||||||
<a
|
<a
|
||||||
href={getPagePath(router, 'author', { slug: reactions[-1].created_by.slug })}
|
href={getPagePath(router, 'author', { slug: n.authors[0].slug })}
|
||||||
onClick={handleLinkClick}
|
onClick={handleLinkClick}
|
||||||
>
|
>
|
||||||
{reactions[-1].created_by.name}
|
{n.authors[0].name}
|
||||||
</a>{' '}
|
</a>{' '}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={styles.timeContainer}>
|
<div class={styles.timeContainer}>
|
||||||
<Show when={props.dateTimeFormat === 'ago'}>
|
<Show when={props.dateTimeFormat === 'ago'}>
|
||||||
<TimeAgo date={reactions[-1].created_at} />
|
<TimeAgo date={n.updated_at} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={props.dateTimeFormat === 'time'}>
|
<Show when={props.dateTimeFormat === 'time'}>{formatTime(new Date(n.updated_at))}</Show>
|
||||||
{formatTime(new Date(reactions[-1].created_at))}
|
|
||||||
</Show>
|
|
||||||
|
|
||||||
<Show when={props.dateTimeFormat === 'date'}>
|
<Show when={props.dateTimeFormat === 'date'}>
|
||||||
{formatDate(new Date(reactions[-1].created_at), { month: 'numeric', year: '2-digit' })}
|
{formatDate(new Date(n.updated_at), { month: 'numeric', year: '2-digit' })}
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
unreadNotificationsCount,
|
unreadNotificationsCount,
|
||||||
loadedNotificationsCount,
|
loadedNotificationsCount,
|
||||||
totalNotificationsCount,
|
totalNotificationsCount,
|
||||||
actions: { loadNotifications, markAllNotificationsAsRead },
|
actions: { loadNotificationsGrouped, markSeenAll },
|
||||||
} = useNotifications()
|
} = useNotifications()
|
||||||
const handleHide = () => {
|
const handleHide = () => {
|
||||||
props.onClose()
|
props.onClose()
|
||||||
|
@ -96,24 +96,24 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const todayNotifications = createMemo(() => {
|
const todayNotifications = createMemo(() => {
|
||||||
return sortedNotifications().filter((notification) => isToday(new Date(notification.created_at * 1000)))
|
return sortedNotifications().filter((notification) => isToday(new Date(notification.updated_at * 1000)))
|
||||||
})
|
})
|
||||||
|
|
||||||
const yesterdayNotifications = createMemo(() => {
|
const yesterdayNotifications = createMemo(() => {
|
||||||
return sortedNotifications().filter((notification) =>
|
return sortedNotifications().filter((notification) =>
|
||||||
isYesterday(new Date(notification.created_at * 1000)),
|
isYesterday(new Date(notification.updated_at * 1000)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const earlierNotifications = createMemo(() => {
|
const earlierNotifications = createMemo(() => {
|
||||||
return sortedNotifications().filter((notification) =>
|
return sortedNotifications().filter((notification) =>
|
||||||
isEarlier(new Date(notification.created_at * 1000)),
|
isEarlier(new Date(notification.updated_at * 1000)),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const scrollContainerRef: { current: HTMLDivElement } = { current: null }
|
const scrollContainerRef: { current: HTMLDivElement } = { current: null }
|
||||||
const loadNextPage = async () => {
|
const loadNextPage = async () => {
|
||||||
await loadNotifications({ after: after(), limit: PAGE_SIZE, offset: loadedNotificationsCount() })
|
await loadNotificationsGrouped({ after: after(), limit: PAGE_SIZE, offset: loadedNotificationsCount() })
|
||||||
if (loadedNotificationsCount() < totalNotificationsCount()) {
|
if (loadedNotificationsCount() < totalNotificationsCount()) {
|
||||||
const hasMore = scrollContainerRef.current.scrollHeight <= scrollContainerRef.current.offsetHeight
|
const hasMore = scrollContainerRef.current.scrollHeight <= scrollContainerRef.current.offsetHeight
|
||||||
|
|
||||||
|
@ -221,11 +221,7 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
|
|
||||||
<Show when={unreadNotificationsCount() > 0}>
|
<Show when={unreadNotificationsCount() > 0}>
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
<Button
|
<Button onClick={(_e) => markSeenAll()} variant="secondary" value={t('Mark as read')} />
|
||||||
onClick={() => markAllNotificationsAsRead()}
|
|
||||||
variant="secondary"
|
|
||||||
value={t('Mark as read')}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,13 +2,14 @@ import { clsx } from 'clsx'
|
||||||
import { For } from 'solid-js'
|
import { For } from 'solid-js'
|
||||||
|
|
||||||
import { Author } from '../../../graphql/schema/core.gen'
|
import { Author } from '../../../graphql/schema/core.gen'
|
||||||
|
import { NotificationAuthor } from '../../../graphql/schema/notifier.gen'
|
||||||
import { Userpic } from '../../Author/Userpic'
|
import { Userpic } from '../../Author/Userpic'
|
||||||
|
|
||||||
import styles from './GroupAvatar.module.scss'
|
import styles from './GroupAvatar.module.scss'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
class?: string
|
class?: string
|
||||||
authors: Author[]
|
authors: Author[] | NotificationAuthor[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GroupAvatar = (props: Props) => {
|
export const GroupAvatar = (props: Props) => {
|
||||||
|
|
|
@ -8,24 +8,25 @@ import { Portal } from 'solid-js/web'
|
||||||
import { ShowIfAuthenticated } from '../components/_shared/ShowIfAuthenticated'
|
import { ShowIfAuthenticated } from '../components/_shared/ShowIfAuthenticated'
|
||||||
import { NotificationsPanel } from '../components/NotificationsPanel'
|
import { NotificationsPanel } from '../components/NotificationsPanel'
|
||||||
import { notifierClient } from '../graphql/client/notifier'
|
import { notifierClient } from '../graphql/client/notifier'
|
||||||
import { Notification, QueryLoad_NotificationsArgs } from '../graphql/schema/notifier.gen'
|
import { NotificationGroup, QueryLoad_NotificationsArgs } from '../graphql/schema/notifier.gen'
|
||||||
|
|
||||||
import { SSEMessage, useConnect } from './connect'
|
import { SSEMessage, useConnect } from './connect'
|
||||||
import { useSession } from './session'
|
import { useSession } from './session'
|
||||||
|
|
||||||
type NotificationsContextType = {
|
type NotificationsContextType = {
|
||||||
notificationEntities: Record<number, Notification>
|
notificationEntities: Record<string, NotificationGroup>
|
||||||
unreadNotificationsCount: Accessor<number>
|
unreadNotificationsCount: Accessor<number>
|
||||||
after: Accessor<number>
|
after: Accessor<number>
|
||||||
sortedNotifications: Accessor<Notification[]>
|
sortedNotifications: Accessor<NotificationGroup[]>
|
||||||
loadedNotificationsCount: Accessor<number>
|
loadedNotificationsCount: Accessor<number>
|
||||||
totalNotificationsCount: Accessor<number>
|
totalNotificationsCount: Accessor<number>
|
||||||
actions: {
|
actions: {
|
||||||
showNotificationsPanel: () => void
|
showNotificationsPanel: () => void
|
||||||
hideNotificationsPanel: () => void
|
hideNotificationsPanel: () => void
|
||||||
markNotificationAsRead: (notification: Notification) => Promise<void>
|
markSeen: (notification_id: number) => Promise<void>
|
||||||
markAllNotificationsAsRead: () => Promise<void>
|
markSeenThread: (threadId: string) => Promise<void>
|
||||||
loadNotifications: (options: QueryLoad_NotificationsArgs) => Promise<Notification[]>
|
markSeenAll: () => Promise<void>
|
||||||
|
loadNotificationsGrouped: (options: QueryLoad_NotificationsArgs) => Promise<NotificationGroup[]>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,56 +41,65 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
const [isNotificationsPanelOpen, setIsNotificationsPanelOpen] = createSignal(false)
|
const [isNotificationsPanelOpen, setIsNotificationsPanelOpen] = createSignal(false)
|
||||||
const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0)
|
const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0)
|
||||||
const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0)
|
const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0)
|
||||||
const [notificationEntities, setNotificationEntities] = createStore<Record<number, Notification>>({})
|
const [notificationEntities, setNotificationEntities] = createStore<Record<string, NotificationGroup>>({})
|
||||||
const { isAuthenticated, author } = useSession()
|
const { isAuthenticated } = useSession()
|
||||||
const { addHandler } = useConnect()
|
const { addHandler } = useConnect()
|
||||||
|
|
||||||
const loadNotifications = async (options: { after: number; limit?: number; offset?: number }) => {
|
const loadNotificationsGrouped = async (options: { after: number; limit?: number; offset?: number }) => {
|
||||||
if (isAuthenticated() && notifierClient?.private) {
|
if (isAuthenticated() && notifierClient?.private) {
|
||||||
const { notifications, unread, total } = await notifierClient.getNotifications(options)
|
const { notifications: groups, unread, total } = await notifierClient.getNotifications(options)
|
||||||
const newNotificationEntities = notifications.reduce((acc, notification) => {
|
const newGroupsEntries = groups.reduce((acc, group: NotificationGroup) => {
|
||||||
acc[notification.id] = notification
|
acc[group.id] = group
|
||||||
return acc
|
return acc
|
||||||
}, {})
|
}, {})
|
||||||
|
|
||||||
setTotalNotificationsCount(total)
|
setTotalNotificationsCount(total)
|
||||||
setUnreadNotificationsCount(unread)
|
setUnreadNotificationsCount(unread)
|
||||||
setNotificationEntities(newNotificationEntities)
|
setNotificationEntities(newGroupsEntries)
|
||||||
console.debug(`[context.notifications] updated`)
|
console.debug(`[context.notifications] groups updated`)
|
||||||
return notifications
|
return groups
|
||||||
} else {
|
} else {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedNotifications = createMemo(() => {
|
const sortedNotifications = createMemo(() => {
|
||||||
return Object.values(notificationEntities).sort((a, b) => b.created_at - a.created_at)
|
return Object.values(notificationEntities).sort((a, b) => b.updated_at - a.updated_at)
|
||||||
})
|
})
|
||||||
|
|
||||||
const now = Math.floor(Date.now() / 1000)
|
const now = Math.floor(Date.now() / 1000)
|
||||||
const loadedNotificationsCount = createMemo(() => Object.keys(notificationEntities).length)
|
const loadedNotificationsCount = createMemo(() => Object.keys(notificationEntities).length)
|
||||||
const [after, setAfter] = createStorageSignal('notifier_timestamp', now)
|
const [after, setAfter] = createStorageSignal('notifier_timestamp', now)
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
addHandler((data: SSEMessage) => {
|
addHandler((data: SSEMessage) => {
|
||||||
if (data.entity === 'reaction' && isAuthenticated()) {
|
if (data.entity === 'reaction' && isAuthenticated()) {
|
||||||
console.info(`[context.notifications] event`, data)
|
console.info(`[context.notifications] event`, data)
|
||||||
loadNotifications({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) })
|
loadNotificationsGrouped({ after: after(), limit: Math.max(PAGE_SIZE, loadedNotificationsCount()) })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
setAfter(now)
|
setAfter(now)
|
||||||
})
|
})
|
||||||
|
|
||||||
const markNotificationAsRead = async (notification: Notification) => {
|
const markSeenThread = async (threadId: string) => {
|
||||||
if (notifierClient.private) await notifierClient.markNotificationAsRead(notification.id)
|
if (notifierClient.private) await notifierClient.markSeenThread(threadId)
|
||||||
notification.seen.push(author().id)
|
const thread = notificationEntities[threadId]
|
||||||
setNotificationEntities((nnn: Notification) => ({ ...nnn, [notification.id]: notification }))
|
thread.seen = true
|
||||||
|
setNotificationEntities((nnn) => ({ ...nnn, [threadId]: thread }))
|
||||||
setUnreadNotificationsCount((oldCount) => oldCount - 1)
|
setUnreadNotificationsCount((oldCount) => oldCount - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAllNotificationsAsRead = async () => {
|
const markSeenAll = async () => {
|
||||||
if (isAuthenticated() && notifierClient.private) {
|
if (isAuthenticated() && notifierClient.private) {
|
||||||
await notifierClient.markAllNotificationsAsRead()
|
await notifierClient.markSeenAfter({ after: after() })
|
||||||
await loadNotifications({ after: after(), limit: loadedNotificationsCount() })
|
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const markSeen = async (notification_id: number) => {
|
||||||
|
if (isAuthenticated() && notifierClient.private) {
|
||||||
|
await notifierClient.markSeen(notification_id)
|
||||||
|
await loadNotificationsGrouped({ after: after(), limit: loadedNotificationsCount() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,9 +114,10 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
const actions = {
|
const actions = {
|
||||||
showNotificationsPanel,
|
showNotificationsPanel,
|
||||||
hideNotificationsPanel,
|
hideNotificationsPanel,
|
||||||
markNotificationAsRead,
|
markSeenThread,
|
||||||
markAllNotificationsAsRead,
|
markSeenAll,
|
||||||
loadNotifications,
|
markSeen,
|
||||||
|
loadNotificationsGrouped,
|
||||||
}
|
}
|
||||||
|
|
||||||
const value: NotificationsContextType = {
|
const value: NotificationsContextType = {
|
||||||
|
|
|
@ -90,7 +90,7 @@ export const apiClient = {
|
||||||
getAllTopics: async () => {
|
getAllTopics: async () => {
|
||||||
const response = await publicGraphQLClient.query(topicsAll, {}).toPromise()
|
const response = await publicGraphQLClient.query(topicsAll, {}).toPromise()
|
||||||
if (response.error) {
|
if (response.error) {
|
||||||
console.debug('[graphql.client.core] get_topicss_all', response.error)
|
console.debug('[graphql.client.core] get_topics_all', response.error)
|
||||||
}
|
}
|
||||||
return response.data.get_topics_all
|
return response.data.get_topics_all
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
import { createGraphQLClient } from '../createGraphQLClient'
|
import { createGraphQLClient } from '../createGraphQLClient'
|
||||||
import markAllNotificationsAsRead from '../mutation/notifier/mark-all-notifications-as-read'
|
import markSeenMutation from '../mutation/notifier/mark-seen'
|
||||||
import markNotificationAsRead from '../mutation/notifier/mark-notification-as-read'
|
import markSeenAfterMutation from '../mutation/notifier/mark-seen-after'
|
||||||
|
import markThreadSeenMutation from '../mutation/notifier/mark-seen-thread'
|
||||||
import loadNotifications from '../query/notifier/notifications-load'
|
import loadNotifications from '../query/notifier/notifications-load'
|
||||||
import { NotificationsResult, QueryLoad_NotificationsArgs } from '../schema/notifier.gen'
|
import {
|
||||||
|
MutationMark_Seen_AfterArgs,
|
||||||
|
NotificationsResult,
|
||||||
|
QueryLoad_NotificationsArgs,
|
||||||
|
} from '../schema/notifier.gen'
|
||||||
|
|
||||||
export const notifierClient = {
|
export const notifierClient = {
|
||||||
private: null,
|
private: null,
|
||||||
|
@ -12,15 +17,15 @@ export const notifierClient = {
|
||||||
const resp = await notifierClient.private.query(loadNotifications, params).toPromise()
|
const resp = await notifierClient.private.query(loadNotifications, params).toPromise()
|
||||||
return resp.data?.load_notifications
|
return resp.data?.load_notifications
|
||||||
},
|
},
|
||||||
markNotificationAsRead: async (notification_id: number): Promise<void> => {
|
|
||||||
await notifierClient.private
|
markSeen: async (notification_id: number): Promise<void> => {
|
||||||
.mutation(markNotificationAsRead, {
|
await notifierClient.private.mutation(markSeenMutation, { notification_id }).toPromise()
|
||||||
notification_id,
|
|
||||||
})
|
|
||||||
.toPromise()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
markAllNotificationsAsRead: async (): Promise<void> => {
|
markSeenAfter: async (options: MutationMark_Seen_AfterArgs): Promise<void> => {
|
||||||
await notifierClient.private.mutation(markAllNotificationsAsRead, {}).toPromise()
|
await notifierClient.private.mutation(markSeenAfterMutation, options).toPromise()
|
||||||
|
},
|
||||||
|
markSeenThread: async (thread: string): Promise<void> => {
|
||||||
|
await notifierClient.private.mutation(markThreadSeenMutation, { thread }).toPromise()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
import { gql } from '@urql/core'
|
|
||||||
|
|
||||||
export default gql`
|
|
||||||
mutation MarkAllNotificationsAsReadMutation {
|
|
||||||
mark_all_notifications_as_read {
|
|
||||||
error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
9
src/graphql/mutation/notifier/mark-seen-after.ts
Normal file
9
src/graphql/mutation/notifier/mark-seen-after.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
mutation MarkSeenAfter($after: Int) {
|
||||||
|
mark_seen_after(after: $after) {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
9
src/graphql/mutation/notifier/mark-seen-thread.ts
Normal file
9
src/graphql/mutation/notifier/mark-seen-thread.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { gql } from '@urql/core'
|
||||||
|
|
||||||
|
export default gql`
|
||||||
|
mutation MarkThreadSeen($thread: String!, $after: Int) {
|
||||||
|
mark_seen_thread(thread: $thread, after: $after) {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
|
@ -2,7 +2,7 @@ import { gql } from '@urql/core'
|
||||||
|
|
||||||
export default gql`
|
export default gql`
|
||||||
mutation MarkNotificationAsReadMutation($notificationId: Int!) {
|
mutation MarkNotificationAsReadMutation($notificationId: Int!) {
|
||||||
mark_notification_as_read(notification_id: $notificationId) {
|
mark_seen(notification_id: $notificationId) {
|
||||||
error
|
error
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -5,11 +5,19 @@ export default gql`
|
||||||
load_notifications(after: $after, limit: $limit, offset: $offset) {
|
load_notifications(after: $after, limit: $limit, offset: $offset) {
|
||||||
notifications {
|
notifications {
|
||||||
id
|
id
|
||||||
entity
|
updated_at
|
||||||
action
|
authors {
|
||||||
payload
|
id
|
||||||
created_at
|
slug
|
||||||
seen
|
name
|
||||||
|
pic
|
||||||
|
}
|
||||||
|
reactions
|
||||||
|
shout {
|
||||||
|
id
|
||||||
|
slug
|
||||||
|
title
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unread
|
unread
|
||||||
total
|
total
|
||||||
|
|
Loading…
Reference in New Issue
Block a user