notification-group-view
This commit is contained in:
parent
db1c00e8af
commit
43b3de572b
|
@ -236,12 +236,6 @@
|
||||||
"No notifications yet": "No notifications yet",
|
"No notifications yet": "No notifications yet",
|
||||||
"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",
|
||||||
"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}}",
|
|
||||||
"Notifications": "Notifications",
|
"Notifications": "Notifications",
|
||||||
"Or paste a link to an image": "Or paste a link to an image",
|
"Or paste a link to an image": "Or paste a link to an image",
|
||||||
"Ordered list": "Ordered list",
|
"Ordered list": "Ordered list",
|
||||||
|
@ -318,6 +312,8 @@
|
||||||
"Slug": "Slug",
|
"Slug": "Slug",
|
||||||
"Social networks": "Social networks",
|
"Social networks": "Social networks",
|
||||||
"Society": "Society",
|
"Society": "Society",
|
||||||
|
"Some new comments to your publication": "{commentsCount, plural, one {New comment} other {{commentsCount} comments}} to your publication",
|
||||||
|
"Some new replies to your comment": "{commentsCount, plural, one {New reply} other {{commentsCount} replays}} to your publication",
|
||||||
"Something went wrong, check email and password": "Something went wrong. Check your email and password",
|
"Something went wrong, check email and password": "Something went wrong. Check your email and password",
|
||||||
"Something went wrong, please try again": "Something went wrong, please try again",
|
"Something went wrong, please try again": "Something went wrong, please try again",
|
||||||
"Song lyrics": "Song lyrics...",
|
"Song lyrics": "Song lyrics...",
|
||||||
|
@ -421,6 +417,7 @@
|
||||||
"actions": "actions",
|
"actions": "actions",
|
||||||
"add link": "add link",
|
"add link": "add link",
|
||||||
"all topics": "all topics",
|
"all topics": "all topics",
|
||||||
|
"and some more authors": "{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}",
|
||||||
"article": "article",
|
"article": "article",
|
||||||
"author": "author",
|
"author": "author",
|
||||||
"authors": "authors",
|
"authors": "authors",
|
||||||
|
@ -442,6 +439,7 @@
|
||||||
"feed": "feed",
|
"feed": "feed",
|
||||||
"follower": "follower",
|
"follower": "follower",
|
||||||
"followersWithCount": "{count} {count, plural, one {follower} other {followers}}",
|
"followersWithCount": "{count} {count, plural, one {follower} other {followers}}",
|
||||||
|
"from": "from",
|
||||||
"header 1": "header 1",
|
"header 1": "header 1",
|
||||||
"header 2": "header 2",
|
"header 2": "header 2",
|
||||||
"header 3": "header 3",
|
"header 3": "header 3",
|
||||||
|
|
|
@ -248,12 +248,6 @@
|
||||||
"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": "Здесь ничего нет",
|
||||||
"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} пользователей}}",
|
|
||||||
"Notifications": "Уведомления",
|
"Notifications": "Уведомления",
|
||||||
"Or paste a link to an image": "Или вставьте ссылку на изображение",
|
"Or paste a link to an image": "Или вставьте ссылку на изображение",
|
||||||
"Ordered list": "Нумерованный список",
|
"Ordered list": "Нумерованный список",
|
||||||
|
@ -338,6 +332,8 @@
|
||||||
"Slug": "Постоянная ссылка",
|
"Slug": "Постоянная ссылка",
|
||||||
"Social networks": "Социальные сети",
|
"Social networks": "Социальные сети",
|
||||||
"Society": "Общество",
|
"Society": "Общество",
|
||||||
|
"Some new comments to your publication": "{commentsCount, plural, one {Новый комментарий} few {{commentsCount} новых комментария} other {{commentsCount} новых комментариев}} к вашей публикации",
|
||||||
|
"Some new replies to your comment": "{commentsCount, plural, one {Новый ответ} few {{commentsCount} новых ответа} other {{commentsCount} новых ответов}} на ваш комментарий к публикации",
|
||||||
"Something went wrong, check email and password": "Что-то пошло не так. Проверьте адрес электронной почты и пароль",
|
"Something went wrong, check email and password": "Что-то пошло не так. Проверьте адрес электронной почты и пароль",
|
||||||
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
"Something went wrong, please try again": "Что-то пошло не так, попробуйте еще раз",
|
||||||
"Song lyrics": "Текст песни...",
|
"Song lyrics": "Текст песни...",
|
||||||
|
@ -444,6 +440,7 @@
|
||||||
"actions": "действия",
|
"actions": "действия",
|
||||||
"add link": "добавить ссылку",
|
"add link": "добавить ссылку",
|
||||||
"all topics": "все темы",
|
"all topics": "все темы",
|
||||||
|
"and some more authors": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
|
||||||
"article": "статья",
|
"article": "статья",
|
||||||
"author": "автор",
|
"author": "автор",
|
||||||
"authors": "авторы",
|
"authors": "авторы",
|
||||||
|
@ -467,6 +464,7 @@
|
||||||
"feed": "лента",
|
"feed": "лента",
|
||||||
"follower": "подписчик",
|
"follower": "подписчик",
|
||||||
"followersWithCount": "{count} {count, plural, one {подписчик} few {подписчика} other {подписчиков}}",
|
"followersWithCount": "{count} {count, plural, one {подписчик} few {подписчика} other {подписчиков}}",
|
||||||
|
"from": "от",
|
||||||
"header 1": "заголовок 1",
|
"header 1": "заголовок 1",
|
||||||
"header 2": "заголовок 2",
|
"header 2": "заголовок 2",
|
||||||
"header 3": "заголовок 3",
|
"header 3": "заголовок 3",
|
||||||
|
|
|
@ -299,7 +299,7 @@ export const FullArticle = (props: Props) => {
|
||||||
const ogTitle = props.article.title
|
const ogTitle = props.article.title
|
||||||
const keywords = getKeywords(props.article)
|
const keywords = getKeywords(props.article)
|
||||||
const getAuthorName = (a: Author) => {
|
const getAuthorName = (a: Author) => {
|
||||||
return lang() == 'en' && isCyrillic(a.name) ? capitalize(a.slug.replace(/-/, ' ')) : a.name
|
return lang() === 'en' && isCyrillic(a.name) ? capitalize(a.slug.replace(/-/, ' ')) : a.name
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { createMemo, createSignal, Match, Show, Switch } from 'solid-js'
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
import { useSession } from '../../../context/session'
|
import { useSession } from '../../../context/session'
|
||||||
import { ChatMember } from '../../../graphql/schema/chat.gen'
|
|
||||||
import { Author, FollowingEntity } from '../../../graphql/schema/core.gen'
|
import { Author, FollowingEntity } from '../../../graphql/schema/core.gen'
|
||||||
import { router, useRouter } from '../../../stores/router'
|
import { router, useRouter } from '../../../stores/router'
|
||||||
import { follow, unfollow } from '../../../stores/zine/common'
|
import { follow, unfollow } from '../../../stores/zine/common'
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
|
import { createEffect, createSignal } from 'solid-js'
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import { apiClient } from '../../../graphql/client/core'
|
||||||
|
import { SearchResult } from '../../../graphql/schema/core.gen'
|
||||||
import { Icon } from '../../_shared/Icon'
|
import { Icon } from '../../_shared/Icon'
|
||||||
|
|
||||||
import styles from './SearchModal.module.scss'
|
import styles from './SearchModal.module.scss'
|
||||||
import { apiClient } from '../../../graphql/client/core'
|
|
||||||
import { createEffect, createSignal } from 'solid-js'
|
|
||||||
import { SearchResult } from '../../../graphql/schema/core.gen'
|
|
||||||
|
|
||||||
export const SearchModal = () => {
|
export const SearchModal = () => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
|
|
|
@ -0,0 +1,194 @@
|
||||||
|
import { getPagePath, openPage } from '@nanostores/router'
|
||||||
|
import { clsx } from 'clsx'
|
||||||
|
import { createEffect, For, Show } from 'solid-js'
|
||||||
|
|
||||||
|
import { useLocalize } from '../../../context/localize'
|
||||||
|
import { useNotifications } from '../../../context/notifications'
|
||||||
|
import { Reaction } from '../../../graphql/schema/core.gen'
|
||||||
|
import { Notification } from '../../../graphql/schema/notifier.gen'
|
||||||
|
import { useRouter, router } from '../../../stores/router'
|
||||||
|
import { GroupAvatar } from '../../_shared/GroupAvatar'
|
||||||
|
import { TimeAgo } from '../../_shared/TimeAgo'
|
||||||
|
import { ArticlePageSearchParams } from '../../Article/FullArticle'
|
||||||
|
|
||||||
|
import styles from './NotificationView.module.scss'
|
||||||
|
|
||||||
|
type NotificationGroupProps = {
|
||||||
|
notifications: Notification[]
|
||||||
|
onClick: () => void
|
||||||
|
dateTimeFormat: 'ago' | 'time' | 'date'
|
||||||
|
class?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const getTitle = (title: string) => {
|
||||||
|
let shoutTitle = ''
|
||||||
|
let i = 0
|
||||||
|
const shoutTitleWords = title.split(' ')
|
||||||
|
|
||||||
|
while (shoutTitle.length <= 30 && i < shoutTitleWords.length) {
|
||||||
|
shoutTitle += shoutTitleWords[i] + ' '
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shoutTitle.length < title.length) {
|
||||||
|
shoutTitle = `${shoutTitle.trim()}...`
|
||||||
|
|
||||||
|
if (shoutTitle[0] === '«') {
|
||||||
|
shoutTitle += '»'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return shoutTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
const reactionsCaption = (threadId: string) =>
|
||||||
|
threadId.includes('__') ? 'Some new replies to your comment' : 'Some new comments to your publication'
|
||||||
|
|
||||||
|
export const NotificationGroup = (props: NotificationGroupProps) => {
|
||||||
|
const { t, formatTime, formatDate } = useLocalize()
|
||||||
|
const { changeSearchParam } = useRouter<ArticlePageSearchParams>()
|
||||||
|
const {
|
||||||
|
actions: { hideNotificationsPanel, markNotificationAsRead },
|
||||||
|
} = useNotifications()
|
||||||
|
const threads = new Map<string, Reaction[]>()
|
||||||
|
const notificationsByThread = new Map<string, Notification[]>()
|
||||||
|
|
||||||
|
const handleClick = (threadId: string) => {
|
||||||
|
props.onClick()
|
||||||
|
|
||||||
|
notificationsByThread.get(threadId).forEach((n: Notification) => {
|
||||||
|
if (!n.seen) markNotificationAsRead(n)
|
||||||
|
})
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
hideNotificationsPanel()
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<For each={[...threads.entries()]}>
|
||||||
|
{([threadId, reactions], _index) => (
|
||||||
|
<>
|
||||||
|
{t(reactionsCaption(threadId), { commentsCount: reactions.length })}{' '}
|
||||||
|
<div
|
||||||
|
class={clsx(styles.NotificationView, props.class, {
|
||||||
|
[styles.seen]: threadId.endsWith('::seen'),
|
||||||
|
})}
|
||||||
|
onClick={(_) => handleClick(threadId)}
|
||||||
|
>
|
||||||
|
<div class={styles.userpic}>
|
||||||
|
<GroupAvatar authors={reactions.map((r: Reaction) => r.created_by)} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href={getPagePath(router, 'article', { slug: reactions[-1].shout.slug })}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
>
|
||||||
|
{getTitle(reactions[-1].shout.title)}
|
||||||
|
</a>{' '}
|
||||||
|
{t('from')}{' '}
|
||||||
|
<a
|
||||||
|
href={getPagePath(router, 'author', { slug: reactions[-1].created_by.slug })}
|
||||||
|
onClick={handleLinkClick}
|
||||||
|
>
|
||||||
|
{reactions[-1].created_by.name}
|
||||||
|
</a>{' '}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class={styles.timeContainer}>
|
||||||
|
<Show when={props.dateTimeFormat === 'ago'}>
|
||||||
|
<TimeAgo date={reactions[-1].created_at} />
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.dateTimeFormat === 'time'}>
|
||||||
|
{formatTime(new Date(reactions[-1].created_at))}
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Show when={props.dateTimeFormat === 'date'}>
|
||||||
|
{formatDate(new Date(reactions[-1].created_at), { month: 'numeric', year: '2-digit' })}
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,164 +0,0 @@
|
||||||
import type { ArticlePageSearchParams } from '../../Article/FullArticle'
|
|
||||||
|
|
||||||
import { getPagePath, openPage } from '@nanostores/router'
|
|
||||||
import { clsx } from 'clsx'
|
|
||||||
import { createMemo, createSignal, onMount, Show } from 'solid-js'
|
|
||||||
|
|
||||||
import { useLocalize } from '../../../context/localize'
|
|
||||||
import { useNotifications } from '../../../context/notifications'
|
|
||||||
import { Reaction } from '../../../graphql/schema/core.gen'
|
|
||||||
import { Notification } from '../../../graphql/schema/notifier.gen'
|
|
||||||
import { router, useRouter } from '../../../stores/router'
|
|
||||||
import { GroupAvatar } from '../../_shared/GroupAvatar'
|
|
||||||
import { TimeAgo } from '../../_shared/TimeAgo'
|
|
||||||
|
|
||||||
import styles from './NotificationView.module.scss'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
notification: Notification
|
|
||||||
onClick: () => void
|
|
||||||
dateTimeFormat: 'ago' | 'time' | 'date'
|
|
||||||
class?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NotificationView = (props: Props) => {
|
|
||||||
const {
|
|
||||||
actions: { markNotificationAsRead, hideNotificationsPanel },
|
|
||||||
} = useNotifications()
|
|
||||||
|
|
||||||
const { changeSearchParam } = useRouter<ArticlePageSearchParams>() // TODO: use search params
|
|
||||||
|
|
||||||
const { t, formatDate, formatTime } = useLocalize()
|
|
||||||
|
|
||||||
const [data, setData] = createSignal<Reaction>(null) // NOTE: supports only SSMessage.entity == "reaction"
|
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
setTimeout(() => setData(JSON.parse(props.notification.payload)))
|
|
||||||
})
|
|
||||||
|
|
||||||
const lastUser = createMemo(() => data().created_by)
|
|
||||||
|
|
||||||
const handleLinkClick = (event: MouseEvent) => {
|
|
||||||
event.stopPropagation()
|
|
||||||
hideNotificationsPanel()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = `${shoutTitle.trim()}...`
|
|
||||||
|
|
||||||
if (shoutTitle[0] === '«') {
|
|
||||||
shoutTitle += '»'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (props.notification.action) {
|
|
||||||
case 'create': {
|
|
||||||
if (data()?.reply_to) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{t('NotificationNewReplyText1', {
|
|
||||||
commentsCount: 0, // FIXME: props.notification.occurrences,
|
|
||||||
})}{' '}
|
|
||||||
<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: 0, // FIXME: data().users.length - 1,
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{t('NotificationNewCommentText1', {
|
|
||||||
commentsCount: 0, // FIXME: props.notification.occurrences,
|
|
||||||
})}{' '}
|
|
||||||
<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: 0, // FIXME: data().users.length - 1,
|
|
||||||
})}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleClick = () => {
|
|
||||||
props.onClick()
|
|
||||||
|
|
||||||
if (!props.notification.seen) {
|
|
||||||
markNotificationAsRead(props.notification)
|
|
||||||
}
|
|
||||||
|
|
||||||
openPage(router, 'article', { slug: data().shout.slug })
|
|
||||||
// FIXME:
|
|
||||||
// if (data().reactionIds) {
|
|
||||||
// changeSearchParam({ commentId: data().reactionIds[0].toString() })
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
const formattedDateTime = createMemo(() => {
|
|
||||||
switch (props.dateTimeFormat) {
|
|
||||||
case 'ago': {
|
|
||||||
return <TimeAgo date={props.notification.created_at} />
|
|
||||||
}
|
|
||||||
case 'time': {
|
|
||||||
return formatTime(new Date(props.notification.created_at))
|
|
||||||
}
|
|
||||||
case 'date': {
|
|
||||||
return formatDate(new Date(props.notification.created_at), { month: 'numeric', year: '2-digit' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Show when={data()}>
|
|
||||||
<div
|
|
||||||
class={clsx(styles.NotificationView, props.class, {
|
|
||||||
[styles.seen]: props.notification.seen,
|
|
||||||
})}
|
|
||||||
onClick={handleClick}
|
|
||||||
>
|
|
||||||
<div class={styles.userpic}>
|
|
||||||
<GroupAvatar authors={[] /*d FIXME: data().users */} />
|
|
||||||
</div>
|
|
||||||
<div>{content()}</div>
|
|
||||||
<div class={styles.timeContainer}>{formattedDateTime()}</div>
|
|
||||||
</div>
|
|
||||||
</Show>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1 +1 @@
|
||||||
export { NotificationView } from './NotificationView'
|
export { NotificationGroup } from './NotificationGroup'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { clsx } from 'clsx'
|
import { clsx } from 'clsx'
|
||||||
import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show } from 'solid-js'
|
import { createEffect, createMemo, createSignal, on, onCleanup, onMount, Show } from 'solid-js'
|
||||||
import { throttle } from 'throttle-debounce'
|
import { throttle } from 'throttle-debounce'
|
||||||
|
|
||||||
import { useLocalize } from '../../context/localize'
|
import { useLocalize } from '../../context/localize'
|
||||||
|
@ -11,7 +11,7 @@ import { Button } from '../_shared/Button'
|
||||||
import { Icon } from '../_shared/Icon'
|
import { Icon } from '../_shared/Icon'
|
||||||
|
|
||||||
import { EmptyMessage } from './EmptyMessage'
|
import { EmptyMessage } from './EmptyMessage'
|
||||||
import { NotificationView } from './NotificationView'
|
import { NotificationGroup } from './NotificationView/NotificationGroup'
|
||||||
|
|
||||||
import styles from './NotificationsPanel.module.scss'
|
import styles from './NotificationsPanel.module.scss'
|
||||||
|
|
||||||
|
@ -186,42 +186,30 @@ export const NotificationsPanel = (props: Props) => {
|
||||||
<div class="col-xs-24">
|
<div class="col-xs-24">
|
||||||
<Show when={todayNotifications().length > 0}>
|
<Show when={todayNotifications().length > 0}>
|
||||||
<div class={styles.periodTitle}>{t('today')}</div>
|
<div class={styles.periodTitle}>{t('today')}</div>
|
||||||
<For each={todayNotifications()}>
|
<NotificationGroup
|
||||||
{(notification) => (
|
notifications={todayNotifications()}
|
||||||
<NotificationView
|
|
||||||
notification={notification}
|
|
||||||
class={styles.notificationView}
|
class={styles.notificationView}
|
||||||
onClick={handleNotificationViewClick}
|
onClick={handleNotificationViewClick}
|
||||||
dateTimeFormat={'ago'}
|
dateTimeFormat={'ago'}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={yesterdayNotifications().length > 0}>
|
<Show when={yesterdayNotifications().length > 0}>
|
||||||
<div class={styles.periodTitle}>{t('yesterday')}</div>
|
<div class={styles.periodTitle}>{t('yesterday')}</div>
|
||||||
<For each={yesterdayNotifications()}>
|
<NotificationGroup
|
||||||
{(notification) => (
|
notifications={yesterdayNotifications()}
|
||||||
<NotificationView
|
|
||||||
notification={notification}
|
|
||||||
class={styles.notificationView}
|
class={styles.notificationView}
|
||||||
onClick={handleNotificationViewClick}
|
onClick={handleNotificationViewClick}
|
||||||
dateTimeFormat={'time'}
|
dateTimeFormat={'time'}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={earlierNotifications().length > 0}>
|
<Show when={earlierNotifications().length > 0}>
|
||||||
<div class={styles.periodTitle}>{t('earlier')}</div>
|
<div class={styles.periodTitle}>{t('earlier')}</div>
|
||||||
<For each={earlierNotifications()}>
|
<NotificationGroup
|
||||||
{(notification) => (
|
notifications={earlierNotifications()}
|
||||||
<NotificationView
|
|
||||||
notification={notification}
|
|
||||||
class={styles.notificationView}
|
class={styles.notificationView}
|
||||||
onClick={handleNotificationViewClick}
|
onClick={handleNotificationViewClick}
|
||||||
dateTimeFormat={'date'}
|
dateTimeFormat={'date'}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</For>
|
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -48,11 +48,9 @@ export const ProfileSettings = () => {
|
||||||
const {
|
const {
|
||||||
actions: { showSnackbar },
|
actions: { showSnackbar },
|
||||||
} = useSnackbar()
|
} = useSnackbar()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actions: { setUser, authorizer },
|
actions: { loadAuthor },
|
||||||
} = useSession()
|
} = useSession()
|
||||||
|
|
||||||
const {
|
const {
|
||||||
actions: { showConfirm },
|
actions: { showConfirm },
|
||||||
} = useConfirm()
|
} = useConfirm()
|
||||||
|
@ -101,10 +99,8 @@ export const ProfileSettings = () => {
|
||||||
}
|
}
|
||||||
showSnackbar({ type: 'error', body: t('Error') })
|
showSnackbar({ type: 'error', body: t('Error') })
|
||||||
}
|
}
|
||||||
const profile = await authorizer().getProfile()
|
|
||||||
if (profile) {
|
await loadAuthor() // renews author's profile
|
||||||
setUser(profile)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCancel = async () => {
|
const handleCancel = async () => {
|
||||||
|
|
|
@ -52,7 +52,7 @@ type Props = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeedView = (props: Props) => {
|
export const FeedView = (props: Props) => {
|
||||||
const { t, lang } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { page, searchParams } = useRouter<FeedSearchParams>()
|
const { page, searchParams } = useRouter<FeedSearchParams>()
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
|
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
|
||||||
|
@ -238,7 +238,7 @@ export const FeedView = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class={styles.commentDetails}>
|
<div class={styles.commentDetails}>
|
||||||
<AuthorLink author={comment.createdBy as Author} size={'XS'} />
|
<AuthorLink author={comment.created_by as Author} size={'XS'} />
|
||||||
<CommentDate comment={comment} isShort={true} isLastInRow={true} />
|
<CommentDate comment={comment} isShort={true} isLastInRow={true} />
|
||||||
</div>
|
</div>
|
||||||
<div class={clsx('text-truncate', styles.commentArticleTitle)}>
|
<div class={clsx('text-truncate', styles.commentArticleTitle)}>
|
||||||
|
|
|
@ -17,7 +17,6 @@ import DialogCard from '../Inbox/DialogCard'
|
||||||
import DialogHeader from '../Inbox/DialogHeader'
|
import DialogHeader from '../Inbox/DialogHeader'
|
||||||
import { Message } from '../Inbox/Message'
|
import { Message } from '../Inbox/Message'
|
||||||
import MessagesFallback from '../Inbox/MessagesFallback'
|
import MessagesFallback from '../Inbox/MessagesFallback'
|
||||||
import QuotedMessage from '../Inbox/QuotedMessage'
|
|
||||||
import Search from '../Inbox/Search'
|
import Search from '../Inbox/Search'
|
||||||
import { Modal } from '../Nav/Modal'
|
import { Modal } from '../Nav/Modal'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { SearchResult, Shout } from '../../graphql/schema/core.gen'
|
import type { SearchResult } from '../../graphql/schema/core.gen'
|
||||||
|
|
||||||
import { Show, For, createSignal } from 'solid-js'
|
import { Show, For, createSignal } from 'solid-js'
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ const LOAD_MORE_PAGE_SIZE = 50
|
||||||
|
|
||||||
export const SearchView = (props: Props) => {
|
export const SearchView = (props: Props) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const { articleEntities, sortedArticles } = useArticlesStore()
|
const { sortedArticles } = useArticlesStore()
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
const [query, setQuery] = createSignal(props.query)
|
const [query, setQuery] = createSignal(props.query)
|
||||||
const [offset, setOffset] = createSignal(0)
|
const [offset, setOffset] = createSignal(0)
|
||||||
|
@ -54,7 +54,7 @@ export const SearchView = (props: Props) => {
|
||||||
name="q"
|
name="q"
|
||||||
ref={searchEl}
|
ref={searchEl}
|
||||||
onInput={handleQueryChange}
|
onInput={handleQueryChange}
|
||||||
placeholder={t('Enter text') + '...'}
|
placeholder={query() || t('Enter text') + '...'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
|
|
|
@ -55,7 +55,7 @@ export const TopicView = (props: Props) => {
|
||||||
)
|
)
|
||||||
const title = () =>
|
const title = () =>
|
||||||
`#${capitalize(
|
`#${capitalize(
|
||||||
lang() == 'en' ? topic()?.slug.replace(/-/, ' ') : topic()?.title || topic()?.slug.replace(/-/, ' '),
|
lang() === 'en' ? topic()?.slug.replace(/-/, ' ') : topic()?.title || topic()?.slug.replace(/-/, ' '),
|
||||||
true,
|
true,
|
||||||
)}`
|
)}`
|
||||||
onMount(() => (document.title = title()))
|
onMount(() => (document.title = title()))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { Accessor, JSX } from 'solid-js'
|
import type { Accessor, JSX } from 'solid-js'
|
||||||
|
|
||||||
import { createStorageSignal } from '@solid-primitives/storage'
|
import { createStorageSignal } from '@solid-primitives/storage'
|
||||||
import { createContext, createEffect, createMemo, createSignal, onMount, useContext } from 'solid-js'
|
import { createContext, createMemo, createSignal, onMount, useContext } from 'solid-js'
|
||||||
import { createStore } from 'solid-js/store'
|
import { createStore } from 'solid-js/store'
|
||||||
import { Portal } from 'solid-js/web'
|
import { Portal } from 'solid-js/web'
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
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<number, Notification>>({})
|
||||||
const { isAuthenticated } = useSession()
|
const { isAuthenticated, author } = useSession()
|
||||||
const { addHandler } = useConnect()
|
const { addHandler } = useConnect()
|
||||||
|
|
||||||
const loadNotifications = async (options: { after: number; limit?: number; offset?: number }) => {
|
const loadNotifications = async (options: { after: number; limit?: number; offset?: number }) => {
|
||||||
|
@ -81,10 +81,11 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
|
||||||
|
|
||||||
const markNotificationAsRead = async (notification: Notification) => {
|
const markNotificationAsRead = async (notification: Notification) => {
|
||||||
if (notifierClient.private) await notifierClient.markNotificationAsRead(notification.id)
|
if (notifierClient.private) await notifierClient.markNotificationAsRead(notification.id)
|
||||||
const nnn = new Set([...notification.seen, notification.id])
|
notification.seen.push(author().id)
|
||||||
setNotificationEntities(notification.id, 'seen', [...nnn])
|
setNotificationEntities((nnn: Notification) => ({ ...nnn, [notification.id]: notification }))
|
||||||
setUnreadNotificationsCount((oldCount) => oldCount - 1)
|
setUnreadNotificationsCount((oldCount) => oldCount - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
const markAllNotificationsAsRead = async () => {
|
const markAllNotificationsAsRead = async () => {
|
||||||
if (isAuthenticated() && notifierClient.private) {
|
if (isAuthenticated() && notifierClient.private) {
|
||||||
await notifierClient.markAllNotificationsAsRead()
|
await notifierClient.markAllNotificationsAsRead()
|
||||||
|
|
|
@ -30,10 +30,7 @@ const userpicUrl = (userpic: string) => {
|
||||||
return userpic
|
return userpic
|
||||||
}
|
}
|
||||||
export const ProfileFormProvider = (props: { children: JSX.Element }) => {
|
export const ProfileFormProvider = (props: { children: JSX.Element }) => {
|
||||||
const {
|
const { author } = useSession()
|
||||||
author,
|
|
||||||
actions: { getToken },
|
|
||||||
} = useSession()
|
|
||||||
const [form, setForm] = createStore<ProfileInput>({})
|
const [form, setForm] = createStore<ProfileInput>({})
|
||||||
|
|
||||||
const currentSlug = createMemo(() => author()?.slug)
|
const currentSlug = createMemo(() => author()?.slug)
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import type { JSX } from 'solid-js'
|
import type { JSX } from 'solid-js'
|
||||||
|
|
||||||
import { createContext, createMemo, onCleanup, useContext } from 'solid-js'
|
import { createContext, onCleanup, useContext } from 'solid-js'
|
||||||
import { createStore, reconcile } from 'solid-js/store'
|
import { createStore, reconcile } from 'solid-js/store'
|
||||||
|
|
||||||
import { apiClient } from '../graphql/client/core'
|
import { apiClient } from '../graphql/client/core'
|
||||||
import { Reaction, ReactionBy, ReactionInput, ReactionKind } from '../graphql/schema/core.gen'
|
import { Reaction, ReactionBy, ReactionInput, ReactionKind } from '../graphql/schema/core.gen'
|
||||||
|
|
||||||
import { useSession } from './session'
|
|
||||||
|
|
||||||
type ReactionsContextType = {
|
type ReactionsContextType = {
|
||||||
reactionEntities: Record<number, Reaction>
|
reactionEntities: Record<number, Reaction>
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -34,9 +32,6 @@ export function useReactions() {
|
||||||
|
|
||||||
export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
|
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
|
||||||
const {
|
|
||||||
actions: { getToken },
|
|
||||||
} = useSession()
|
|
||||||
|
|
||||||
const loadReactionsBy = async ({
|
const loadReactionsBy = async ({
|
||||||
by,
|
by,
|
||||||
|
|
|
@ -48,6 +48,7 @@ export type SessionContextType = {
|
||||||
loadSession: () => AuthToken | Promise<AuthToken>
|
loadSession: () => AuthToken | Promise<AuthToken>
|
||||||
setSession: (token: AuthToken | null) => void // setSession
|
setSession: (token: AuthToken | null) => void // setSession
|
||||||
loadAuthor: (info?: unknown) => Author | Promise<Author>
|
loadAuthor: (info?: unknown) => Author | Promise<Author>
|
||||||
|
setAuthor: (a: Author) => void
|
||||||
loadSubscriptions: () => Promise<void>
|
loadSubscriptions: () => Promise<void>
|
||||||
requireAuthentication: (
|
requireAuthentication: (
|
||||||
callback: (() => Promise<void>) | (() => void),
|
callback: (() => Promise<void>) | (() => void),
|
||||||
|
@ -92,14 +93,14 @@ export const SessionProvider = (props: {
|
||||||
Authorization: tkn,
|
Authorization: tkn,
|
||||||
})
|
})
|
||||||
if (authResult?.access_token) {
|
if (authResult?.access_token) {
|
||||||
mutate(authResult)
|
setSession(authResult)
|
||||||
// console.debug('[context.session] token after:', authResult.access_token)
|
// console.debug('[context.session] token after:', authResult.access_token)
|
||||||
await loadSubscriptions()
|
await loadSubscriptions()
|
||||||
return authResult
|
return authResult
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[context.session] getSession error:', error)
|
console.error('[context.session] getSession error:', error)
|
||||||
mutate(null)
|
setSession(null)
|
||||||
return null
|
return null
|
||||||
} finally {
|
} finally {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -108,7 +109,7 @@ export const SessionProvider = (props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [session, { refetch: loadSession, mutate }] = createResource<AuthToken>(getSession, {
|
const [session, { refetch: loadSession, mutate: setSession }] = createResource<AuthToken>(getSession, {
|
||||||
ssrLoadFrom: 'initial',
|
ssrLoadFrom: 'initial',
|
||||||
initialValue: null,
|
initialValue: null,
|
||||||
})
|
})
|
||||||
|
@ -142,7 +143,7 @@ export const SessionProvider = (props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [author, { refetch: loadAuthor }] = createResource<Author | null>(
|
const [author, { refetch: loadAuthor, mutate: setAuthor }] = createResource<Author | null>(
|
||||||
async () => {
|
async () => {
|
||||||
const u = session()?.user
|
const u = session()?.user
|
||||||
if (u) {
|
if (u) {
|
||||||
|
@ -162,7 +163,7 @@ export const SessionProvider = (props: {
|
||||||
const authResult: AuthToken | void = await authorizer().login(params)
|
const authResult: AuthToken | void = await authorizer().login(params)
|
||||||
|
|
||||||
if (authResult && authResult.access_token) {
|
if (authResult && authResult.access_token) {
|
||||||
mutate(authResult)
|
setSession(authResult)
|
||||||
await loadSubscriptions()
|
await loadSubscriptions()
|
||||||
console.debug('[context.session] signed in')
|
console.debug('[context.session] signed in')
|
||||||
} else {
|
} else {
|
||||||
|
@ -211,7 +212,7 @@ export const SessionProvider = (props: {
|
||||||
setIsAuthWithCallback(() => callback)
|
setIsAuthWithCallback(() => callback)
|
||||||
|
|
||||||
const userdata = await authorizer().getProfile()
|
const userdata = await authorizer().getProfile()
|
||||||
if (userdata) mutate({ ...session(), user: userdata })
|
if (userdata) setSession({ ...session(), user: userdata })
|
||||||
|
|
||||||
if (!isAuthenticated()) {
|
if (!isAuthenticated()) {
|
||||||
showModal('auth', modalSource)
|
showModal('auth', modalSource)
|
||||||
|
@ -220,7 +221,7 @@ export const SessionProvider = (props: {
|
||||||
|
|
||||||
const signOut = async () => {
|
const signOut = async () => {
|
||||||
await authorizer().logout()
|
await authorizer().logout()
|
||||||
mutate(null)
|
setSession(null)
|
||||||
setSubscriptions(EMPTY_SUBSCRIPTIONS)
|
setSubscriptions(EMPTY_SUBSCRIPTIONS)
|
||||||
showSnackbar({ body: t("You've successfully logged out") })
|
showSnackbar({ body: t("You've successfully logged out") })
|
||||||
}
|
}
|
||||||
|
@ -228,7 +229,7 @@ export const SessionProvider = (props: {
|
||||||
const confirmEmail = async (input: VerifyEmailInput) => {
|
const confirmEmail = async (input: VerifyEmailInput) => {
|
||||||
console.debug(`[context.session] calling authorizer's verify email with`, input)
|
console.debug(`[context.session] calling authorizer's verify email with`, input)
|
||||||
const at: void | AuthToken = await authorizer().verifyEmail(input)
|
const at: void | AuthToken = await authorizer().verifyEmail(input)
|
||||||
if (at) mutate(at)
|
if (at) setSession(at)
|
||||||
console.log(`[context.session] confirmEmail got result ${at}`)
|
console.log(`[context.session] confirmEmail got result ${at}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +244,8 @@ export const SessionProvider = (props: {
|
||||||
signOut,
|
signOut,
|
||||||
confirmEmail,
|
confirmEmail,
|
||||||
setIsSessionLoaded,
|
setIsSessionLoaded,
|
||||||
setSession: mutate,
|
setSession,
|
||||||
|
setAuthor,
|
||||||
authorizer,
|
authorizer,
|
||||||
loadAuthor,
|
loadAuthor,
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const onBeforeRender = async (pageContext: PageContext) => {
|
||||||
const { layout } = pageContext.routeParams
|
const { layout } = pageContext.routeParams
|
||||||
|
|
||||||
const expoShouts = await apiClient.getShouts({
|
const expoShouts = await apiClient.getShouts({
|
||||||
filters: { layouts: ['audio', 'video', 'literature', 'image'] },
|
filters: { layouts: layout ? [layout] : ['audio', 'video', 'literature', 'image'] },
|
||||||
limit: PRERENDERED_ARTICLES_COUNT,
|
limit: PRERENDERED_ARTICLES_COUNT,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import type {
|
import type {
|
||||||
Author,
|
Author,
|
||||||
Shout,
|
Shout,
|
||||||
ShoutInput,
|
|
||||||
LoadShoutsOptions,
|
LoadShoutsOptions,
|
||||||
QueryLoad_Shouts_SearchArgs,
|
QueryLoad_Shouts_SearchArgs,
|
||||||
} from '../../graphql/schema/core.gen'
|
} from '../../graphql/schema/core.gen'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user