notification-group-view

This commit is contained in:
Untone 2023-12-20 19:54:20 +03:00
parent db1c00e8af
commit 43b3de572b
21 changed files with 256 additions and 254 deletions

View File

@ -236,12 +236,6 @@
"No notifications yet": "No notifications yet",
"Nothing here yet": "There's nothing here yet",
"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",
"Or paste a link to an image": "Or paste a link to an image",
"Ordered list": "Ordered list",
@ -318,6 +312,8 @@
"Slug": "Slug",
"Social networks": "Social networks",
"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, please try again": "Something went wrong, please try again",
"Song lyrics": "Song lyrics...",
@ -421,6 +417,7 @@
"actions": "actions",
"add link": "add link",
"all topics": "all topics",
"and some more authors": "{restUsersCount, plural, =0 {} one { and one more user} other { and more {restUsersCount} users}}",
"article": "article",
"author": "author",
"authors": "authors",
@ -442,6 +439,7 @@
"feed": "feed",
"follower": "follower",
"followersWithCount": "{count} {count, plural, one {follower} other {followers}}",
"from": "from",
"header 1": "header 1",
"header 2": "header 2",
"header 3": "header 3",

View File

@ -248,12 +248,6 @@
"No such account, please try to register": "Такой адрес не найден, попробуйте зарегистрироваться",
"Nothing here yet": "Здесь пока ничего нет",
"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": "Уведомления",
"Or paste a link to an image": "Или вставьте ссылку на изображение",
"Ordered list": "Нумерованный список",
@ -338,6 +332,8 @@
"Slug": "Постоянная ссылка",
"Social networks": "Социальные сети",
"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, please try again": "Что-то пошло не так, попробуйте еще раз",
"Song lyrics": "Текст песни...",
@ -444,6 +440,7 @@
"actions": "действия",
"add link": "добавить ссылку",
"all topics": "все темы",
"and some more authors": "{restUsersCount, plural, =0 {} one { и ещё 1 пользователя} few { и ещё {restUsersCount} пользователей} other { и ещё {restUsersCount} пользователей}}",
"article": "статья",
"author": "автор",
"authors": "авторы",
@ -467,6 +464,7 @@
"feed": "лента",
"follower": "подписчик",
"followersWithCount": "{count} {count, plural, one {подписчик} few {подписчика} other {подписчиков}}",
"from": "от",
"header 1": "заголовок 1",
"header 2": "заголовок 2",
"header 3": "заголовок 3",

View File

@ -299,7 +299,7 @@ export const FullArticle = (props: Props) => {
const ogTitle = props.article.title
const keywords = getKeywords(props.article)
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 (
<>

View File

@ -4,7 +4,6 @@ import { createMemo, createSignal, Match, Show, Switch } from 'solid-js'
import { useLocalize } from '../../../context/localize'
import { useSession } from '../../../context/session'
import { ChatMember } from '../../../graphql/schema/chat.gen'
import { Author, FollowingEntity } from '../../../graphql/schema/core.gen'
import { router, useRouter } from '../../../stores/router'
import { follow, unfollow } from '../../../stores/zine/common'

View File

@ -59,7 +59,7 @@ const CreateModalContent = (props: Props) => {
const handleCreate = async () => {
try {
const initChat = await actions.createChat(usersId(), chatTitle())
console.debug('[components.Inbox] create chat result: ', initChat)
console.debug('[components.Inbox] create chat result:', initChat)
hideModal()
await actions.loadChats()
} catch (error) {

View File

@ -1,12 +1,12 @@
import { clsx } from 'clsx'
import { createEffect, createSignal } from 'solid-js'
import { useLocalize } from '../../../context/localize'
import { apiClient } from '../../../graphql/client/core'
import { SearchResult } from '../../../graphql/schema/core.gen'
import { Icon } from '../../_shared/Icon'
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 = () => {
const { t } = useLocalize()

View File

@ -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>
</>
)
}

View File

@ -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>
)
}

View File

@ -1 +1 @@
export { NotificationView } from './NotificationView'
export { NotificationGroup } from './NotificationGroup'

View File

@ -1,5 +1,5 @@
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 { useLocalize } from '../../context/localize'
@ -11,7 +11,7 @@ import { Button } from '../_shared/Button'
import { Icon } from '../_shared/Icon'
import { EmptyMessage } from './EmptyMessage'
import { NotificationView } from './NotificationView'
import { NotificationGroup } from './NotificationView/NotificationGroup'
import styles from './NotificationsPanel.module.scss'
@ -186,42 +186,30 @@ export const NotificationsPanel = (props: Props) => {
<div class="col-xs-24">
<Show when={todayNotifications().length > 0}>
<div class={styles.periodTitle}>{t('today')}</div>
<For each={todayNotifications()}>
{(notification) => (
<NotificationView
notification={notification}
<NotificationGroup
notifications={todayNotifications()}
class={styles.notificationView}
onClick={handleNotificationViewClick}
dateTimeFormat={'ago'}
/>
)}
</For>
</Show>
<Show when={yesterdayNotifications().length > 0}>
<div class={styles.periodTitle}>{t('yesterday')}</div>
<For each={yesterdayNotifications()}>
{(notification) => (
<NotificationView
notification={notification}
<NotificationGroup
notifications={yesterdayNotifications()}
class={styles.notificationView}
onClick={handleNotificationViewClick}
dateTimeFormat={'time'}
/>
)}
</For>
</Show>
<Show when={earlierNotifications().length > 0}>
<div class={styles.periodTitle}>{t('earlier')}</div>
<For each={earlierNotifications()}>
{(notification) => (
<NotificationView
notification={notification}
<NotificationGroup
notifications={earlierNotifications()}
class={styles.notificationView}
onClick={handleNotificationViewClick}
dateTimeFormat={'date'}
/>
)}
</For>
</Show>
</div>
</div>

View File

@ -48,11 +48,9 @@ export const ProfileSettings = () => {
const {
actions: { showSnackbar },
} = useSnackbar()
const {
actions: { setUser, authorizer },
actions: { loadAuthor },
} = useSession()
const {
actions: { showConfirm },
} = useConfirm()
@ -101,10 +99,8 @@ export const ProfileSettings = () => {
}
showSnackbar({ type: 'error', body: t('Error') })
}
const profile = await authorizer().getProfile()
if (profile) {
setUser(profile)
}
await loadAuthor() // renews author's profile
}
const handleCancel = async () => {

View File

@ -52,7 +52,7 @@ type Props = {
}
export const FeedView = (props: Props) => {
const { t, lang } = useLocalize()
const { t } = useLocalize()
const { page, searchParams } = useRouter<FeedSearchParams>()
const [isLoading, setIsLoading] = createSignal(false)
const [isRightColumnLoaded, setIsRightColumnLoaded] = createSignal(false)
@ -238,7 +238,7 @@ export const FeedView = (props: Props) => {
/>
</div>
<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} />
</div>
<div class={clsx('text-truncate', styles.commentArticleTitle)}>

View File

@ -17,7 +17,6 @@ import DialogCard from '../Inbox/DialogCard'
import DialogHeader from '../Inbox/DialogHeader'
import { Message } from '../Inbox/Message'
import MessagesFallback from '../Inbox/MessagesFallback'
import QuotedMessage from '../Inbox/QuotedMessage'
import Search from '../Inbox/Search'
import { Modal } from '../Nav/Modal'

View File

@ -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'
@ -22,7 +22,7 @@ const LOAD_MORE_PAGE_SIZE = 50
export const SearchView = (props: Props) => {
const { t } = useLocalize()
const { articleEntities, sortedArticles } = useArticlesStore()
const { sortedArticles } = useArticlesStore()
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
const [query, setQuery] = createSignal(props.query)
const [offset, setOffset] = createSignal(0)
@ -54,7 +54,7 @@ export const SearchView = (props: Props) => {
name="q"
ref={searchEl}
onInput={handleQueryChange}
placeholder={t('Enter text') + '...'}
placeholder={query() || t('Enter text') + '...'}
/>
</div>
<div class="col-sm-6">

View File

@ -55,7 +55,7 @@ export const TopicView = (props: Props) => {
)
const title = () =>
`#${capitalize(
lang() == 'en' ? topic()?.slug.replace(/-/, ' ') : topic()?.title || topic()?.slug.replace(/-/, ' '),
lang() === 'en' ? topic()?.slug.replace(/-/, ' ') : topic()?.title || topic()?.slug.replace(/-/, ' '),
true,
)}`
onMount(() => (document.title = title()))

View File

@ -1,7 +1,7 @@
import type { Accessor, JSX } from 'solid-js'
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 { Portal } from 'solid-js/web'
@ -41,7 +41,7 @@ export const NotificationsProvider = (props: { children: JSX.Element }) => {
const [unreadNotificationsCount, setUnreadNotificationsCount] = createSignal(0)
const [totalNotificationsCount, setTotalNotificationsCount] = createSignal(0)
const [notificationEntities, setNotificationEntities] = createStore<Record<number, Notification>>({})
const { isAuthenticated } = useSession()
const { isAuthenticated, author } = useSession()
const { addHandler } = useConnect()
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) => {
if (notifierClient.private) await notifierClient.markNotificationAsRead(notification.id)
const nnn = new Set([...notification.seen, notification.id])
setNotificationEntities(notification.id, 'seen', [...nnn])
notification.seen.push(author().id)
setNotificationEntities((nnn: Notification) => ({ ...nnn, [notification.id]: notification }))
setUnreadNotificationsCount((oldCount) => oldCount - 1)
}
const markAllNotificationsAsRead = async () => {
if (isAuthenticated() && notifierClient.private) {
await notifierClient.markAllNotificationsAsRead()

View File

@ -30,10 +30,7 @@ const userpicUrl = (userpic: string) => {
return userpic
}
export const ProfileFormProvider = (props: { children: JSX.Element }) => {
const {
author,
actions: { getToken },
} = useSession()
const { author } = useSession()
const [form, setForm] = createStore<ProfileInput>({})
const currentSlug = createMemo(() => author()?.slug)

View File

@ -1,13 +1,11 @@
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 { apiClient } from '../graphql/client/core'
import { Reaction, ReactionBy, ReactionInput, ReactionKind } from '../graphql/schema/core.gen'
import { useSession } from './session'
type ReactionsContextType = {
reactionEntities: Record<number, Reaction>
actions: {
@ -34,9 +32,6 @@ export function useReactions() {
export const ReactionsProvider = (props: { children: JSX.Element }) => {
const [reactionEntities, setReactionEntities] = createStore<Record<number, Reaction>>({})
const {
actions: { getToken },
} = useSession()
const loadReactionsBy = async ({
by,

View File

@ -48,6 +48,7 @@ export type SessionContextType = {
loadSession: () => AuthToken | Promise<AuthToken>
setSession: (token: AuthToken | null) => void // setSession
loadAuthor: (info?: unknown) => Author | Promise<Author>
setAuthor: (a: Author) => void
loadSubscriptions: () => Promise<void>
requireAuthentication: (
callback: (() => Promise<void>) | (() => void),
@ -92,14 +93,14 @@ export const SessionProvider = (props: {
Authorization: tkn,
})
if (authResult?.access_token) {
mutate(authResult)
setSession(authResult)
// console.debug('[context.session] token after:', authResult.access_token)
await loadSubscriptions()
return authResult
}
} catch (error) {
console.error('[context.session] getSession error:', error)
mutate(null)
setSession(null)
return null
} finally {
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',
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 () => {
const u = session()?.user
if (u) {
@ -162,7 +163,7 @@ export const SessionProvider = (props: {
const authResult: AuthToken | void = await authorizer().login(params)
if (authResult && authResult.access_token) {
mutate(authResult)
setSession(authResult)
await loadSubscriptions()
console.debug('[context.session] signed in')
} else {
@ -211,7 +212,7 @@ export const SessionProvider = (props: {
setIsAuthWithCallback(() => callback)
const userdata = await authorizer().getProfile()
if (userdata) mutate({ ...session(), user: userdata })
if (userdata) setSession({ ...session(), user: userdata })
if (!isAuthenticated()) {
showModal('auth', modalSource)
@ -220,7 +221,7 @@ export const SessionProvider = (props: {
const signOut = async () => {
await authorizer().logout()
mutate(null)
setSession(null)
setSubscriptions(EMPTY_SUBSCRIPTIONS)
showSnackbar({ body: t("You've successfully logged out") })
}
@ -228,7 +229,7 @@ export const SessionProvider = (props: {
const confirmEmail = async (input: VerifyEmailInput) => {
console.debug(`[context.session] calling authorizer's verify email with`, 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}`)
}
@ -243,7 +244,8 @@ export const SessionProvider = (props: {
signOut,
confirmEmail,
setIsSessionLoaded,
setSession: mutate,
setSession,
setAuthor,
authorizer,
loadAuthor,
}

View File

@ -8,7 +8,7 @@ export const onBeforeRender = async (pageContext: PageContext) => {
const { layout } = pageContext.routeParams
const expoShouts = await apiClient.getShouts({
filters: { layouts: ['audio', 'video', 'literature', 'image'] },
filters: { layouts: layout ? [layout] : ['audio', 'video', 'literature', 'image'] },
limit: PRERENDERED_ARTICLES_COUNT,
})

View File

@ -1,7 +1,6 @@
import type {
Author,
Shout,
ShoutInput,
LoadShoutsOptions,
QueryLoad_Shouts_SearchArgs,
} from '../../graphql/schema/core.gen'