fmt
This commit is contained in:
parent
01e7dec615
commit
b61b19a119
|
@ -4,12 +4,7 @@ import { useFeed } from '~/context/feed'
|
||||||
import { useLocalize } from '~/context/localize'
|
import { useLocalize } from '~/context/localize'
|
||||||
import { COMMENTS_PER_PAGE, useReactions } from '~/context/reactions'
|
import { COMMENTS_PER_PAGE, useReactions } from '~/context/reactions'
|
||||||
import { useSession } from '~/context/session'
|
import { useSession } from '~/context/session'
|
||||||
import {
|
import { Reaction, ReactionKind, ReactionSort, Shout } from '~/graphql/schema/core.gen'
|
||||||
Reaction,
|
|
||||||
ReactionKind,
|
|
||||||
ReactionSort,
|
|
||||||
Shout
|
|
||||||
} from '~/graphql/schema/core.gen'
|
|
||||||
import { byCreated, byStat } from '~/lib/sort'
|
import { byCreated, byStat } from '~/lib/sort'
|
||||||
import { SortFunction } from '~/types/common'
|
import { SortFunction } from '~/types/common'
|
||||||
import { Button } from '../_shared/Button'
|
import { Button } from '../_shared/Button'
|
||||||
|
|
|
@ -26,8 +26,14 @@ export const RatingControl = (props: RatingControlProps) => {
|
||||||
const [_, changeSearchParams] = useSearchParams()
|
const [_, changeSearchParams] = useSearchParams()
|
||||||
const snackbar = useSnackbar()
|
const snackbar = useSnackbar()
|
||||||
const { session } = useSession()
|
const { session } = useSession()
|
||||||
const { reactionEntities, reactionsByShout, createReaction, deleteReaction, loadShoutRatings, loadCommentRatings } =
|
const {
|
||||||
useReactions()
|
reactionEntities,
|
||||||
|
reactionsByShout,
|
||||||
|
createReaction,
|
||||||
|
deleteReaction,
|
||||||
|
loadShoutRatings,
|
||||||
|
loadCommentRatings
|
||||||
|
} = useReactions()
|
||||||
const [myRate, setMyRate] = createSignal<Reaction | undefined>()
|
const [myRate, setMyRate] = createSignal<Reaction | undefined>()
|
||||||
const [ratingReactions, setRatingReactions] = createSignal<Reaction[]>([])
|
const [ratingReactions, setRatingReactions] = createSignal<Reaction[]>([])
|
||||||
const [isLoading, setIsLoading] = createSignal(false)
|
const [isLoading, setIsLoading] = createSignal(false)
|
||||||
|
@ -85,7 +91,7 @@ export const RatingControl = (props: RatingControlProps) => {
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch (err) {
|
||||||
snackbar?.showSnackbar({ type: 'error', body: `${t('Error')}: ${error || err || ''}` })
|
snackbar?.showSnackbar({ type: 'error', body: `${t('Error')}: ${error || err || ''}` })
|
||||||
}
|
}
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
|
|
|
@ -262,135 +262,135 @@ const SimplifiedEditor = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<ShowOnlyOnClient>
|
<ShowOnlyOnClient>
|
||||||
<Suspense>
|
<Suspense>
|
||||||
<div
|
<div
|
||||||
ref={(el) => (wrapperEditorElRef = el)}
|
ref={(el) => (wrapperEditorElRef = el)}
|
||||||
class={clsx(styles.SimplifiedEditor, {
|
class={clsx(styles.SimplifiedEditor, {
|
||||||
[styles.smallHeight]: props.smallHeight,
|
[styles.smallHeight]: props.smallHeight,
|
||||||
[styles.minimal]: props.variant === 'minimal',
|
[styles.minimal]: props.variant === 'minimal',
|
||||||
[styles.bordered]: props.variant === 'bordered',
|
[styles.bordered]: props.variant === 'bordered',
|
||||||
[styles.isFocused]: isFocused() || !isEmpty(),
|
[styles.isFocused]: isFocused() || !isEmpty(),
|
||||||
[styles.labelVisible]: props.label && counter() > 0
|
[styles.labelVisible]: props.label && counter() > 0
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Show when={props.maxLength && editor()}>
|
<Show when={props.maxLength && editor()}>
|
||||||
<div class={styles.limit}>{maxLength - counter()}</div>
|
<div class={styles.limit}>{maxLength - counter()}</div>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={props.label && counter() > 0}>
|
<Show when={props.label && counter() > 0}>
|
||||||
<div class={styles.label}>{props.label}</div>
|
<div class={styles.label}>{props.label}</div>
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={props.maxHeight} fallback={<div ref={(el) => (editorElRef = el)} />}>
|
<Show when={props.maxHeight} fallback={<div ref={(el) => (editorElRef = el)} />}>
|
||||||
<div style={maxHeightStyle} ref={(el) => (editorElRef = el)} />
|
<div style={maxHeightStyle} ref={(el) => (editorElRef = el)} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<Show when={!props.onlyBubbleControls}>
|
<Show when={!props.onlyBubbleControls}>
|
||||||
<div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
|
<div class={clsx(styles.controls, { [styles.alwaysVisible]: props.controlsAlwaysVisible })}>
|
||||||
<div class={styles.actions}>
|
<div class={styles.actions}>
|
||||||
<Popover content={t('Bold')}>
|
<Popover content={t('Bold')}>
|
||||||
{(triggerRef: (el: HTMLElement) => void) => (
|
{(triggerRef: (el: HTMLElement) => void) => (
|
||||||
<button
|
<button
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
class={clsx(styles.actionButton, { [styles.active]: isBold() })}
|
class={clsx(styles.actionButton, { [styles.active]: isBold() })}
|
||||||
onClick={() => editor()?.chain().focus().toggleBold().run()}
|
onClick={() => editor()?.chain().focus().toggleBold().run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-bold" />
|
<Icon name="editor-bold" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
<Popover content={t('Italic')}>
|
<Popover content={t('Italic')}>
|
||||||
{(triggerRef) => (
|
|
||||||
<button
|
|
||||||
ref={triggerRef}
|
|
||||||
type="button"
|
|
||||||
class={clsx(styles.actionButton, { [styles.active]: isItalic() })}
|
|
||||||
onClick={() => editor()?.chain().focus().toggleItalic().run()}
|
|
||||||
>
|
|
||||||
<Icon name="editor-italic" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Popover>
|
|
||||||
<Popover content={t('Add url')}>
|
|
||||||
{(triggerRef) => (
|
|
||||||
<button
|
|
||||||
ref={triggerRef}
|
|
||||||
type="button"
|
|
||||||
onClick={handleShowLinkBubble}
|
|
||||||
class={clsx(styles.actionButton, { [styles.active]: isLink() })}
|
|
||||||
>
|
|
||||||
<Icon name="editor-link" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</Popover>
|
|
||||||
<Show when={props.quoteEnabled}>
|
|
||||||
<Popover content={t('Add blockquote')}>
|
|
||||||
{(triggerRef) => (
|
{(triggerRef) => (
|
||||||
<button
|
<button
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => editor()?.chain().focus().toggleBlockquote().run()}
|
class={clsx(styles.actionButton, { [styles.active]: isItalic() })}
|
||||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
onClick={() => editor()?.chain().focus().toggleItalic().run()}
|
||||||
>
|
>
|
||||||
<Icon name="editor-quote" />
|
<Icon name="editor-italic" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
</Show>
|
<Popover content={t('Add url')}>
|
||||||
<Show when={props.imageEnabled}>
|
|
||||||
<Popover content={t('Add image')}>
|
|
||||||
{(triggerRef) => (
|
{(triggerRef) => (
|
||||||
<button
|
<button
|
||||||
ref={triggerRef}
|
ref={triggerRef}
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => showModal('simplifiedEditorUploadImage')}
|
onClick={handleShowLinkBubble}
|
||||||
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
class={clsx(styles.actionButton, { [styles.active]: isLink() })}
|
||||||
>
|
>
|
||||||
<Icon name="editor-image-dd-full" />
|
<Icon name="editor-link" />
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</Popover>
|
</Popover>
|
||||||
</Show>
|
<Show when={props.quoteEnabled}>
|
||||||
</div>
|
<Popover content={t('Add blockquote')}>
|
||||||
<Show when={!props.onChange}>
|
{(triggerRef) => (
|
||||||
<div class={styles.buttons}>
|
<button
|
||||||
<Show when={isCancelButtonVisible()}>
|
ref={triggerRef}
|
||||||
<Button value={t('Cancel')} variant="secondary" onClick={handleClear} />
|
type="button"
|
||||||
|
onClick={() => editor()?.chain().focus().toggleBlockquote().run()}
|
||||||
|
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||||
|
>
|
||||||
|
<Icon name="editor-quote" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
</Show>
|
</Show>
|
||||||
<Show when={!props.isPosting} fallback={<Loading />}>
|
<Show when={props.imageEnabled}>
|
||||||
<Button
|
<Popover content={t('Add image')}>
|
||||||
value={props.submitButtonText ?? t('Send')}
|
{(triggerRef) => (
|
||||||
variant="primary"
|
<button
|
||||||
disabled={isEmpty()}
|
ref={triggerRef}
|
||||||
onClick={() => props.onSubmit?.(html() || '')}
|
type="button"
|
||||||
/>
|
onClick={() => showModal('simplifiedEditorUploadImage')}
|
||||||
|
class={clsx(styles.actionButton, { [styles.active]: isBlockquote() })}
|
||||||
|
>
|
||||||
|
<Icon name="editor-image-dd-full" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</Popover>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
|
<Show when={!props.onChange}>
|
||||||
|
<div class={styles.buttons}>
|
||||||
|
<Show when={isCancelButtonVisible()}>
|
||||||
|
<Button value={t('Cancel')} variant="secondary" onClick={handleClear} />
|
||||||
|
</Show>
|
||||||
|
<Show when={!props.isPosting} fallback={<Loading />}>
|
||||||
|
<Button
|
||||||
|
value={props.submitButtonText ?? t('Send')}
|
||||||
|
variant="primary"
|
||||||
|
disabled={isEmpty()}
|
||||||
|
onClick={() => props.onSubmit?.(html() || '')}
|
||||||
|
/>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
<Show when={props.imageEnabled}>
|
||||||
|
<Portal>
|
||||||
|
<Modal variant="narrow" name="simplifiedEditorUploadImage">
|
||||||
|
<UploadModalContent onClose={(value) => value && renderImage(value)} />
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
</Show>
|
||||||
|
<Show when={!!editor()}>
|
||||||
|
<Show when={props.onlyBubbleControls}>
|
||||||
|
<TextBubbleMenu
|
||||||
|
shouldShow={true}
|
||||||
|
isCommonMarkup={true}
|
||||||
|
editor={editor() as Editor}
|
||||||
|
ref={(el) => (textBubbleMenuRef = el)}
|
||||||
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
</div>
|
<LinkBubbleMenuModule
|
||||||
</Show>
|
|
||||||
<Show when={props.imageEnabled}>
|
|
||||||
<Portal>
|
|
||||||
<Modal variant="narrow" name="simplifiedEditorUploadImage">
|
|
||||||
<UploadModalContent onClose={(value) => value && renderImage(value)} />
|
|
||||||
</Modal>
|
|
||||||
</Portal>
|
|
||||||
</Show>
|
|
||||||
<Show when={!!editor()}>
|
|
||||||
<Show when={props.onlyBubbleControls}>
|
|
||||||
<TextBubbleMenu
|
|
||||||
shouldShow={true}
|
|
||||||
isCommonMarkup={true}
|
|
||||||
editor={editor() as Editor}
|
editor={editor() as Editor}
|
||||||
ref={(el) => (textBubbleMenuRef = el)}
|
ref={(el) => (linkBubbleMenuRef = el)}
|
||||||
|
onClose={handleHideLinkBubble}
|
||||||
/>
|
/>
|
||||||
</Show>
|
</Show>
|
||||||
<LinkBubbleMenuModule
|
</div>
|
||||||
editor={editor() as Editor}
|
|
||||||
ref={(el) => (linkBubbleMenuRef = el)}
|
|
||||||
onClose={handleHideLinkBubble}
|
|
||||||
/>
|
|
||||||
</Show>
|
|
||||||
</div>
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</ShowOnlyOnClient>
|
</ShowOnlyOnClient>
|
||||||
)
|
)
|
||||||
|
|
|
@ -261,7 +261,6 @@ export const AuthorView = (props: AuthorViewProps) => {
|
||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
<AuthorFeed />
|
<AuthorFeed />
|
||||||
|
|
||||||
</Match>
|
</Match>
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -53,14 +53,12 @@ export const LoadMoreWrapper = (props: LoadMoreProps) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{props.children}
|
{props.children}
|
||||||
<Show when={isLoading()}><Loading /></Show>
|
<Show when={isLoading()}>
|
||||||
|
<Loading />
|
||||||
|
</Show>
|
||||||
<Show when={isLoadMoreButtonVisible() && !props.hidden && !isLoading()}>
|
<Show when={isLoadMoreButtonVisible() && !props.hidden && !isLoading()}>
|
||||||
<div class="load-more-container">
|
<div class="load-more-container">
|
||||||
<Button
|
<Button onClick={loadItems} value={t('Load more')} title={`${items().length} ${t('loaded')}`} />
|
||||||
onClick={loadItems}
|
|
||||||
value={t('Load more')}
|
|
||||||
title={`${items().length} ${t('loaded')}`}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</Show>
|
</Show>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -2,7 +2,12 @@ import type { JSX } from 'solid-js'
|
||||||
|
|
||||||
import { createContext, 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 { loadCommentRatings, loadReactions, loadShoutComments, loadShoutRatings } from '~/graphql/api/public'
|
import {
|
||||||
|
loadCommentRatings,
|
||||||
|
loadReactions,
|
||||||
|
loadShoutComments,
|
||||||
|
loadShoutRatings
|
||||||
|
} from '~/graphql/api/public'
|
||||||
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
|
import createReactionMutation from '~/graphql/mutation/core/reaction-create'
|
||||||
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
|
import destroyReactionMutation from '~/graphql/mutation/core/reaction-destroy'
|
||||||
import updateReactionMutation from '~/graphql/mutation/core/reaction-update'
|
import updateReactionMutation from '~/graphql/mutation/core/reaction-update'
|
||||||
|
@ -69,7 +74,11 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadShoutRatingsAdding = async (shout: number, limit = RATINGS_PER_PAGE, offset = 0): Promise<Reaction[]> => {
|
const loadShoutRatingsAdding = async (
|
||||||
|
shout: number,
|
||||||
|
limit = RATINGS_PER_PAGE,
|
||||||
|
offset = 0
|
||||||
|
): Promise<Reaction[]> => {
|
||||||
const fetcher = await loadShoutRatings({ shout, limit, offset })
|
const fetcher = await loadShoutRatings({ shout, limit, offset })
|
||||||
const result = (await fetcher()) || []
|
const result = (await fetcher()) || []
|
||||||
console.debug('[context.reactions] shout ratings loaded', result)
|
console.debug('[context.reactions] shout ratings loaded', result)
|
||||||
|
@ -77,7 +86,11 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadCommentRatingsAdding = async (comment: number, limit = RATINGS_PER_PAGE, offset = 0): Promise<Reaction[]> => {
|
const loadCommentRatingsAdding = async (
|
||||||
|
comment: number,
|
||||||
|
limit = RATINGS_PER_PAGE,
|
||||||
|
offset = 0
|
||||||
|
): Promise<Reaction[]> => {
|
||||||
const fetcher = await loadCommentRatings({ comment, limit, offset })
|
const fetcher = await loadCommentRatings({ comment, limit, offset })
|
||||||
const result = (await fetcher()) || []
|
const result = (await fetcher()) || []
|
||||||
console.debug('[context.reactions] shout ratings loaded', result)
|
console.debug('[context.reactions] shout ratings loaded', result)
|
||||||
|
@ -85,7 +98,11 @@ export const ReactionsProvider = (props: { children: JSX.Element }) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
const loadShoutCommentsAdding = async (shout: number, limit = COMMENTS_PER_PAGE, offset = 0): Promise<Reaction[]> => {
|
const loadShoutCommentsAdding = async (
|
||||||
|
shout: number,
|
||||||
|
limit = COMMENTS_PER_PAGE,
|
||||||
|
offset = 0
|
||||||
|
): Promise<Reaction[]> => {
|
||||||
const fetcher = await loadShoutComments({ shout, limit, offset })
|
const fetcher = await loadShoutComments({ shout, limit, offset })
|
||||||
const result = (await fetcher()) || []
|
const result = (await fetcher()) || []
|
||||||
console.debug('[context.reactions] shout comments loaded', result)
|
console.debug('[context.reactions] shout comments loaded', result)
|
||||||
|
|
|
@ -62,7 +62,7 @@ export const loadShouts = (options: LoadShoutsOptions) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadShoutComments = (options: QueryLoad_Shout_RatingsArgs) => {
|
export const loadShoutComments = (options: QueryLoad_Shout_RatingsArgs) => {
|
||||||
const page = `${options.offset || 0}-${(options.limit||1) + (options.offset || 0)}`
|
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
||||||
return cache(async () => {
|
return cache(async () => {
|
||||||
const resp = await defaultClient.query(loadShoutCommentsQuery, options).toPromise()
|
const resp = await defaultClient.query(loadShoutCommentsQuery, options).toPromise()
|
||||||
const result = resp?.data?.load_reactions_by
|
const result = resp?.data?.load_reactions_by
|
||||||
|
@ -71,7 +71,7 @@ export const loadShoutComments = (options: QueryLoad_Shout_RatingsArgs) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadShoutRatings = (options: QueryLoad_Shout_RatingsArgs) => {
|
export const loadShoutRatings = (options: QueryLoad_Shout_RatingsArgs) => {
|
||||||
const page = `${options.offset || 0}-${(options.limit||1) + (options.offset || 0)}`
|
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
||||||
return cache(async () => {
|
return cache(async () => {
|
||||||
const resp = await defaultClient.query(loadShoutRatingsQuery, options).toPromise()
|
const resp = await defaultClient.query(loadShoutRatingsQuery, options).toPromise()
|
||||||
const result = resp?.data?.load_reactions_by
|
const result = resp?.data?.load_reactions_by
|
||||||
|
@ -81,7 +81,7 @@ export const loadShoutRatings = (options: QueryLoad_Shout_RatingsArgs) => {
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noExplicitAny: FIXME: wait backend
|
// biome-ignore lint/suspicious/noExplicitAny: FIXME: wait backend
|
||||||
export const loadCommentRatings = (options: any) => {
|
export const loadCommentRatings = (options: any) => {
|
||||||
const page = `${options.offset || 0}-${(options.limit||1) + (options.offset || 0)}`
|
const page = `${options.offset || 0}-${(options.limit || 1) + (options.offset || 0)}`
|
||||||
return cache(async () => {
|
return cache(async () => {
|
||||||
const resp = await defaultClient.query(loadCommentRatingsQuery, options).toPromise()
|
const resp = await defaultClient.query(loadCommentRatingsQuery, options).toPromise()
|
||||||
const result = resp?.data?.load_reactions_by
|
const result = resp?.data?.load_reactions_by
|
||||||
|
|
|
@ -54,7 +54,10 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
const { authorsEntities } = useAuthors()
|
const { authorsEntities } = useAuthors()
|
||||||
const { addFeed, feedByAuthor } = useFeed()
|
const { addFeed, feedByAuthor } = useFeed()
|
||||||
const { t } = useLocalize()
|
const { t } = useLocalize()
|
||||||
const author = createAsync(async() => props.data.author || authorsEntities()[props.params.slug] || await fetchAuthor(props.params.slug))
|
const author = createAsync(
|
||||||
|
async () =>
|
||||||
|
props.data.author || authorsEntities()[props.params.slug] || (await fetchAuthor(props.params.slug))
|
||||||
|
)
|
||||||
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
const shoutsByAuthor = createMemo(() => feedByAuthor()[props.params.slug])
|
||||||
const title = createMemo(() => `${author()?.name || ''}`)
|
const title = createMemo(() => `${author()?.name || ''}`)
|
||||||
const cover = createMemo(() =>
|
const cover = createMemo(() =>
|
||||||
|
@ -74,7 +77,6 @@ export default function AuthorPage(props: RouteSectionProps<AuthorPageProps>) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// author shouts
|
// author shouts
|
||||||
const loadAuthorShoutsMore = async (offset: number) => {
|
const loadAuthorShoutsMore = async (offset: number) => {
|
||||||
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
const loadedShouts = await fetchAuthorShouts(props.params.slug, offset)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user