loadmore-main-fix
This commit is contained in:
parent
7573c6334c
commit
1eb9c57f0d
|
@ -8,7 +8,7 @@ import sassDts from 'vite-plugin-sass-dts'
|
||||||
const isVercel = Boolean(process?.env.VERCEL)
|
const isVercel = Boolean(process?.env.VERCEL)
|
||||||
const isBun = Boolean(process.env.BUN)
|
const isBun = Boolean(process.env.BUN)
|
||||||
|
|
||||||
console.info(`[app.config] build for ${isVercel ? 'vercel' : isBun? 'bun' : 'node'}!`)
|
console.info(`[app.config] build for ${isVercel ? 'vercel' : isBun ? 'bun' : 'node'}!`)
|
||||||
|
|
||||||
const polyfillOptions = {
|
const polyfillOptions = {
|
||||||
include: ['path', 'stream', 'util'],
|
include: ['path', 'stream', 'util'],
|
||||||
|
|
|
@ -117,7 +117,12 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
|
|
||||||
// on load
|
// on load
|
||||||
createEffect(on(() => bioContainerRef, checkBioHeight))
|
createEffect(on(() => bioContainerRef, checkBioHeight))
|
||||||
createEffect(on(() => props.selectedTab, (tab) => tab && console.log('[views.Author] profile tab switched')))
|
createEffect(
|
||||||
|
on(
|
||||||
|
() => props.selectedTab,
|
||||||
|
(tab) => tab && console.log('[views.Author] profile tab switched')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={styles.authorPage}>
|
<div class={styles.authorPage}>
|
||||||
|
|
|
@ -40,9 +40,9 @@ export type FeedProps = {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PERIODS = {
|
const PERIODS = {
|
||||||
'day': 24 * 60 * 60,
|
day: 24 * 60 * 60,
|
||||||
'month': 30 * 24 * 60 * 60,
|
month: 30 * 24 * 60 * 60,
|
||||||
'year': 365 * 24 * 60 * 60
|
year: 365 * 24 * 60 * 60
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FeedView = (props: FeedProps) => {
|
export const FeedView = (props: FeedProps) => {
|
||||||
|
@ -98,7 +98,7 @@ export const FeedView = (props: FeedProps) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const asOption = (o: string) => {
|
const asOption = (o: string) => {
|
||||||
const value = Math.floor(Date.now()/1000) - PERIODS[o as keyof typeof PERIODS]
|
const value = Math.floor(Date.now() / 1000) - PERIODS[o as keyof typeof PERIODS]
|
||||||
return { value, title: t(o) }
|
return { value, title: t(o) }
|
||||||
}
|
}
|
||||||
const asOptions = (opts: string[]) => opts.map(asOption)
|
const asOptions = (opts: string[]) => opts.map(asOption)
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
import { JSX, Show, createSignal, onMount } from 'solid-js'
|
import { JSX, Show, createEffect, createSignal, on, onMount } from 'solid-js'
|
||||||
import { Button } from '~/components/_shared/Button'
|
import { Button } from '~/components/_shared/Button'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
import { Author, Reaction, Shout } from '~/graphql/schema/core.gen'
|
||||||
|
import { byCreated } from '~/lib/sort'
|
||||||
|
import { SortFunction } from '~/types/common'
|
||||||
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
import { restoreScrollPosition, saveScrollPosition } from '~/utils/scroll'
|
||||||
|
|
||||||
export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
export type LoadMoreItems = Shout[] | Author[] | Reaction[]
|
||||||
|
|
||||||
type LoadMoreProps = {
|
type LoadMoreProps = {
|
||||||
loadFunction: (offset?: number) => Promise<LoadMoreItems>
|
loadFunction: (offset: number) => Promise<LoadMoreItems>
|
||||||
pageSize: number
|
pageSize: number
|
||||||
hidden?: boolean
|
hidden?: boolean
|
||||||
children: JSX.Element
|
children: JSX.Element
|
||||||
|
@ -17,18 +19,30 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const [items, setItems] = createSignal<LoadMoreItems>([])
|
const [items, setItems] = createSignal<LoadMoreItems>([])
|
||||||
const [offset, setOffset] = createSignal(0)
|
const [offset, setOffset] = createSignal(0)
|
||||||
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(true)
|
const [isLoadMoreButtonVisible, setIsLoadMoreButtonVisible] = createSignal(false)
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
|
|
||||||
|
createEffect(
|
||||||
|
on(items, (iii) => {
|
||||||
|
if (Array.isArray(iii)) {
|
||||||
|
setIsLoadMoreButtonVisible(iii.length - offset() >= 0)
|
||||||
|
setOffset(iii.length)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const loadItems = async () => {
|
const loadItems = async () => {
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
saveScrollPosition()
|
saveScrollPosition()
|
||||||
const newItems = await props.loadFunction(offset())
|
const newItems = await props.loadFunction(offset())
|
||||||
if (!Array.isArray(newItems)) return
|
if (!Array.isArray(newItems)) return
|
||||||
console.debug('[_share] load more items', newItems)
|
console.debug('[_share] load more items', newItems)
|
||||||
setItems((prev) => [...prev, ...newItems] as LoadMoreItems)
|
setItems(
|
||||||
setOffset((prev) => prev + props.pageSize)
|
(prev) =>
|
||||||
setIsLoadMoreButtonVisible(newItems.length >= props.pageSize - 1)
|
Array.from(new Set([...prev, ...newItems])).sort(
|
||||||
|
byCreated as SortFunction<unknown>
|
||||||
|
) as LoadMoreItems
|
||||||
|
)
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
restoreScrollPosition()
|
restoreScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,8 @@ export const byFirstChar = (a: Author | Topic, b: Author | Topic) =>
|
||||||
(b as Author).name || (b as Topic).title || ''
|
(b as Author).name || (b as Topic).title || ''
|
||||||
)
|
)
|
||||||
|
|
||||||
export const byCreated = (a: Shout | Reaction, b: Shout | Reaction) => {
|
export const byCreated = (a: { created_at?: number }, b: { created_at?: number }) => {
|
||||||
return a?.created_at - b?.created_at
|
return (a?.created_at || 0) - (b?.created_at || 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const byPublished = (a: Shout, b: Shout) => {
|
export const byPublished = (a: Shout, b: Shout) => {
|
||||||
|
|
|
@ -7,15 +7,12 @@ import { PageLayout } from '~/components/_shared/PageLayout'
|
||||||
import { useAuthors } from '~/context/authors'
|
import { useAuthors } from '~/context/authors'
|
||||||
import { useFeed } from '~/context/feed'
|
import { useFeed } from '~/context/feed'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { ReactionsProvider, useReactions } from '~/context/reactions'
|
import { ReactionsProvider } from '~/context/reactions'
|
||||||
import { loadAuthors, loadReactions, loadShouts, loadTopics } from '~/graphql/api/public'
|
import { loadAuthors, loadShouts, loadTopics } from '~/graphql/api/public'
|
||||||
import {
|
import {
|
||||||
Author,
|
Author,
|
||||||
LoadShoutsOptions,
|
LoadShoutsOptions,
|
||||||
QueryLoad_Authors_ByArgs,
|
QueryLoad_Authors_ByArgs,
|
||||||
QueryLoad_Reactions_ByArgs,
|
|
||||||
Reaction,
|
|
||||||
ReactionKind,
|
|
||||||
Shout,
|
Shout,
|
||||||
Topic
|
Topic
|
||||||
} from '~/graphql/schema/core.gen'
|
} from '~/graphql/schema/core.gen'
|
||||||
|
@ -28,16 +25,6 @@ const fetchAuthorShouts = async (slug: string, offset?: number) => {
|
||||||
return await shoutsLoader()
|
return await shoutsLoader()
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchAuthorComments = async (slug: string, offset?: number) => {
|
|
||||||
const opts: QueryLoad_Reactions_ByArgs = {
|
|
||||||
by: { comment: true, author: slug },
|
|
||||||
limit: SHOUTS_PER_PAGE,
|
|
||||||
offset
|
|
||||||
}
|
|
||||||
const shoutsLoader = loadReactions(opts)
|
|
||||||
return await shoutsLoader()
|
|
||||||
}
|
|
||||||
|
|
||||||
const fetchAllTopics = async () => {
|
const fetchAllTopics = async () => {
|
||||||
const topicsFetcher = loadTopics()
|
const topicsFetcher = loadTopics()
|
||||||
return await topicsFetcher()
|
return await topicsFetcher()
|
||||||
|
@ -86,13 +73,6 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
: getImageUrl('production/image/logo_image.png')
|
: getImageUrl('production/image/logo_image.png')
|
||||||
)
|
)
|
||||||
|
|
||||||
// author comments
|
|
||||||
const { addReactions, reactionEntities } = useReactions()
|
|
||||||
const commentsByAuthor = createMemo(() =>
|
|
||||||
Object.values(reactionEntities).filter(
|
|
||||||
(r: Reaction) => r.kind === ReactionKind.Comment && r.created_by.id === author()?.id
|
|
||||||
)
|
|
||||||
)
|
|
||||||
// author shouts
|
// author shouts
|
||||||
const { addFeed, feedByAuthor } = useFeed()
|
const { addFeed, feedByAuthor } = useFeed()
|
||||||
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
||||||
|
@ -113,15 +93,8 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const loadAuthorDataMore = async (offset = 0) => {
|
const loadAuthorShoutsMore = async (offset: number) => {
|
||||||
if (props.params.tab === 'comments') {
|
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
||||||
const commentsOffset = commentsByAuthor().length
|
|
||||||
const loadedComments = await fetchAuthorComments(props.params.slug, commentsOffset)
|
|
||||||
loadedComments && addReactions(loadedComments)
|
|
||||||
return (loadedComments || []) as LoadMoreItems
|
|
||||||
}
|
|
||||||
const shoutsOffset = shoutsByAuthor().length
|
|
||||||
const loadedShouts = await fetchAuthorShouts(props.params.slug, shoutsOffset)
|
|
||||||
loadedShouts && addFeed(loadedShouts)
|
loadedShouts && addFeed(loadedShouts)
|
||||||
return (loadedShouts || []) as LoadMoreItems
|
return (loadedShouts || []) as LoadMoreItems
|
||||||
}
|
}
|
||||||
|
@ -136,11 +109,7 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
cover={cover()}
|
cover={cover()}
|
||||||
>
|
>
|
||||||
<ReactionsProvider>
|
<ReactionsProvider>
|
||||||
<LoadMoreWrapper
|
<LoadMoreWrapper loadFunction={loadAuthorShoutsMore} pageSize={SHOUTS_PER_PAGE}>
|
||||||
loadFunction={loadAuthorDataMore}
|
|
||||||
pageSize={SHOUTS_PER_PAGE}
|
|
||||||
hidden={!props.params.tab || props.params.tab !== 'comments'}
|
|
||||||
>
|
|
||||||
<AuthorView
|
<AuthorView
|
||||||
author={author() as Author}
|
author={author() as Author}
|
||||||
selectedTab={props.params.tab}
|
selectedTab={props.params.tab}
|
||||||
|
|
|
@ -15,15 +15,16 @@ import { LayoutType } from '~/types/common'
|
||||||
export default () => {
|
export default () => {
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const client = useGraphQL()
|
const client = useGraphQL()
|
||||||
const {showSnackbar} = useSnackbar()
|
const { showSnackbar } = useSnackbar()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const handleCreate = async (layout: LayoutType) => {
|
const handleCreate = async (layout: LayoutType) => {
|
||||||
console.debug('[routes : edit/new] handling create click...')
|
console.debug('[routes : edit/new] handling create click...')
|
||||||
const result = await client.mutation(createShoutMutation, { shout: { layout: layout } }).toPromise()
|
const result = await client.mutation(createShoutMutation, { shout: { layout: layout } }).toPromise()
|
||||||
if (result) {
|
if (result) {
|
||||||
console.debug(result)
|
console.debug(result)
|
||||||
const {shout, error} = result.data.create_shout
|
const { shout, error } = result.data.create_shout
|
||||||
if (error) showSnackbar({
|
if (error)
|
||||||
|
showSnackbar({
|
||||||
body: `${t('Error')}: ${t(error)}`,
|
body: `${t('Error')}: ${t(error)}`,
|
||||||
type: 'error'
|
type: 'error'
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue
Block a user