This commit is contained in:
Untone 2024-07-22 14:29:33 +03:00
parent 01e7dec615
commit b61b19a119
8 changed files with 146 additions and 129 deletions

View File

@ -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'

View File

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

View File

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

View File

@ -261,7 +261,6 @@ export const AuthorView = (props: AuthorViewProps) => {
</Show> </Show>
<AuthorFeed /> <AuthorFeed />
</Match> </Match>
</Switch> </Switch>
</div> </div>

View File

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

View File

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

View File

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

View File

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