mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
99 use swr (#100)
* 99: made node use SWR * 99: fixed comments for SWR node * 99: added error toast to useNodeFormFormik.ts
This commit is contained in:
parent
832386d39a
commit
c2d1c2bfc9
35 changed files with 366 additions and 413 deletions
|
@ -8,8 +8,9 @@ import classNames from 'classnames';
|
|||
import { NEW_COMMENT_CLASSNAME } from '~/constants/comment';
|
||||
|
||||
type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||
is_empty?: boolean;
|
||||
is_loading?: boolean;
|
||||
nodeId: number;
|
||||
isEmpty?: boolean;
|
||||
isLoading?: boolean;
|
||||
group: ICommentGroup;
|
||||
isSame?: boolean;
|
||||
canEdit?: boolean;
|
||||
|
@ -20,9 +21,10 @@ type IProps = HTMLAttributes<HTMLDivElement> & {
|
|||
const Comment: FC<IProps> = memo(
|
||||
({
|
||||
group,
|
||||
is_empty,
|
||||
nodeId,
|
||||
isEmpty,
|
||||
isSame,
|
||||
is_loading,
|
||||
isLoading,
|
||||
className,
|
||||
canEdit,
|
||||
onDelete,
|
||||
|
@ -34,8 +36,8 @@ const Comment: FC<IProps> = memo(
|
|||
className={classNames(className, {
|
||||
[NEW_COMMENT_CLASSNAME]: group.hasNew,
|
||||
})}
|
||||
isEmpty={is_empty}
|
||||
isLoading={is_loading}
|
||||
isEmpty={isEmpty}
|
||||
isLoading={isLoading}
|
||||
user={group.user}
|
||||
isNew={group.hasNew && !isSame}
|
||||
{...props}
|
||||
|
@ -48,9 +50,10 @@ const Comment: FC<IProps> = memo(
|
|||
|
||||
return (
|
||||
<CommentContent
|
||||
nodeId={nodeId}
|
||||
comment={comment}
|
||||
key={comment.id}
|
||||
can_edit={!!canEdit}
|
||||
canEdit={!!canEdit}
|
||||
onDelete={onDelete}
|
||||
onShowImageModal={onShowImageModal}
|
||||
/>
|
||||
|
|
|
@ -13,106 +13,108 @@ import { PRESETS } from '~/constants/urls';
|
|||
import { COMMENT_BLOCK_RENDERERS } from '~/constants/comment';
|
||||
import { CommentMenu } from '../CommentMenu';
|
||||
import { CommentForm } from '~/components/comment/CommentForm';
|
||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||
import { selectNode } from '~/redux/node/selectors';
|
||||
|
||||
interface IProps {
|
||||
nodeId: number;
|
||||
comment: IComment;
|
||||
can_edit: boolean;
|
||||
canEdit: boolean;
|
||||
onDelete: (id: IComment['id'], isLocked: boolean) => void;
|
||||
onShowImageModal: (images: IFile[], index: number) => void;
|
||||
}
|
||||
|
||||
const CommentContent: FC<IProps> = memo(({ comment, can_edit, onDelete, onShowImageModal }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const { current } = useShallowSelect(selectNode);
|
||||
const CommentContent: FC<IProps> = memo(
|
||||
({ comment, canEdit, nodeId, onDelete, onShowImageModal }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const startEditing = useCallback(() => setIsEditing(true), [setIsEditing]);
|
||||
const stopEditing = useCallback(() => setIsEditing(false), [setIsEditing]);
|
||||
const startEditing = useCallback(() => setIsEditing(true), [setIsEditing]);
|
||||
const stopEditing = useCallback(() => setIsEditing(false), [setIsEditing]);
|
||||
|
||||
const groupped = useMemo<Record<keyof typeof UPLOAD_TYPES, IFile[]>>(
|
||||
() =>
|
||||
reduce(
|
||||
(group, file) =>
|
||||
file.type ? assocPath([file.type], append(file, group[file.type]), group) : group,
|
||||
{},
|
||||
comment.files
|
||||
),
|
||||
[comment]
|
||||
);
|
||||
const groupped = useMemo<Record<keyof typeof UPLOAD_TYPES, IFile[]>>(
|
||||
() =>
|
||||
reduce(
|
||||
(group, file) =>
|
||||
file.type ? assocPath([file.type], append(file, group[file.type]), group) : group,
|
||||
{},
|
||||
comment.files
|
||||
),
|
||||
[comment]
|
||||
);
|
||||
|
||||
const onLockClick = useCallback(() => {
|
||||
onDelete(comment.id, !comment.deleted_at);
|
||||
}, [comment, onDelete]);
|
||||
const onLockClick = useCallback(() => {
|
||||
onDelete(comment.id, !comment.deleted_at);
|
||||
}, [comment, onDelete]);
|
||||
|
||||
const menu = useMemo(
|
||||
() => can_edit && <CommentMenu onDelete={onLockClick} onEdit={startEditing} />,
|
||||
[can_edit, startEditing, onLockClick]
|
||||
);
|
||||
const menu = useMemo(
|
||||
() => canEdit && <CommentMenu onDelete={onLockClick} onEdit={startEditing} />,
|
||||
[canEdit, startEditing, onLockClick]
|
||||
);
|
||||
|
||||
const blocks = useMemo(
|
||||
() =>
|
||||
!!comment.text.trim()
|
||||
? formatCommentText(path(['user', 'username'], comment), comment.text)
|
||||
: [],
|
||||
[comment]
|
||||
);
|
||||
const blocks = useMemo(
|
||||
() =>
|
||||
!!comment.text.trim()
|
||||
? formatCommentText(path(['user', 'username'], comment), comment.text)
|
||||
: [],
|
||||
[comment]
|
||||
);
|
||||
|
||||
if (isEditing) {
|
||||
return <CommentForm nodeId={current.id} comment={comment} onCancelEdit={stopEditing} />;
|
||||
}
|
||||
if (isEditing) {
|
||||
return <CommentForm nodeId={nodeId} comment={comment} onCancelEdit={stopEditing} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
{comment.text && (
|
||||
<Group className={classnames(styles.block, styles.block_text)}>
|
||||
{menu}
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
{comment.text && (
|
||||
<Group className={classnames(styles.block, styles.block_text)}>
|
||||
{menu}
|
||||
|
||||
<Group className={styles.renderers}>
|
||||
{blocks.map(
|
||||
(block, key) =>
|
||||
COMMENT_BLOCK_RENDERERS[block.type] &&
|
||||
createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key })
|
||||
)}
|
||||
<Group className={styles.renderers}>
|
||||
{blocks.map(
|
||||
(block, key) =>
|
||||
COMMENT_BLOCK_RENDERERS[block.type] &&
|
||||
createElement(COMMENT_BLOCK_RENDERERS[block.type], { block, key })
|
||||
)}
|
||||
</Group>
|
||||
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</Group>
|
||||
)}
|
||||
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</Group>
|
||||
)}
|
||||
{groupped.image && groupped.image.length > 0 && (
|
||||
<div className={classnames(styles.block, styles.block_image)}>
|
||||
{menu}
|
||||
|
||||
{groupped.image && groupped.image.length > 0 && (
|
||||
<div className={classnames(styles.block, styles.block_image)}>
|
||||
{menu}
|
||||
<div
|
||||
className={classNames(styles.images, {
|
||||
[styles.multiple]: groupped.image.length > 1,
|
||||
})}
|
||||
>
|
||||
{groupped.image.map((file, index) => (
|
||||
<div key={file.id} onClick={() => onShowImageModal(groupped.image, index)}>
|
||||
<img src={getURL(file, PRESETS['600'])} alt={file.name} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={classNames(styles.images, { [styles.multiple]: groupped.image.length > 1 })}
|
||||
>
|
||||
{groupped.image.map((file, index) => (
|
||||
<div key={file.id} onClick={() => onShowImageModal(groupped.image, index)}>
|
||||
<img src={getURL(file, PRESETS['600'])} alt={file.name} />
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{groupped.audio && groupped.audio.length > 0 && (
|
||||
<Fragment>
|
||||
{groupped.audio.map(file => (
|
||||
<div className={classnames(styles.block, styles.block_audio)} key={file.id}>
|
||||
{menu}
|
||||
|
||||
<AudioPlayer file={file} />
|
||||
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{groupped.audio && groupped.audio.length > 0 && (
|
||||
<Fragment>
|
||||
{groupped.audio.map(file => (
|
||||
<div className={classnames(styles.block, styles.block_audio)} key={file.id}>
|
||||
{menu}
|
||||
|
||||
<AudioPlayer file={file} />
|
||||
|
||||
<div className={styles.date}>{getPrettyDate(comment.created_at)}</div>
|
||||
</div>
|
||||
))}
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
</Fragment>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
export { CommentContent };
|
||||
|
|
|
@ -10,7 +10,7 @@ import styles from './styles.module.scss';
|
|||
import { NodeEditorProps } from '~/redux/node/types';
|
||||
import { useNodeImages } from '~/utils/hooks/node/useNodeImages';
|
||||
import { useNodeAudios } from '~/utils/hooks/node/useNodeAudios';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
import { UploadDropzone } from '~/components/upload/UploadDropzone';
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React, { FC, createElement } from 'react';
|
||||
import styles from './styles.module.scss';
|
||||
import { INode } from '~/redux/types';
|
||||
import { NODE_PANEL_COMPONENTS } from '~/redux/node/constants';
|
||||
import { has } from 'ramda';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
|
||||
const EditorActionsPanel: FC = () => {
|
||||
const { values } = useNodeFormContext();
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Group } from '~/components/containers/Group';
|
|||
import { InputText } from '~/components/input/InputText';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { Padder } from '~/components/containers/Padder';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
|
||||
const EditorButtons: FC = () => {
|
||||
const { values, handleChange, isSubmitting } = useNodeFormContext();
|
||||
|
@ -28,6 +28,7 @@ const EditorButtons: FC = () => {
|
|||
iconRight="check"
|
||||
color={values.is_promoted ? 'primary' : 'lab'}
|
||||
disabled={isSubmitting}
|
||||
type="submit"
|
||||
/>
|
||||
</Group>
|
||||
</Padder>
|
||||
|
|
|
@ -3,7 +3,7 @@ import { IEditorComponentProps } from '~/redux/node/types';
|
|||
import { Button } from '~/components/input/Button';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import styles from './styles.module.scss';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
|
||||
interface IProps extends IEditorComponentProps {}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { UPLOAD_TYPES } from '~/redux/uploads/constants';
|
|||
import { IEditorComponentProps } from '~/redux/node/types';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
import { getFileType } from '~/utils/uploader';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
import { Button } from '~/components/input/Button';
|
||||
|
||||
type IProps = IEditorComponentProps & {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { Icon } from '~/components/input/Icon';
|
|||
import { PRESETS } from '~/constants/urls';
|
||||
import { IEditorComponentProps } from '~/redux/node/types';
|
||||
import { useFileUploader, useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
import { getFileType } from '~/utils/uploader';
|
||||
|
||||
type IProps = IEditorComponentProps & {};
|
||||
|
|
|
@ -4,7 +4,7 @@ import styles from './styles.module.scss';
|
|||
import { Textarea } from '~/components/input/Textarea';
|
||||
import { path } from 'ramda';
|
||||
import { NodeEditorProps } from '~/redux/node/types';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
|
||||
type IProps = NodeEditorProps & {};
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { InputText } from '~/components/input/InputText';
|
|||
import classnames from 'classnames';
|
||||
import { getYoutubeThumb } from '~/utils/dom';
|
||||
import { NodeEditorProps } from '~/redux/node/types';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useNodeFormContext } from '~/utils/hooks/node/useNodeFormFormik';
|
||||
|
||||
type IProps = NodeEditorProps & {};
|
||||
|
||||
|
|
|
@ -64,7 +64,6 @@ const NodeImageSwiperBlock: FC<IProps> = ({ node }) => {
|
|||
<div className={styles.single}>
|
||||
<ImagePreloader
|
||||
file={images[0]}
|
||||
onLoad={updateSwiper}
|
||||
onClick={() => onOpenPhotoSwipe(0)}
|
||||
className={styles.image}
|
||||
/>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue