author-shouts-loadmore

This commit is contained in:
Untone 2024-09-03 11:07:32 +03:00
parent 33a81d8ee7
commit e176544e36
3 changed files with 63 additions and 51 deletions

View File

@ -62,8 +62,8 @@ const getTitleAndSubtitle = (
title: string
subtitle: string
} => {
let title = article?.title || ''
let subtitle: string = article?.subtitle || ''
let title = article.title || ''
let subtitle: string = article.subtitle || ''
if (!subtitle) {
let titleParts = article.title?.split('. ') || []
@ -85,8 +85,8 @@ const getTitleAndSubtitle = (
}
const getMainTopicTitle = (article: Shout, lng: string) => {
const mainTopicSlug = article?.main_topic || ''
const mainTopic = (article?.topics || []).find((tpc: Maybe<Topic>) => tpc?.slug === mainTopicSlug)
const mainTopicSlug = article.main_topic || ''
const mainTopic = (article.topics || []).find((tpc: Maybe<Topic>) => tpc?.slug === mainTopicSlug)
const mainTopicTitle =
mainTopicSlug && lng === 'en' ? mainTopicSlug.replace(/-/, ' ') : mainTopic?.title || ''
@ -109,29 +109,30 @@ export const ArticleCard = (props: ArticleCardProps) => {
const [isActionPopupActive, setIsActionPopupActive] = createSignal(false)
const [isCoverImageLoadError, setIsCoverImageLoadError] = createSignal(false)
const [isCoverImageLoading, setIsCoverImageLoading] = createSignal(true)
const description = descFromBody(props.article?.body)
const aspectRatio: Accessor<string> = () => LAYOUT_ASPECT[props.article?.layout as string]
const description = descFromBody(props.article.body)
const aspectRatio: Accessor<string> = () => LAYOUT_ASPECT[props.article.layout as string]
const [mainTopicTitle, mainTopicSlug] = getMainTopicTitle(props.article, lang())
const { title, subtitle } = getTitleAndSubtitle(props.article)
const formattedDate = createMemo<string>(() =>
props.article?.published_at ? formatDate(new Date(props.article.published_at * 1000)) : ''
props.article.published_at ? formatDate(new Date(props.article.published_at * 1000)) : ''
)
const canEdit = createMemo(
() =>
Boolean(author()?.id) &&
(props.article?.authors?.some((a) => Boolean(a) && a?.id === author().id) ||
props.article?.created_by?.id === author().id ||
(props.article.authors?.some((a) => Boolean(a) && a?.id === author().id) ||
props.article.created_by?.id === author().id ||
session()?.user?.roles?.includes('editor'))
)
const navigate = useNavigate()
const scrollToComments = (event: MouseEvent & { currentTarget: HTMLAnchorElement; target: Element }) => {
event.preventDefault()
navigate(`/${props.article.slug}`)
changeSearchParams({
scrollTo: 'comments'
})
navigate(`/${props.article.slug}`)
}
const onInvite = () => {
@ -196,10 +197,9 @@ export const ArticleCard = (props: ArticleCardProps) => {
}
>
<div class={styles.shoutCardType}>
<a href={`/expo/${props.article.layout}`}>
<A href={`/expo/${props.article.layout}`}>
<Icon name={props.article.layout} class={styles.icon} />
{/*<Icon name={`${layout}-hover`} class={clsx(styles.icon, styles.iconHover)} />*/}
</a>
</A>
</div>
</Show>
@ -220,7 +220,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
[styles.shoutCardTitlesContainerFeedMode]: props.settings?.isFeedMode
})}
>
<A href={`/${props.article?.slug || ''}`}>
<A href={`/${props.article.slug}`}>
<div class={styles.shoutCardTitle}>
<span class={styles.shoutCardLinkWrapper}>
<span class={styles.shoutCardLinkContainer} innerHTML={title} />
@ -280,10 +280,9 @@ export const ArticleCard = (props: ArticleCardProps) => {
}
>
<div class={styles.shoutCardType}>
<a href={`/expo/${props.article.layout}`}>
<A href={`/expo/${props.article.layout}`}>
<Icon name={props.article.layout} class={styles.icon} />
{/*<Icon name={`${layout}-hover`} class={clsx(styles.icon, styles.iconHover)} />*/}
</a>
</A>
</div>
</Show>
<div class={styles.shoutCardCover}>
@ -332,7 +331,7 @@ export const ArticleCard = (props: ArticleCardProps) => {
<Popover content={t('Edit')} disabled={isActionPopupActive()}>
{(triggerRef: (el: HTMLElement) => void) => (
<div class={styles.shoutCardDetailsItem} ref={triggerRef}>
<A href={`/edit/${props.article?.id}`}>
<A href={`/edit/${props.article.id}`}>
<Icon name="pencil-outline" class={clsx(styles.icon, styles.feedControlIcon)} />
<Icon
name="pencil-outline-hover"

View File

@ -1,16 +1,20 @@
import { A, useLocation, useParams } from '@solidjs/router'
import { clsx } from 'clsx'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal } from 'solid-js'
import { For, Match, Show, Switch, createEffect, createMemo, createSignal, on } from 'solid-js'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { Loading } from '~/components/_shared/Loading'
import { coreApiUrl } from '~/config'
import { useAuthors } from '~/context/authors'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { useFollowing } from '~/context/following'
import { useLocalize } from '~/context/localize'
import { useSession } from '~/context/session'
import { loadShouts } from '~/graphql/api/public'
import { graphqlClientCreate } from '~/graphql/client'
import getAuthorFollowersQuery from '~/graphql/query/core/author-followers'
import getAuthorFollowsQuery from '~/graphql/query/core/author-follows'
import type { Author, Reaction, Shout, Topic } from '~/graphql/schema/core.gen'
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
import { byCreated } from '~/utils/sort'
import stylesArticle from '../../Article/Article.module.scss'
import { Comment } from '../../Article/Comment'
@ -131,6 +135,32 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
)
const { feedByAuthor, addFeed } = useFeed()
const [sortedFeed, setSortedFeed] = createSignal<Shout[]>([])
const [loadMoreHidden, setLoadMoreHidden] = createSignal(false)
const loadMore = async () => {
saveScrollPosition()
const amountBefore = feedByAuthor()?.[props.authorSlug]?.length || 0
const topicShoutsFetcher = loadShouts({
filters: { author: props.authorSlug },
limit: SHOUTS_PER_PAGE,
offset: amountBefore
})
const result = await topicShoutsFetcher()
result && addFeed(result)
const amountAfter = feedByAuthor()?.[props.authorSlug].length
setLoadMoreHidden(amountAfter === amountBefore)
restoreScrollPosition()
return result as LoadMoreItems
}
// fx to update author's feed
createEffect(on(feedByAuthor, (byAuthor) => {
const feed = byAuthor[props.authorSlug] as Shout[]
if (!feed) return
setSortedFeed(feed)
},{}))
return (
<div class={styles.authorPage}>
<div class="wide-container">
@ -218,10 +248,14 @@ export const AuthorView = (props: AuthorViewProps) => {
</div>
</Show>
<Show when={Array.isArray(props.shouts) && props.shouts.length > 0}>
<For each={props.shouts.filter((_, i) => i % 3 === 0)}>
<LoadMoreWrapper loadFunction={loadMore} pageSize={SHOUTS_PER_PAGE} hidden={loadMoreHidden()}>
<For
each={sortedFeed()
.filter((_, i) => i % 3 === 0)}
>
{(_shout, index) => {
const articles = props.shouts.slice(index() * 3, index() * 3 + 3)
const articles = sortedFeed()
.slice(index() * 3, index() * 3 + 3)
return (
<>
<Switch>
@ -239,7 +273,7 @@ export const AuthorView = (props: AuthorViewProps) => {
)
}}
</For>
</Show>
</LoadMoreWrapper>
</Match>
</Switch>
</div>

View File

@ -2,11 +2,10 @@ import { RouteSectionProps, createAsync } from '@solidjs/router'
import { ErrorBoundary, Suspense, createEffect, createSignal, on } from 'solid-js'
import { AuthorView } from '~/components/Views/Author'
import { FourOuFourView } from '~/components/Views/FourOuFour'
import { LoadMoreItems, LoadMoreWrapper } from '~/components/_shared/LoadMoreWrapper'
import { Loading } from '~/components/_shared/Loading'
import { PageLayout } from '~/components/_shared/PageLayout'
import { useAuthors } from '~/context/authors'
import { SHOUTS_PER_PAGE, useFeed } from '~/context/feed'
import { SHOUTS_PER_PAGE } from '~/context/feed'
import { useLocalize } from '~/context/localize'
import { ReactionsProvider } from '~/context/reactions'
import { loadAuthors, loadShouts, loadTopics } from '~/graphql/api/public'
@ -106,21 +105,7 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
)
// author's shouts
const { addFeed, feedByAuthor } = useFeed()
const [loadMoreHidden, setLoadMoreHidden] = createSignal(true)
const authorShouts = createAsync(async () => {
const sss: Shout[] = (props.data.articles as Shout[]) || feedByAuthor()[props.params.slug] || []
const result = sss || (await fetchAuthorShouts(props.params.slug, 0))
if (!result) setLoadMoreHidden(true)
return result
})
// load more shouts
const loadAuthorShoutsMore = async (offset: number) => {
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
loadedShouts && addFeed(loadedShouts)
return (loadedShouts || []) as LoadMoreItems
}
const authorShouts = createAsync(async () => props.data.articles as Shout[] || await fetchAuthorShouts(props.params.slug, 0))
return (
<ErrorBoundary fallback={(_err) => <FourOuFourView />}>
@ -133,17 +118,11 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
cover={cover()}
>
<ReactionsProvider>
<LoadMoreWrapper
loadFunction={loadAuthorShoutsMore}
pageSize={SHOUTS_PER_PAGE}
hidden={loadMoreHidden()}
>
<AuthorView
author={author() as Author}
authorSlug={props.params.slug}
shouts={authorShouts() || []}
/>
</LoadMoreWrapper>
<AuthorView
author={author() as Author}
authorSlug={props.params.slug}
shouts={authorShouts() || []}
/>
</ReactionsProvider>
</PageLayout>
</Suspense>