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:
parent
0a1d2cbf99
commit
bb81bdae69
16 changed files with 80 additions and 127 deletions
|
@ -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>
|
||||||
);
|
);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
18
src/components/input/LoadMoreButton/index.tsx
Normal file
18
src/components/input/LoadMoreButton/index.tsx
Normal 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 }
|
5
src/components/input/LoadMoreButton/styles.module.scss
Normal file
5
src/components/input/LoadMoreButton/styles.module.scss
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
@import "src/styles/variables";
|
||||||
|
|
||||||
|
.more {
|
||||||
|
width: 100%;
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -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 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue