1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

fixed comments loader

This commit is contained in:
Fedor Katurov 2022-07-17 13:27:33 +07:00
parent 0a1d2cbf99
commit bb81bdae69
16 changed files with 80 additions and 127 deletions

View file

@ -4,18 +4,17 @@ import Tippy from '@tippyjs/react';
import classnames from 'classnames'; import classnames from 'classnames';
import { Icon } from '~/components/input/Icon'; import { Icon } from '~/components/input/Icon';
import { LoaderCircle } from '~/components/input/LoaderCircle';
import { IIcon } from '~/types'; import { IIcon } from '~/types';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
import 'tippy.js/dist/tippy.css';
type IButtonProps = DetailedHTMLProps< type IButtonProps = DetailedHTMLProps<
ButtonHTMLAttributes<HTMLButtonElement>, ButtonHTMLAttributes<HTMLButtonElement>,
HTMLButtonElement HTMLButtonElement
> & { > & {
size?: 'mini' | 'normal' | 'big' | 'giant' | 'micro' | 'small'; size?: 'mini' | 'normal' | 'big' | 'giant' | 'micro' | 'small';
color?: 'primary' | 'secondary' | 'outline' | 'link' | 'gray' | 'lab' | 'outline-white'; color?: 'primary' | 'secondary' | 'outline' | 'link' | 'gray' | 'lab' | 'outline-white' | 'flat';
iconLeft?: IIcon; iconLeft?: IIcon;
iconRight?: IIcon; iconRight?: IIcon;
title?: string; title?: string;
@ -23,6 +22,7 @@ type IButtonProps = DetailedHTMLProps<
iconOnly?: boolean; iconOnly?: boolean;
label?: string; label?: string;
round?: boolean; round?: boolean;
loading?: boolean;
}; };
const Button: FC<IButtonProps> = memo( const Button: FC<IButtonProps> = memo(
@ -40,6 +40,7 @@ const Button: FC<IButtonProps> = memo(
label, label,
ref, ref,
round, round,
loading,
...props ...props
}) => { }) => {
const computedClassName = useMemo( const computedClassName = useMemo(
@ -73,6 +74,9 @@ const Button: FC<IButtonProps> = memo(
{iconLeft && <Icon icon={iconLeft} size={20} key={0} className={styles.icon_left} />} {iconLeft && <Icon icon={iconLeft} size={20} key={0} className={styles.icon_left} />}
{!!title ? <span>{title}</span> : children} {!!title ? <span>{title}</span> : children}
{iconRight && <Icon icon={iconRight} size={20} key={2} className={styles.icon_right} />} {iconRight && <Icon icon={iconRight} size={20} key={2} className={styles.icon_right} />}
{
loading && <div className={styles.loading}><LoaderCircle /></div>
}
</button> </button>
</Tippy> </Tippy>
); );

View file

@ -76,6 +76,7 @@
&:global(.stretchy) { &:global(.stretchy) {
flex: 1; flex: 1;
width: 100%;
} }
&:global(.disabled), &:global(.disabled),
@ -243,3 +244,21 @@
.lab { .lab {
background: $blue; background: $blue;
} }
.flat {
box-shadow: none;
background: $comment_bg;
color: darken(white, 20%);
}
.loading {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
background: transparentize($content_bg, 0.1);
}

View file

@ -0,0 +1,18 @@
import React, { FC } from 'react';
import { Button } from '~/components/input/Button';
import styles from './styles.module.scss';
interface LoadMoreButtonProps {
isLoading: boolean;
onClick?: () => void;
}
const LoadMoreButton: FC<LoadMoreButtonProps> = ({ isLoading, onClick }) => (
<Button color="flat" onClick={onClick} stretchy className={styles.more} loading={isLoading}>
Показать ещё комментарии
</Button>
);
export { LoadMoreButton }

View file

@ -0,0 +1,5 @@
@import "src/styles/variables";
.more {
width: 100%;
}

File diff suppressed because one or more lines are too long

View file

@ -12,25 +12,5 @@
.icon { .icon {
fill: currentColor; fill: currentColor;
stroke: none; stroke: none;
} animation: spin 0.75s infinite linear;
.e0 {
animation: spin infinite linear 2s;
transform-origin: 50% 50%;
opacity: 0.1;
}
.e1 {
animation: spin infinite reverse 2s -0.25s;
transform-origin: 50% 50%;
opacity: 0.1;
}
.e2 {
animation: spin infinite linear 2s -0.25s;
transform-origin: 50% 50%;
opacity: 0.1;
}
.e {
opacity: 0.1;
} }

View file

@ -7,7 +7,7 @@ import { NodeNoComments } from '~/components/node/NodeNoComments';
import { isSSR } from '~/constants/ssr'; import { isSSR } from '~/constants/ssr';
import { NodeComments } from '~/containers/node/NodeComments'; import { NodeComments } from '~/containers/node/NodeComments';
import { useAuth } from '~/hooks/auth/useAuth'; import { useAuth } from '~/hooks/auth/useAuth';
import { CommentContextProvider, useCommentContext } from '~/utils/context/CommentContextProvider'; import { useCommentContext } from '~/utils/context/CommentContextProvider';
import { useNodeContext } from '~/utils/context/NodeContextProvider'; import { useNodeContext } from '~/utils/context/NodeContextProvider';
import { useUserContext } from '~/utils/context/UserContextProvider'; import { useUserContext } from '~/utils/context/UserContextProvider';
@ -21,23 +21,10 @@ const BorisComments: FC<IProps> = () => {
isLoading, isLoading,
comments, comments,
onSaveComment, onSaveComment,
onLoadMoreComments,
onDeleteComment,
onShowImageModal,
hasMore,
} = useCommentContext(); } = useCommentContext();
const { node } = useNodeContext(); const { node } = useNodeContext();
return ( return (
<CommentContextProvider
onSaveComment={onSaveComment}
comments={comments}
hasMore={hasMore}
onDeleteComment={onDeleteComment}
onLoadMoreComments={onLoadMoreComments}
onShowImageModal={onShowImageModal}
isLoading={isLoading}
>
<Group> <Group>
{(isUser || isSSR) && ( {(isUser || isSSR) && (
<NodeCommentFormSSR user={user} nodeId={node.id} saveComment={onSaveComment} /> <NodeCommentFormSSR user={user} nodeId={node.id} saveComment={onSaveComment} />
@ -51,7 +38,6 @@ const BorisComments: FC<IProps> = () => {
<Footer /> <Footer />
</Group> </Group>
</CommentContextProvider>
); );
}; };

View file

@ -1,6 +1,7 @@
import React, { FC, memo, useMemo } from 'react'; import React, { FC, memo, useMemo } from 'react';
import { Comment } from '~/components/comment/Comment'; import { Comment } from '~/components/comment/Comment';
import { LoadMoreButton } from '~/components/input/LoadMoreButton';
import { useGrouppedComments } from '~/hooks/node/useGrouppedComments'; import { useGrouppedComments } from '~/hooks/node/useGrouppedComments';
import { ICommentGroup } from '~/types'; import { ICommentGroup } from '~/types';
import { useCommentContext } from '~/utils/context/CommentContextProvider'; import { useCommentContext } from '~/utils/context/CommentContextProvider';
@ -21,6 +22,8 @@ const NodeComments: FC<IProps> = memo(({ order }) => {
const { const {
comments, comments,
hasMore, hasMore,
isLoading,
isLoadingMore,
lastSeenCurrent, lastSeenCurrent,
onLoadMoreComments, onLoadMoreComments,
onDeleteComment, onDeleteComment,
@ -36,12 +39,10 @@ const NodeComments: FC<IProps> = memo(({ order }) => {
const more = useMemo( const more = useMemo(
() => () =>
hasMore && ( hasMore && <div className={styles.more}>
<div className={styles.more} onClick={onLoadMoreComments}> <LoadMoreButton isLoading={isLoadingMore} onClick={onLoadMoreComments} />
Показать ещё комментарии </div>,
</div> [hasMore, onLoadMoreComments, isLoadingMore]
),
[hasMore, onLoadMoreComments]
); );
if (!node?.id) { if (!node?.id) {

View file

@ -1,4 +1,4 @@
@import "../../../styles/variables"; @import "src/styles/variables";
.wrap { .wrap {
& > div { & > div {
@ -11,40 +11,5 @@
} }
.more { .more {
padding: $gap; margin-bottom: $gap;
box-sizing: border-box;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
border-radius: $radius;
text-transform: uppercase;
color: darken(white, 60%);
font: $font_14_medium;
cursor: pointer;
transition: all 0.25s;
user-select: none;
background: url('../../../sprites/stripes.svg');
position: relative;
&:hover {
color: $wisegreen;
background-color: darken($wisegreen, 12%);
.bar {
background: $wisegreen;
}
}
}
.bar {
position: absolute;
height: 2px;
border-radius: 2px;
background: darken(white, 60%);
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
width: 50%;
transition: width 0.25s;
} }

View file

@ -45,9 +45,10 @@ export const useGetComments = (nodeId: number, fallbackData?: IComment[]) => {
); );
const comments = useMemo(() => flatten(data || []), [data]); const comments = useMemo(() => flatten(data || []), [data]);
const hasMore = (data?.[size - 1]?.length || 0) >= COMMENTS_DISPLAY; const hasMore = (data?.[size - 1]?.length || 0) >= COMMENTS_DISPLAY ||
(!!data?.length && data?.length > 0 && isValidating);
const onLoadMoreComments = useCallback(() => setSize(size + 1), [setSize, size]); const onLoadMoreComments = useCallback(() => setSize(size + 1), [setSize, size]);
return { comments, hasMore, onLoadMoreComments, isLoading: !data && isValidating, mutate, data }; return { comments, hasMore, onLoadMoreComments, isLoading: !data && isValidating, mutate, data, isLoadingMore: !!data?.length && isValidating };
}; };

View file

@ -6,7 +6,7 @@ import { IComment } from '~/types';
import { showErrorToast } from '~/utils/errors/showToast'; import { showErrorToast } from '~/utils/errors/showToast';
export const useNodeComments = (nodeId: number, fallbackData?: IComment[]) => { export const useNodeComments = (nodeId: number, fallbackData?: IComment[]) => {
const { comments, isLoading, onLoadMoreComments, hasMore, data, mutate } = useGetComments( const { comments, isLoading, onLoadMoreComments, hasMore, data, mutate, isLoadingMore } = useGetComments(
nodeId, nodeId,
fallbackData fallbackData
); );
@ -62,5 +62,5 @@ export const useNodeComments = (nodeId: number, fallbackData?: IComment[]) => {
[data, mutate, nodeId] [data, mutate, nodeId]
); );
return { onLoadMoreComments, onDelete, comments, hasMore, isLoading, onEdit }; return { onLoadMoreComments, onDelete, comments, hasMore, isLoading, onEdit, isLoadingMore };
}; };

View file

@ -21,6 +21,7 @@ import { SearchProvider } from '~/utils/providers/SearchProvider';
import { ToastProvider } from '~/utils/providers/ToastProvider'; import { ToastProvider } from '~/utils/providers/ToastProvider';
import '~/styles/main.scss'; import '~/styles/main.scss';
import 'tippy.js/dist/tippy.css';
const mobxStore = getMOBXStore(); const mobxStore = getMOBXStore();

View file

@ -23,6 +23,7 @@ const BorisPage: VFC = observer(() => {
comments, comments,
hasMore, hasMore,
isLoading: isLoadingComments, isLoading: isLoadingComments,
isLoadingMore,
} = useNodeComments(696); } = useNodeComments(696);
const { title, setIsBetaTester, isTester, stats, isLoadingStats } = useBoris(comments); const { title, setIsBetaTester, isTester, stats, isLoadingStats } = useBoris(comments);
@ -33,6 +34,7 @@ const BorisPage: VFC = observer(() => {
comments={comments} comments={comments}
hasMore={hasMore} hasMore={hasMore}
isLoading={isLoadingComments} isLoading={isLoadingComments}
isLoadingMore={isLoadingMore}
onShowImageModal={onShowImageModal} onShowImageModal={onShowImageModal}
onLoadMoreComments={onLoadMoreComments} onLoadMoreComments={onLoadMoreComments}
onDeleteComment={onDeleteComment} onDeleteComment={onDeleteComment}

View file

@ -99,6 +99,7 @@ const NodePage: FC<Props> = observer(props => {
comments, comments,
hasMore, hasMore,
isLoading: isLoadingComments, isLoading: isLoadingComments,
isLoadingMore: isLoadingMoreComments,
} = useNodeComments(parseInt(id, 10), props.comments); } = useNodeComments(parseInt(id, 10), props.comments);
const { onDelete: onTagDelete, onChange: onTagsChange, onClick: onTagClick } = useNodeTags( const { onDelete: onTagDelete, onChange: onTagsChange, onClick: onTagClick } = useNodeTags(
@ -122,6 +123,7 @@ const NodePage: FC<Props> = observer(props => {
hasMore={hasMore} hasMore={hasMore}
lastSeenCurrent={lastSeen} lastSeenCurrent={lastSeen}
isLoading={isLoadingComments} isLoading={isLoadingComments}
isLoadingMore={isLoadingMoreComments}
onShowImageModal={onShowImageModal} onShowImageModal={onShowImageModal}
onLoadMoreComments={onLoadMoreComments} onLoadMoreComments={onLoadMoreComments}
onDeleteComment={onDeleteComment} onDeleteComment={onDeleteComment}

View file

@ -7,6 +7,7 @@ export interface CommentProviderProps {
hasMore: boolean; hasMore: boolean;
lastSeenCurrent?: string | null; lastSeenCurrent?: string | null;
isLoading: boolean; isLoading: boolean;
isLoadingMore: boolean;
onShowImageModal: (images: IFile[], index: number) => void; onShowImageModal: (images: IFile[], index: number) => void;
onLoadMoreComments: () => void; onLoadMoreComments: () => void;
onSaveComment: (comment: IComment) => Promise<unknown>; onSaveComment: (comment: IComment) => Promise<unknown>;
@ -19,6 +20,7 @@ const CommentContext = createContext<CommentProviderProps>({
hasMore: false, hasMore: false,
lastSeenCurrent: null, lastSeenCurrent: null,
isLoading: false, isLoading: false,
isLoadingMore: false,
onSaveComment: async () => {}, onSaveComment: async () => {},
onShowImageModal: () => {}, onShowImageModal: () => {},
onLoadMoreComments: () => {}, onLoadMoreComments: () => {},

File diff suppressed because one or more lines are too long