mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
Отрефакторил бэк, исправил ошибки (#138)
* fixed paths to match refactored backend * fixed some paths according to new backend * fixed auth urls for new endpoints * fixed urls * fixed error handling * fixes * fixed error handling on user form * fixed error handling on oauth * using fallback: true on node pages * type button for comment attach buttons * fixed return types of social delete * changed the way we upload user avatars
This commit is contained in:
parent
1745cc636d
commit
080d59858c
42 changed files with 544 additions and 420 deletions
|
@ -1,7 +1,7 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Avatar } from '~/components/common/Avatar';
|
||||
import { MenuButton } from '~/components/menu';
|
||||
import { MenuButton } from '~/components/menu/MenuButton';
|
||||
import { ProfileQuickInfo } from '~/containers/profile/ProfileQuickInfo';
|
||||
import { IUser } from '~/types/auth';
|
||||
import { path } from '~/utils/ramda';
|
||||
|
@ -16,7 +16,11 @@ const CommentAvatar: FC<Props> = ({ user, className }) => {
|
|||
<MenuButton
|
||||
position="auto"
|
||||
icon={
|
||||
<Avatar url={path(['photo', 'url'], user)} username={user.username} className={className} />
|
||||
<Avatar
|
||||
url={path(['photo', 'url'], user)}
|
||||
username={user.username}
|
||||
className={className}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ProfileQuickInfo user={user} />
|
||||
|
|
|
@ -10,26 +10,32 @@ interface IProps {
|
|||
|
||||
const CommentFormAttachButtons: FC<IProps> = ({ onUpload }) => {
|
||||
const onInputChange = useCallback(
|
||||
event => {
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const files = Array.from(event.target?.files as File[]).filter((file: File) =>
|
||||
COMMENT_FILE_TYPES.includes(file.type)
|
||||
const files = Array.from(event.target?.files as File[]).filter(
|
||||
(file: File) => COMMENT_FILE_TYPES.includes(file.type),
|
||||
);
|
||||
if (!files || !files.length) return;
|
||||
|
||||
onUpload(files);
|
||||
},
|
||||
[onUpload]
|
||||
[onUpload],
|
||||
);
|
||||
|
||||
return (
|
||||
<ButtonGroup>
|
||||
<Button iconLeft="photo" size="small" color="gray" iconOnly>
|
||||
<Button iconLeft="photo" size="small" color="gray" iconOnly type="button">
|
||||
<input type="file" onInput={onInputChange} multiple accept="image/*" />
|
||||
</Button>
|
||||
|
||||
<Button iconRight="audio" size="small" color="gray" iconOnly>
|
||||
<Button
|
||||
iconRight="audio"
|
||||
size="small"
|
||||
color="gray"
|
||||
iconOnly
|
||||
type="button"
|
||||
>
|
||||
<input type="file" onInput={onInputChange} multiple accept="audio/*" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { SortableAudioGrid, SortableImageGrid } from '~/components/sortable';
|
||||
import { SortableAudioGrid } from '~/components/sortable/SortableAudioGrid';
|
||||
import { SortableImageGrid } from '~/components/sortable/SortableImageGrid';
|
||||
import { COMMENT_FILE_TYPES } from '~/constants/uploads';
|
||||
import { useFileDropZone } from '~/hooks';
|
||||
import { IFile } from '~/types';
|
||||
|
@ -27,34 +28,36 @@ const CommentFormAttaches: FC = () => {
|
|||
|
||||
const onImageMove = useCallback(
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles([...filesAudios, ...newFiles.filter(it => it)]);
|
||||
setFiles([...filesAudios, ...newFiles.filter((it) => it)]);
|
||||
},
|
||||
[setFiles, filesImages, filesAudios]
|
||||
[setFiles, filesImages, filesAudios],
|
||||
);
|
||||
|
||||
const onAudioMove = useCallback(
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles([...filesImages, ...newFiles]);
|
||||
},
|
||||
[setFiles, filesImages, filesAudios]
|
||||
[setFiles, filesImages, filesAudios],
|
||||
);
|
||||
|
||||
const onFileDelete = useCallback(
|
||||
(fileId: IFile['id']) => {
|
||||
setFiles(files.filter(file => file.id !== fileId));
|
||||
setFiles(files.filter((file) => file.id !== fileId));
|
||||
},
|
||||
[files, setFiles]
|
||||
[files, setFiles],
|
||||
);
|
||||
|
||||
const onAudioTitleChange = useCallback(
|
||||
(fileId: IFile['id'], title: string) => {
|
||||
setFiles(
|
||||
files.map(file =>
|
||||
file.id === fileId ? { ...file, metadata: { ...file.metadata, title } } : file
|
||||
)
|
||||
files.map((file) =>
|
||||
file.id === fileId
|
||||
? { ...file, metadata: { ...file.metadata, title } }
|
||||
: file,
|
||||
),
|
||||
);
|
||||
},
|
||||
[files, setFiles]
|
||||
[files, setFiles],
|
||||
);
|
||||
|
||||
if (!hasAttaches) return null;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { SortableAudioGrid } from '~/components/sortable';
|
||||
import { SortableAudioGrid } from '~/components/sortable/SortableAudioGrid';
|
||||
import { UploadStatus } from '~/store/uploader/UploaderStore';
|
||||
import { IFile } from '~/types';
|
||||
|
||||
|
@ -15,25 +15,27 @@ const AudioGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
|||
(newFiles: IFile[]) => {
|
||||
setFiles(newFiles);
|
||||
},
|
||||
[setFiles, files]
|
||||
[setFiles, files],
|
||||
);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(remove_id: IFile['id']) => {
|
||||
setFiles(files.filter(file => file && file.id !== remove_id));
|
||||
setFiles(files.filter((file) => file && file.id !== remove_id));
|
||||
},
|
||||
[setFiles, files]
|
||||
[setFiles, files],
|
||||
);
|
||||
|
||||
const onTitleChange = useCallback(
|
||||
(changeId: IFile['id'], title: string) => {
|
||||
setFiles(
|
||||
files.map(file =>
|
||||
file && file.id === changeId ? { ...file, metadata: { ...file.metadata, title } } : file
|
||||
)
|
||||
files.map((file) =>
|
||||
file && file.id === changeId
|
||||
? { ...file, metadata: { ...file.metadata, title } }
|
||||
: file,
|
||||
),
|
||||
);
|
||||
},
|
||||
[setFiles, files]
|
||||
[setFiles, files],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
|
||||
import { SortableImageGrid } from '~/components/sortable';
|
||||
import { SortableImageGrid } from '~/components/sortable/SortableImageGrid';
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
import { UploadStatus } from '~/store/uploader/UploaderStore';
|
||||
import { IFile } from '~/types';
|
||||
|
@ -16,14 +16,14 @@ const ImageGrid: FC<IProps> = ({ files, setFiles, locked }) => {
|
|||
|
||||
const onMove = useCallback(
|
||||
(newFiles: IFile[]) => {
|
||||
setFiles(newFiles.filter(it => it));
|
||||
setFiles(newFiles.filter((it) => it));
|
||||
},
|
||||
[setFiles, files],
|
||||
);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(id: IFile['id']) => {
|
||||
setFiles(files.filter(file => file && file.id !== id));
|
||||
setFiles(files.filter((file) => file && file.id !== id));
|
||||
},
|
||||
[setFiles, files],
|
||||
);
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
export * from './VerticalMenu';
|
||||
export * from './HorizontalMenu';
|
||||
export * from './MenuButton';
|
||||
export * from './MenuItemWithIcon';
|
||||
export * from './SeparatedMenu';
|
|
@ -1,38 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
|
||||
import { Filler } from '~/components/containers/Filler';
|
||||
import { Group } from '~/components/containers/Group';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps {
|
||||
title: string;
|
||||
description?: string;
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const MenuButton: FC<IProps> = ({
|
||||
title,
|
||||
icon,
|
||||
description,
|
||||
}) => (
|
||||
<div
|
||||
className={styles.button}
|
||||
>
|
||||
<Group horizontal>
|
||||
<div className={styles.icon}>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
|
||||
<path fill="none" d="M0 0h24v24H0V0z" />
|
||||
<path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<Filler>
|
||||
<div className={styles.title}>{title}</div>
|
||||
{ description && <div className={styles.description}>{description}</div> }
|
||||
</Filler>
|
||||
</Group>
|
||||
</div>
|
||||
);
|
||||
|
||||
export { MenuButton };
|
|
@ -1,29 +0,0 @@
|
|||
@import 'src/styles/variables';
|
||||
|
||||
.button {
|
||||
fill: white;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon {
|
||||
flex: 0 0 38px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.title {
|
||||
font: $font_16_semibold;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 2px;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.description {
|
||||
font: $font_12_regular;
|
||||
color: $gray_75;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -3,7 +3,9 @@ import React, { VFC } from 'react';
|
|||
import Tippy from '@tippyjs/react';
|
||||
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { MenuButton, MenuItemWithIcon, SeparatedMenu } from '~/components/menu';
|
||||
import { MenuButton } from '~/components/menu/MenuButton';
|
||||
import { MenuItemWithIcon } from '~/components/menu/MenuItemWithIcon';
|
||||
import { SeparatedMenu } from '~/components/menu/SeparatedMenu';
|
||||
import { useWindowSize } from '~/hooks/dom/useWindowSize';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
|
|
@ -3,7 +3,7 @@ import React, { memo, VFC } from 'react';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { SeparatedMenu } from '~/components/menu';
|
||||
import { SeparatedMenu } from '~/components/menu/SeparatedMenu';
|
||||
import { NodeEditMenu } from '~/components/node/NodeEditMenu';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { getPrettyDate } from '~/utils/dom';
|
||||
|
@ -35,7 +35,6 @@ interface IProps {
|
|||
|
||||
const NodeTitle: VFC<IProps> = memo(
|
||||
({
|
||||
id,
|
||||
title,
|
||||
username,
|
||||
createdAt,
|
||||
|
@ -69,7 +68,9 @@ const NodeTitle: VFC<IProps> = memo(
|
|||
{isLoading ? (
|
||||
<Placeholder width="100px" />
|
||||
) : (
|
||||
`~${username.toLocaleLowerCase()}, ${getPrettyDate(createdAt)}`
|
||||
`~${username.toLocaleLowerCase()}, ${getPrettyDate(
|
||||
createdAt,
|
||||
)}`
|
||||
)}
|
||||
</aside>
|
||||
)}
|
||||
|
@ -90,7 +91,9 @@ const NodeTitle: VFC<IProps> = memo(
|
|||
|
||||
{canLike && (
|
||||
<div
|
||||
className={classNames(styles.button, styles.like, { [styles.is_liked]: isLiked })}
|
||||
className={classNames(styles.button, styles.like, {
|
||||
[styles.is_liked]: isLiked,
|
||||
})}
|
||||
>
|
||||
{isLiked ? (
|
||||
<Icon icon="heart_full" size={24} onClick={onLike} />
|
||||
|
@ -107,7 +110,7 @@ const NodeTitle: VFC<IProps> = memo(
|
|||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
export { NodeTitle };
|
||||
|
|
|
@ -5,7 +5,7 @@ import { rectSortingStrategy, SortableContext } from '@dnd-kit/sortable';
|
|||
import classNames from 'classnames';
|
||||
|
||||
import { DragOverlayItem } from '~/components/sortable/DragOverlayItem';
|
||||
import { useSortableActions } from '~/hooks/sortable';
|
||||
import { useSortableActions } from '~/hooks/sortable/useSortableActions';
|
||||
import { DivProps } from '~/utils/types';
|
||||
|
||||
import { SortableItem } from '../SortableItem';
|
||||
|
@ -16,7 +16,7 @@ interface SortableGridProps<
|
|||
ItemRendererProps extends {},
|
||||
LockedRendererProps extends {},
|
||||
Item extends {},
|
||||
Locked extends {}
|
||||
Locked extends {},
|
||||
> {
|
||||
items: Item[];
|
||||
locked: Locked[];
|
||||
|
@ -44,18 +44,18 @@ const SortableGrid = <RIP, RLP, I, L>({
|
|||
onSortEnd,
|
||||
size,
|
||||
}: SortableGridProps<RIP, RLP, I, L>) => {
|
||||
const { sensors, onDragEnd, onDragStart, draggingItem, ids } = useSortableActions(
|
||||
items,
|
||||
getID,
|
||||
onSortEnd
|
||||
);
|
||||
const { sensors, onDragEnd, onDragStart, draggingItem, ids } =
|
||||
useSortableActions(items, getID, onSortEnd);
|
||||
|
||||
const gridStyle = useMemo<DivProps['style']>(
|
||||
() =>
|
||||
size
|
||||
? { gridTemplateColumns: size && `repeat(auto-fill, minmax(${size}px, 1fr))` }
|
||||
? {
|
||||
gridTemplateColumns:
|
||||
size && `repeat(auto-fill, minmax(${size}px, 1fr))`,
|
||||
}
|
||||
: undefined,
|
||||
[size]
|
||||
[size],
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -67,30 +67,35 @@ const SortableGrid = <RIP, RLP, I, L>({
|
|||
>
|
||||
<SortableContext items={ids} strategy={rectSortingStrategy}>
|
||||
<div className={classNames(styles.grid, className)} style={gridStyle}>
|
||||
{items.map(item => (
|
||||
{items.map((item) => (
|
||||
<SortableItem
|
||||
key={getID(item)}
|
||||
id={getID(item)}
|
||||
className={
|
||||
draggingItem && getID(item) === getID(draggingItem) ? styles.dragging : undefined
|
||||
draggingItem && getID(item) === getID(draggingItem)
|
||||
? styles.dragging
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{createElement(renderItem, { ...renderItemProps, item })}
|
||||
</SortableItem>
|
||||
))}
|
||||
|
||||
{locked.map(item =>
|
||||
{locked.map((item) =>
|
||||
createElement(renderLocked, {
|
||||
...renderLockedProps,
|
||||
locked: item,
|
||||
key: getLockedID(item),
|
||||
})
|
||||
}),
|
||||
)}
|
||||
|
||||
<DragOverlay>
|
||||
{draggingItem ? (
|
||||
<DragOverlayItem>
|
||||
{createElement(renderItem, { ...renderItemProps, item: draggingItem })}
|
||||
{createElement(renderItem, {
|
||||
...renderItemProps,
|
||||
item: draggingItem,
|
||||
})}
|
||||
</DragOverlayItem>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import React, { createElement, FC } from 'react';
|
||||
|
||||
import { closestCenter, DndContext, DragOverlay } from '@dnd-kit/core';
|
||||
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
|
||||
import {
|
||||
SortableContext,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { DragOverlayItem } from '~/components/sortable/DragOverlayItem';
|
||||
import { SortableItem } from '~/components/sortable/SortableItem';
|
||||
import { useSortableActions } from '~/hooks/sortable';
|
||||
import { useSortableActions } from '~/hooks/sortable/useSortableActions';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
|
@ -14,7 +17,7 @@ interface SortableListProps<
|
|||
RenderItemProps extends {},
|
||||
RenderLockedProps extends {},
|
||||
Item extends {},
|
||||
Locked extends {}
|
||||
Locked extends {},
|
||||
> {
|
||||
items: Item[];
|
||||
locked: Locked[];
|
||||
|
@ -40,11 +43,8 @@ const SortableList = <RIP, RLP, I, L>({
|
|||
renderLockedProps,
|
||||
onSortEnd,
|
||||
}: SortableListProps<RIP, RLP, I, L>) => {
|
||||
const { sensors, onDragEnd, onDragStart, draggingItem, ids } = useSortableActions(
|
||||
items,
|
||||
getID,
|
||||
onSortEnd
|
||||
);
|
||||
const { sensors, onDragEnd, onDragStart, draggingItem, ids } =
|
||||
useSortableActions(items, getID, onSortEnd);
|
||||
|
||||
return (
|
||||
<DndContext
|
||||
|
@ -55,30 +55,39 @@ const SortableList = <RIP, RLP, I, L>({
|
|||
>
|
||||
<SortableContext items={ids} strategy={verticalListSortingStrategy}>
|
||||
<div className={classNames(styles.grid, className)}>
|
||||
{items.map(item => (
|
||||
{items.map((item) => (
|
||||
<SortableItem
|
||||
key={getID(item)}
|
||||
id={getID(item)}
|
||||
className={
|
||||
draggingItem && getID(item) === getID(draggingItem) ? styles.dragging : undefined
|
||||
draggingItem && getID(item) === getID(draggingItem)
|
||||
? styles.dragging
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{createElement(renderItem, { ...renderItemProps, item, key: getID(item) })}
|
||||
{createElement(renderItem, {
|
||||
...renderItemProps,
|
||||
item,
|
||||
key: getID(item),
|
||||
})}
|
||||
</SortableItem>
|
||||
))}
|
||||
|
||||
{locked.map(item =>
|
||||
{locked.map((item) =>
|
||||
createElement(renderLocked, {
|
||||
...renderLockedProps,
|
||||
locked: item,
|
||||
key: getLockedID(item),
|
||||
})
|
||||
}),
|
||||
)}
|
||||
|
||||
<DragOverlay>
|
||||
{draggingItem ? (
|
||||
<DragOverlayItem>
|
||||
{createElement(renderItem, { ...renderItemProps, item: draggingItem })}
|
||||
{createElement(renderItem, {
|
||||
...renderItemProps,
|
||||
item: draggingItem,
|
||||
})}
|
||||
</DragOverlayItem>
|
||||
) : null}
|
||||
</DragOverlay>
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
export * from './SortableImageGrid';
|
||||
export * from './SortableAudioGrid';
|
Loading…
Add table
Add a link
Reference in a new issue