webapp/src/components/NotificationsPanel/NotificationView/NotificationView.tsx

151 lines
4.5 KiB
TypeScript
Raw Normal View History

import { clsx } from 'clsx'
import { createMemo, createSignal, onMount, Show } from 'solid-js'
2023-10-14 23:05:07 +00:00
import { Author } from '../../../graphql/types.gen'
import { openPage } from '@nanostores/router'
import { router, useRouter } from '../../../stores/router'
2023-10-19 14:44:26 +00:00
import { SSEMessage, useNotifications } from '../../../context/notifications'
import { Userpic } from '../../Author/Userpic'
import { useLocalize } from '../../../context/localize'
import type { ArticlePageSearchParams } from '../../Article/FullArticle'
import { TimeAgo } from '../../_shared/TimeAgo'
import styles from './NotificationView.module.scss'
type Props = {
2023-10-19 14:44:26 +00:00
notification: SSEMessage
onClick: () => void
dateTimeFormat: 'ago' | 'time' | 'date'
class?: string
}
2023-10-14 23:05:07 +00:00
// NOTE: not a graphql generated type
2023-11-13 14:43:08 +00:00
export enum NewNotificationType {
2023-10-14 23:05:07 +00:00
NewComment = 'NEW_COMMENT',
NewReply = 'NEW_REPLY',
NewFollower = 'NEW_FOLLOWER',
NewShout = 'NEW_SHOUT',
NewLike = 'NEW_LIKE',
NewDislike = 'NEW_DISLIKE'
}
2023-11-13 14:43:08 +00:00
export type NotificationUser = {
id: number
name: string
slug: string
userpic: string
}
2023-10-14 23:05:07 +00:00
const TEMPLATES = {
// FIXME: set proper templates
2023-10-19 14:44:26 +00:00
'follower:join': 'new follower',
'shout:create': 'new shout'
2023-11-13 14:43:08 +00:00
/*
2023-10-14 23:05:07 +00:00
new_reaction0: 'new like',
new_reaction1: 'new dislike',
new_reaction2: 'new agreement',
new_reaction3: 'new disagreement',
new_reaction4: 'new proof',
new_reaction5: 'new disproof',
new_reaction6: 'new comment',
new_reaction7: 'new quote',
new_reaction8: 'new proposal',
new_reaction9: 'new question',
new_reaction10: 'new remark',
//"new_reaction11": "new footnote",
new_reaction12: 'new acception',
2023-11-13 14:43:08 +00:00
new_reaction13: 'new rejection'
2023-10-19 14:44:26 +00:00
*/
}
export const NotificationView = (props: Props) => {
const {
actions: { markNotificationAsRead }
} = useNotifications()
2023-10-19 14:44:26 +00:00
const [data, setData] = createSignal<SSEMessage>(null)
2023-11-13 14:43:08 +00:00
const [kind, setKind] = createSignal<NewNotificationType>()
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
const { t, formatDate, formatTime } = useLocalize()
onMount(() => {
2023-10-14 23:05:07 +00:00
setTimeout(() => setData(props.notification))
})
const lastUser = createMemo(() => {
2023-10-19 14:44:26 +00:00
return props.notification.entity === 'follower' ? data().payload : data().payload.author
})
const content = createMemo(() => {
if (!data()) {
return null
}
2023-11-13 14:43:08 +00:00
let caption: string, author: Author, ntype: NewNotificationType
2023-10-14 23:05:07 +00:00
// TODO: count occurencies from in-browser notifications-db
2023-10-19 14:44:26 +00:00
switch (props.notification.entity) {
case 'follower': {
2023-10-14 23:05:07 +00:00
caption = ''
author = data().payload
2023-11-13 14:43:08 +00:00
ntype = NewNotificationType.NewFollower
2023-10-14 23:05:07 +00:00
break
}
2023-11-13 14:43:08 +00:00
case 'shout':
{
caption = data().payload.title
author = data().payload.authors[-1]
ntype = NewNotificationType.NewShout
break
}
2023-10-14 23:05:07 +00:00
break
2023-10-19 14:44:26 +00:00
case 'reaction': {
2023-11-13 14:43:08 +00:00
ntype = data().payload.replyTo ? NewNotificationType.NewReply : NewNotificationType.NewComment
2023-10-19 14:44:26 +00:00
console.log(data().payload.kind)
// TODO: handle all needed reaction kinds
2023-10-14 23:05:07 +00:00
}
default: {
caption = data().payload.shout.title
author = data().payload.author
}
}
2023-10-14 23:05:07 +00:00
setKind(ntype) // FIXME: use it somewhere if needed or remove
2023-10-19 14:44:26 +00:00
return t(TEMPLATES[`${props.notification.entity}:${props.notification.action}`], { caption, author })
})
const handleClick = () => {
if (!props.notification.seen) {
markNotificationAsRead(props.notification)
}
2023-10-19 14:44:26 +00:00
const subpath = props.notification.entity === 'follower' ? 'author' : 'article'
const slug = props.notification.entity === 'reaction' ? data().payload.shout.slug : data().payload.slug
2023-10-14 23:05:07 +00:00
openPage(router, subpath, { slug })
props.onClick()
}
const formattedDateTime = createMemo(() => {
switch (props.dateTimeFormat) {
case 'ago': {
2023-10-18 23:34:15 +00:00
return <TimeAgo date={props.notification.timestamp} />
}
case 'time': {
2023-10-18 23:34:15 +00:00
return formatTime(new Date(props.notification.timestamp))
}
case 'date': {
2023-10-18 23:34:15 +00:00
return formatDate(new Date(props.notification.timestamp), { month: 'numeric', year: '2-digit' })
}
}
})
return (
<Show when={data()}>
<div
class={clsx(styles.NotificationView, props.class, {
[styles.seen]: props.notification.seen
})}
onClick={handleClick}
>
<Userpic name={lastUser().name} userpic={lastUser().userpic} class={styles.userpic} />
<div>{content()}</div>
<div class={styles.timeContainer}>{formattedDateTime()}</div>
</div>
</Show>
)
}