notification system update (#265)
* notification system update Co-authored-by: Igor Lobanov <igor.lobanov@onetwotrip.com>
This commit is contained in:
parent
4da78d2e68
commit
9262367f68
|
@ -210,12 +210,19 @@
|
||||||
"New only": "New only",
|
"New only": "New only",
|
||||||
"New password": "New password",
|
"New password": "New password",
|
||||||
"New stories every day and even more!": "New stories and more are waiting for you every day!",
|
"New stories every day and even more!": "New stories and more are waiting for you every day!",
|
||||||
"NewCommentNotificationText": "{commentsCount, plural, one {New comment} other {{commentsCount} comments}} to your publication {shoutTitle} from {lastCommenterName}{restUsersCount, plural, =0 {} one { one more user} other { and more {restUsersCount} users}}",
|
|
||||||
"NewReplyNotificationText": "{commentsCount, plural, one {New reply} other {{commentsCount} replays} other {{commentsCount} новых ответов}} to your publication {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}",
|
"NotificationNewCommentText1": "{commentsCount, plural, one {New comment} other {{commentsCount} comments}} to your publication",
|
||||||
|
"NotificationNewCommentText2": "from",
|
||||||
|
"NotificationNewCommentText3": "{restUsersCount, plural, =0 {} one { one more user} other { and more {restUsersCount} users}}",
|
||||||
|
|
||||||
|
"NotificationNewReplyText1": "{commentsCount, plural, one {New reply} other {{commentsCount} replays}} to your publication",
|
||||||
|
"NotificationNewReplyText2": "from",
|
||||||
|
"NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}",
|
||||||
|
|
||||||
"Newsletter": "Newsletter",
|
"Newsletter": "Newsletter",
|
||||||
"Night mode": "Night mode",
|
"Night mode": "Night mode",
|
||||||
"No notifications, yet": "No notifications, yet",
|
"No notifications yet": "No notifications yet",
|
||||||
"No such account, please try to register": "No such account found, please try to register",
|
"Write good articles, comment\nand it won't be so empty here": "Write good articles, comment\nand it won't be so empty here",
|
||||||
"Nothing here yet": "There's nothing here yet",
|
"Nothing here yet": "There's nothing here yet",
|
||||||
"Nothing is here": "There is nothing here",
|
"Nothing is here": "There is nothing here",
|
||||||
"Notifications": "Notifications",
|
"Notifications": "Notifications",
|
||||||
|
|
|
@ -220,11 +220,19 @@
|
||||||
"New only": "Только новые",
|
"New only": "Только новые",
|
||||||
"New password": "Новый пароль",
|
"New password": "Новый пароль",
|
||||||
"New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!",
|
"New stories every day and even more!": "Каждый день вас ждут новые истории и ещё много всего интересного!",
|
||||||
"NewCommentNotificationText": "{commentsCount, plural, one {Новый комментарий} few {{commentsCount} новых комментария} other {{commentsCount} новых комментариев}} к вашей публикации {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
|
||||||
"NewReplyNotificationText": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} к вашему комментарию к публикации {shoutTitle} от {lastCommenterName}{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
"NotificationNewCommentText1": "{commentsCount, plural, one {Новый комментарий} few {{commentsCount} новых комментария} other {{commentsCount} новых комментариев}} к вашей публикации",
|
||||||
|
"NotificationNewCommentText2": "от",
|
||||||
|
"NotificationNewCommentText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
||||||
|
|
||||||
|
"NotificationNewReplyText1": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} к вашему комментарию к публикации",
|
||||||
|
"NotificationNewReplyText2": "от",
|
||||||
|
"NotificationNewReplyText3": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
||||||
|
|
||||||
"Newsletter": "Рассылка",
|
"Newsletter": "Рассылка",
|
||||||
"Night mode": "Ночная тема",
|
"Night mode": "Ночная тема",
|
||||||
"No notifications, yet": "Тут пока пусто",
|
"No notifications yet": "Уведомлений пока нет",
|
||||||
|
"Write good articles, comment\nand it won't be so empty here": "Пишите хорошие статьи, комментируйте,\nи здесь станет не так пусто",
|
||||||
"No such account, please try to register": "Такой адрес не найден, попробуйте зарегистрироваться",
|
"No such account, please try to register": "Такой адрес не найден, попробуйте зарегистрироваться",
|
||||||
"Nothing here yet": "Здесь пока ничего нет",
|
"Nothing here yet": "Здесь пока ничего нет",
|
||||||
"Nothing is here": "Здесь ничего нет",
|
"Nothing is here": "Здесь ничего нет",
|
||||||
|
|
|
@ -141,7 +141,7 @@ export const Comment = (props: Props) => {
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
<small>
|
<small>
|
||||||
<a href={`#comment-${comment()?.id}`}>{comment()?.shout.title || ''}</a>
|
<a href={`#comment_${comment()?.id}`}>{comment()?.shout.title || ''}</a>
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ export const Comment = (props: Props) => {
|
||||||
<CommentRatingControl comment={comment()} />
|
<CommentRatingControl comment={comment()} />
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
<div class={styles.commentBody} id={'comment-' + (comment().id || '')}>
|
<div class={styles.commentBody}>
|
||||||
<Show when={editMode()} fallback={<MD body={body()} />}>
|
<Show when={editMode()} fallback={<MD body={body()} />}>
|
||||||
<Suspense fallback={<p>{t('Loading')}</p>}>
|
<Suspense fallback={<p>{t('Loading')}</p>}>
|
||||||
<SimplifiedEditor
|
<SimplifiedEditor
|
||||||
|
|
|
@ -181,7 +181,7 @@ export const CommentsTree = (props: Props) => {
|
||||||
<SimplifiedEditor
|
<SimplifiedEditor
|
||||||
quoteEnabled={true}
|
quoteEnabled={true}
|
||||||
imageEnabled={true}
|
imageEnabled={true}
|
||||||
autoFocus={true}
|
autoFocus={false}
|
||||||
submitByCtrlEnter={true}
|
submitByCtrlEnter={true}
|
||||||
placeholder={t('Write a comment...')}
|
placeholder={t('Write a comment...')}
|
||||||
onSubmit={(value) => handleSubmitComment(value)}
|
onSubmit={(value) => handleSubmitComment(value)}
|
||||||
|
|
|
@ -28,11 +28,24 @@ import styles from './Article.module.scss'
|
||||||
import { CardTopic } from '../Feed/CardTopic'
|
import { CardTopic } from '../Feed/CardTopic'
|
||||||
import { createPopper } from '@popperjs/core'
|
import { createPopper } from '@popperjs/core'
|
||||||
|
|
||||||
interface Props {
|
type Props = {
|
||||||
article: Shout
|
article: Shout
|
||||||
scrollToComments?: boolean
|
scrollToComments?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ArticlePageSearchParams = {
|
||||||
|
scrollTo: 'comments'
|
||||||
|
commentId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const scrollTo = (el: HTMLElement) => {
|
||||||
|
window.scrollTo({
|
||||||
|
top: el.offsetTop - 96,
|
||||||
|
left: 0,
|
||||||
|
behavior: 'smooth'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const FullArticle = (props: Props) => {
|
export const FullArticle = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const {
|
const {
|
||||||
|
@ -78,15 +91,12 @@ export const FullArticle = (props: Props) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const commentsRef: { current: HTMLDivElement } = { current: null }
|
const commentsRef: { current: HTMLDivElement } = { current: null }
|
||||||
|
|
||||||
const scrollToComments = () => {
|
const scrollToComments = () => {
|
||||||
window.scrollTo({
|
scrollTo(commentsRef.current)
|
||||||
top: commentsRef.current.offsetTop - 96,
|
|
||||||
left: 0,
|
|
||||||
behavior: 'smooth'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { searchParams, changeSearchParam } = useRouter()
|
const { searchParams, changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (props.scrollToComments) {
|
if (props.scrollToComments) {
|
||||||
|
@ -105,9 +115,12 @@ export const FullArticle = (props: Props) => {
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (searchParams().commentId && isReactionsLoaded()) {
|
if (searchParams().commentId && isReactionsLoaded()) {
|
||||||
const commentElement = document.querySelector(`[id='comment_${searchParams().commentId}']`)
|
const commentElement = document.querySelector<HTMLElement>(
|
||||||
|
`[id='comment_${searchParams().commentId}']`
|
||||||
|
)
|
||||||
|
changeSearchParam({ commentId: null })
|
||||||
if (commentElement) {
|
if (commentElement) {
|
||||||
commentElement.scrollIntoView({ behavior: 'smooth' })
|
scrollTo(commentElement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -67,7 +67,7 @@ export const Header = (props: Props) => {
|
||||||
let windowScrollTop = 0
|
let windowScrollTop = 0
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
const mainContent = document.querySelector('.main-content') as HTMLDivElement
|
const mainContent = document.querySelector<HTMLDivElement>('.main-content')
|
||||||
|
|
||||||
if (fixed() || modal() !== null) {
|
if (fixed() || modal() !== null) {
|
||||||
windowScrollTop = window.scrollY
|
windowScrollTop = window.scrollY
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
.EmptyMessage {
|
||||||
|
// TODO: check markup
|
||||||
|
color: var(--black-500);
|
||||||
|
text-align: center;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import styles from './EmptyMessage.module.scss'
|
||||||
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
|
||||||
|
export const EmptyMessage = () => {
|
||||||
|
const { t } = useLocalize()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={clsx(styles.EmptyMessage)}>
|
||||||
|
<div class={styles.title}>{t('No notifications yet')}</div>
|
||||||
|
<div>{t("Write good articles, comment\nand it won't be so empty here")}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
src/components/NotificationsPanel/EmptyMessage/index.ts
Normal file
1
src/components/NotificationsPanel/EmptyMessage/index.ts
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export { EmptyMessage } from './EmptyMessage'
|
|
@ -20,6 +20,13 @@
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--gray-100);
|
background-color: var(--gray-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a,
|
||||||
|
a:visited {
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
border-bottom: none !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.userpic {
|
.userpic {
|
||||||
|
|
|
@ -3,11 +3,12 @@ import styles from './NotificationView.module.scss'
|
||||||
import type { Notification } from '../../../graphql/types.gen'
|
import type { Notification } from '../../../graphql/types.gen'
|
||||||
import { createMemo, createSignal, onMount, Show } from 'solid-js'
|
import { createMemo, createSignal, onMount, Show } from 'solid-js'
|
||||||
import { NotificationType } from '../../../graphql/types.gen'
|
import { NotificationType } from '../../../graphql/types.gen'
|
||||||
import { openPage } from '@nanostores/router'
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
import { router } from '../../../stores/router'
|
import { router, useRouter } from '../../../stores/router'
|
||||||
import { useNotifications } from '../../../context/notifications'
|
import { useNotifications } from '../../../context/notifications'
|
||||||
import { Userpic } from '../../Author/Userpic'
|
import { Userpic } from '../../Author/Userpic'
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import type { ArticlePageSearchParams } from '../../Article/FullArticle'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
notification: Notification
|
notification: Notification
|
||||||
|
@ -26,13 +27,16 @@ type NotificationData = {
|
||||||
slug: string
|
slug: string
|
||||||
userpic: string
|
userpic: string
|
||||||
}[]
|
}[]
|
||||||
|
reactionIds: number[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const NotificationView = (props: Props) => {
|
export const NotificationView = (props: Props) => {
|
||||||
const {
|
const {
|
||||||
actions: { markNotificationAsRead }
|
actions: { markNotificationAsRead, hideNotificationsPanel }
|
||||||
} = useNotifications()
|
} = useNotifications()
|
||||||
|
|
||||||
|
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
||||||
|
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
|
||||||
const [data, setData] = createSignal<NotificationData>(null)
|
const [data, setData] = createSignal<NotificationData>(null)
|
||||||
|
@ -49,6 +53,11 @@ export const NotificationView = (props: Props) => {
|
||||||
return data().users[data().users.length - 1]
|
return data().users[data().users.length - 1]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleLinkClick = (event: MouseEvent) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
hideNotificationsPanel()
|
||||||
|
}
|
||||||
|
|
||||||
const content = createMemo(() => {
|
const content = createMemo(() => {
|
||||||
if (!data()) {
|
if (!data()) {
|
||||||
return null
|
return null
|
||||||
|
@ -64,47 +73,67 @@ export const NotificationView = (props: Props) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shoutTitle.length < data().shout.title.length) {
|
if (shoutTitle.length < data().shout.title.length) {
|
||||||
shoutTitle += '...'
|
shoutTitle = `${shoutTitle.trim()}...`
|
||||||
|
|
||||||
|
if (shoutTitle[0] === '«') {
|
||||||
|
shoutTitle += '»'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (props.notification.type) {
|
switch (props.notification.type) {
|
||||||
case NotificationType.NewComment: {
|
case NotificationType.NewComment: {
|
||||||
return t('NewCommentNotificationText', {
|
return (
|
||||||
commentsCount: props.notification.occurrences,
|
<>
|
||||||
shoutTitle,
|
{t('NotificationNewCommentText1', {
|
||||||
lastCommenterName: lastUser().name,
|
commentsCount: props.notification.occurrences
|
||||||
restUsersCount: data().users.length - 1
|
})}{' '}
|
||||||
})
|
<a href={getPagePath(router, 'article', { slug: data().shout.slug })} onClick={handleLinkClick}>
|
||||||
|
{shoutTitle}
|
||||||
|
</a>{' '}
|
||||||
|
{t('NotificationNewCommentText2')}{' '}
|
||||||
|
<a href={getPagePath(router, 'author', { slug: lastUser().slug })} onClick={handleLinkClick}>
|
||||||
|
{lastUser().name}
|
||||||
|
</a>{' '}
|
||||||
|
{t('NotificationNewCommentText3', {
|
||||||
|
restUsersCount: data().users.length - 1
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case NotificationType.NewReply: {
|
case NotificationType.NewReply: {
|
||||||
return t('NewReplyNotificationText', {
|
return (
|
||||||
commentsCount: props.notification.occurrences,
|
<>
|
||||||
shoutTitle,
|
{t('NotificationNewReplyText1', {
|
||||||
lastCommenterName: lastUser().name,
|
commentsCount: props.notification.occurrences
|
||||||
restUsersCount: data().users.length - 1
|
})}{' '}
|
||||||
})
|
<a href={getPagePath(router, 'article', { slug: data().shout.slug })} onClick={handleLinkClick}>
|
||||||
|
{shoutTitle}
|
||||||
|
</a>{' '}
|
||||||
|
{t('NotificationNewReplyText2')}{' '}
|
||||||
|
<a href={getPagePath(router, 'author', { slug: lastUser().slug })} onClick={handleLinkClick}>
|
||||||
|
{lastUser().name}
|
||||||
|
</a>{' '}
|
||||||
|
{t('NotificationNewReplyText3', {
|
||||||
|
restUsersCount: data().users.length - 1
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
props.onClick()
|
||||||
|
|
||||||
if (!props.notification.seen) {
|
if (!props.notification.seen) {
|
||||||
markNotificationAsRead(props.notification)
|
markNotificationAsRead(props.notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
openPage(router, 'article', { slug: data().shout.slug })
|
openPage(router, 'article', { slug: data().shout.slug })
|
||||||
props.onClick()
|
|
||||||
|
|
||||||
// switch (props.notification.type) {
|
if (data().reactionIds) {
|
||||||
// case NotificationType.NewComment: {
|
changeSearchParam({ commentId: data().reactionIds[0].toString() })
|
||||||
// openPage(router, 'article', { slug: data().shout.slug })
|
}
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// case NotificationType.NewReply: {
|
|
||||||
// openPage(router, 'article', { slug: data().shout.slug })
|
|
||||||
// break
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { Icon } from '../_shared/Icon'
|
||||||
import { createEffect, For } from 'solid-js'
|
import { createEffect, For } from 'solid-js'
|
||||||
import { useNotifications } from '../../context/notifications'
|
import { useNotifications } from '../../context/notifications'
|
||||||
import { NotificationView } from './NotificationView'
|
import { NotificationView } from './NotificationView'
|
||||||
|
import { EmptyMessage } from './EmptyMessage'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
|
@ -30,8 +31,22 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
handler: () => handleHide()
|
handler: () => handleHide()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
let windowScrollTop = 0
|
||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
|
const mainContent = document.querySelector<HTMLDivElement>('.main-content')
|
||||||
|
|
||||||
|
if (props.isOpen) {
|
||||||
|
windowScrollTop = window.scrollY
|
||||||
|
mainContent.style.marginTop = `-${windowScrollTop}px`
|
||||||
|
}
|
||||||
|
|
||||||
document.body.classList.toggle('fixed', props.isOpen)
|
document.body.classList.toggle('fixed', props.isOpen)
|
||||||
|
|
||||||
|
if (!props.isOpen) {
|
||||||
|
mainContent.style.marginTop = ''
|
||||||
|
window.scrollTo(0, windowScrollTop)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
useEscKeyDownHandler(handleHide)
|
useEscKeyDownHandler(handleHide)
|
||||||
|
@ -52,10 +67,7 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
<Icon name="close" />
|
<Icon name="close" />
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.title}>{t('Notifications')}</div>
|
<div class={styles.title}>{t('Notifications')}</div>
|
||||||
<For
|
<For each={sortedNotifications()} fallback={<EmptyMessage />}>
|
||||||
each={sortedNotifications()}
|
|
||||||
fallback={<div class={styles.emptyMessageContainer}>{t('No notifications, yet')}</div>}
|
|
||||||
>
|
|
||||||
{(notification) => (
|
{(notification) => (
|
||||||
<NotificationView
|
<NotificationView
|
||||||
notification={notification}
|
notification={notification}
|
||||||
|
|
|
@ -16,6 +16,7 @@ type NotificationsContextType = {
|
||||||
sortedNotifications: Accessor<Notification[]>
|
sortedNotifications: Accessor<Notification[]>
|
||||||
actions: {
|
actions: {
|
||||||
showNotificationsPanel: () => void
|
showNotificationsPanel: () => void
|
||||||
|
hideNotificationsPanel: () => void
|
||||||
markNotificationAsRead: (notification: Notification) => Promise<void>
|
markNotificationAsRead: (notification: Notification) => Promise<void>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
setIsNotificationsPanelOpen(true)
|
setIsNotificationsPanelOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = { showNotificationsPanel, markNotificationAsRead }
|
const hideNotificationsPanel = () => {
|
||||||
|
setIsNotificationsPanelOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = { showNotificationsPanel, hideNotificationsPanel, markNotificationAsRead }
|
||||||
|
|
||||||
const value: NotificationsContextType = {
|
const value: NotificationsContextType = {
|
||||||
notificationEntities,
|
notificationEntities,
|
||||||
|
|
|
@ -11,18 +11,9 @@ import { setPageLoadManagerPromise } from '../utils/pageLoadManager'
|
||||||
|
|
||||||
export const ArticlePage = (props: PageProps) => {
|
export const ArticlePage = (props: PageProps) => {
|
||||||
const shouts = props.article ? [props.article] : []
|
const shouts = props.article ? [props.article] : []
|
||||||
|
const { page } = useRouter()
|
||||||
|
|
||||||
const slug = createMemo(() => {
|
const slug = createMemo(() => page().params['slug'] as string)
|
||||||
const { page: getPage } = useRouter()
|
|
||||||
|
|
||||||
const page = getPage()
|
|
||||||
|
|
||||||
if (page.route !== 'article') {
|
|
||||||
throw new Error('ts guard')
|
|
||||||
}
|
|
||||||
|
|
||||||
return page.params.slug
|
|
||||||
})
|
|
||||||
|
|
||||||
const { articleEntities } = useArticlesStore({
|
const { articleEntities } = useArticlesStore({
|
||||||
shouts
|
shouts
|
||||||
|
|
Loading…
Reference in New Issue
Block a user