2023-10-14 11:39:24 +00:00
|
|
|
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'
|
2023-10-14 11:39:24 +00:00
|
|
|
import { openPage } from '@nanostores/router'
|
2023-10-16 17:24:33 +00:00
|
|
|
import { router, useRouter } from '../../../stores/router'
|
2023-10-19 14:44:26 +00:00
|
|
|
import { SSEMessage, useNotifications } from '../../../context/notifications'
|
2023-10-14 11:39:24 +00:00
|
|
|
import { Userpic } from '../../Author/Userpic'
|
|
|
|
import { useLocalize } from '../../../context/localize'
|
2023-10-16 17:24:33 +00:00
|
|
|
import type { ArticlePageSearchParams } from '../../Article/FullArticle'
|
2023-10-18 10:56:41 +00:00
|
|
|
import { TimeAgo } from '../../_shared/TimeAgo'
|
|
|
|
import styles from './NotificationView.module.scss'
|
2023-10-14 11:39:24 +00:00
|
|
|
|
|
|
|
type Props = {
|
2023-10-19 14:44:26 +00:00
|
|
|
notification: SSEMessage
|
2023-10-14 11:39:24 +00:00
|
|
|
onClick: () => void
|
2023-10-18 10:56:41 +00:00
|
|
|
dateTimeFormat: 'ago' | 'time' | 'date'
|
2023-10-14 11:39:24 +00:00
|
|
|
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
|
|
|
*/
|
2023-10-14 11:39:24 +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>()
|
2023-10-16 17:24:33 +00:00
|
|
|
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
2023-10-18 10:56:41 +00:00
|
|
|
const { t, formatDate, formatTime } = useLocalize()
|
2023-10-14 11:39:24 +00:00
|
|
|
|
|
|
|
onMount(() => {
|
2023-10-14 23:05:07 +00:00
|
|
|
setTimeout(() => setData(props.notification))
|
2023-10-14 11:39:24 +00:00
|
|
|
})
|
|
|
|
const lastUser = createMemo(() => {
|
2023-10-19 14:44:26 +00:00
|
|
|
return props.notification.entity === 'follower' ? data().payload : data().payload.author
|
2023-10-14 11:39:24 +00:00
|
|
|
})
|
|
|
|
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 11:39:24 +00:00
|
|
|
|
2023-10-14 23:05:07 +00:00
|
|
|
// TODO: count occurencies from in-browser notifications-db
|
2023-10-14 11:39:24 +00:00
|
|
|
|
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 11:39:24 +00:00
|
|
|
}
|
|
|
|
}
|
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 })
|
2023-10-14 11:39:24 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
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 })
|
2023-10-14 11:39:24 +00:00
|
|
|
props.onClick()
|
|
|
|
}
|
|
|
|
|
2023-10-18 10:56:41 +00:00
|
|
|
const formattedDateTime = createMemo(() => {
|
|
|
|
switch (props.dateTimeFormat) {
|
|
|
|
case 'ago': {
|
2023-10-18 23:34:15 +00:00
|
|
|
return <TimeAgo date={props.notification.timestamp} />
|
2023-10-18 10:56:41 +00:00
|
|
|
}
|
|
|
|
case 'time': {
|
2023-10-18 23:34:15 +00:00
|
|
|
return formatTime(new Date(props.notification.timestamp))
|
2023-10-18 10:56:41 +00:00
|
|
|
}
|
|
|
|
case 'date': {
|
2023-10-18 23:34:15 +00:00
|
|
|
return formatDate(new Date(props.notification.timestamp), { month: 'numeric', year: '2-digit' })
|
2023-10-18 10:56:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2023-10-14 11:39:24 +00:00
|
|
|
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>
|
2023-10-18 10:56:41 +00:00
|
|
|
<div class={styles.timeContainer}>{formattedDateTime()}</div>
|
2023-10-14 11:39:24 +00:00
|
|
|
</div>
|
|
|
|
</Show>
|
|
|
|
)
|
|
|
|
}
|