mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
removed all router-modals
This commit is contained in:
parent
85d20e5009
commit
ffce400398
15 changed files with 99 additions and 143 deletions
|
@ -1,26 +1,26 @@
|
||||||
import React, { FC, useCallback, useState } from 'react';
|
import React, { FC, useCallback, useState } from 'react';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useRouteMatch } from 'react-router';
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
import { useShowModal } from '~/hooks/modal/useShowModal';
|
||||||
|
import { Dialog } from '~/constants/modal';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLab?: boolean;
|
isLab?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SubmitBar: FC<Props> = ({ isLab }) => {
|
const SubmitBar: FC<Props> = ({ isLab }) => {
|
||||||
const { url } = useRouteMatch();
|
const showModal = useShowModal(Dialog.CreateNode);
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
|
||||||
const onFocus = useCallback(() => setFocused(true), [setFocused]);
|
const onFocus = useCallback(() => setFocused(true), [setFocused]);
|
||||||
const onBlur = useCallback(() => setFocused(false), [setFocused]);
|
const onBlur = useCallback(() => setFocused(false), [setFocused]);
|
||||||
|
|
||||||
const createUrl = useCallback(
|
const createUrl = useCallback(
|
||||||
(type: string) => {
|
(type: string) => () => {
|
||||||
return [url.replace(/\/$/, ''), 'create', type].join('/');
|
showModal({ type, isInLab: !!isLab });
|
||||||
},
|
},
|
||||||
[url]
|
[isLab, showModal]
|
||||||
);
|
);
|
||||||
|
|
||||||
const icon = isLab ? 'lab' : 'plus';
|
const icon = isLab ? 'lab' : 'plus';
|
||||||
|
@ -28,21 +28,21 @@ const SubmitBar: FC<Props> = ({ isLab }) => {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.wrap, { [styles.lab]: isLab })}>
|
<div className={classNames(styles.wrap, { [styles.lab]: isLab })}>
|
||||||
<div className={classNames(styles.panel, { [styles.active]: focused })}>
|
<div className={classNames(styles.panel, { [styles.active]: focused })}>
|
||||||
<Link to={createUrl('image')} className={styles.link}>
|
<button onClick={createUrl('image')} className={styles.link}>
|
||||||
<Icon icon="image" size={32} />
|
<Icon icon="image" size={32} />
|
||||||
</Link>
|
</button>
|
||||||
|
|
||||||
<Link to={createUrl('text')} className={styles.link}>
|
<button onClick={createUrl('text')} className={styles.link}>
|
||||||
<Icon icon="text" size={32} />
|
<Icon icon="text" size={32} />
|
||||||
</Link>
|
</button>
|
||||||
|
|
||||||
<Link to={createUrl('video')} className={styles.link}>
|
<button onClick={createUrl('video')} className={styles.link}>
|
||||||
<Icon icon="video" size={32} />
|
<Icon icon="video" size={32} />
|
||||||
</Link>
|
</button>
|
||||||
|
|
||||||
<Link to={createUrl('audio')} className={styles.link}>
|
<button onClick={createUrl('audio')} className={styles.link}>
|
||||||
<Icon icon="audio" size={32} />
|
<Icon icon="audio" size={32} />
|
||||||
</Link>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button className={styles.button} onFocus={onFocus} onBlur={onBlur} type="button">
|
<button className={styles.button} onFocus={onFocus} onBlur={onBlur} type="button">
|
||||||
|
|
|
@ -27,6 +27,7 @@ interface IProps {
|
||||||
onLike: () => void;
|
onLike: () => void;
|
||||||
onStar: () => void;
|
onStar: () => void;
|
||||||
onLock: () => void;
|
onLock: () => void;
|
||||||
|
onEdit: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodeTitle: VFC<IProps> = memo(
|
const NodeTitle: VFC<IProps> = memo(
|
||||||
|
@ -50,6 +51,7 @@ const NodeTitle: VFC<IProps> = memo(
|
||||||
onStar,
|
onStar,
|
||||||
onLike,
|
onLike,
|
||||||
onLock,
|
onLock,
|
||||||
|
onEdit,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.wrap)}>
|
<div className={classNames(styles.wrap)}>
|
||||||
|
@ -91,11 +93,7 @@ const NodeTitle: VFC<IProps> = memo(
|
||||||
<Icon icon={isLocked ? 'locked' : 'unlocked'} size={24} onClick={onLock} />
|
<Icon icon={isLocked ? 'locked' : 'unlocked'} size={24} onClick={onLock} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{!!id && (
|
{!!id && <Icon icon="edit" size={24} onClick={onEdit} />}
|
||||||
<Link to={URLS.NODE_EDIT_URL(id)}>
|
|
||||||
<Icon icon="edit" size={24} />
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import { ProfileDialog } from '~/containers/dialogs/ProfileDialog';
|
||||||
import { RestoreRequestDialog } from '~/containers/dialogs/RestoreRequestDialog';
|
import { RestoreRequestDialog } from '~/containers/dialogs/RestoreRequestDialog';
|
||||||
import { RestorePasswordDialog } from '~/containers/dialogs/RestorePasswordDialog';
|
import { RestorePasswordDialog } from '~/containers/dialogs/RestorePasswordDialog';
|
||||||
import { PhotoSwipe } from '~/containers/dialogs/PhotoSwipe';
|
import { PhotoSwipe } from '~/containers/dialogs/PhotoSwipe';
|
||||||
|
import { EditorCreateDialog } from '~/containers/dialogs/EditorCreateDialog';
|
||||||
|
import { EditorEditDialog } from '~/containers/dialogs/EditorEditDialog';
|
||||||
|
import { TagSidebar } from '~/containers/sidebars/TagSidebar';
|
||||||
|
|
||||||
export enum Dialog {
|
export enum Dialog {
|
||||||
Login = 'Login',
|
Login = 'Login',
|
||||||
|
@ -16,6 +19,9 @@ export enum Dialog {
|
||||||
RestorePassword = 'RestorePassword',
|
RestorePassword = 'RestorePassword',
|
||||||
Test = 'Test',
|
Test = 'Test',
|
||||||
Photoswipe = 'Photoswipe',
|
Photoswipe = 'Photoswipe',
|
||||||
|
CreateNode = 'CreateNode',
|
||||||
|
EditNode = 'EditNode',
|
||||||
|
TagSidebar = 'TagNodes',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DIALOG_CONTENT = {
|
export const DIALOG_CONTENT = {
|
||||||
|
@ -27,4 +33,7 @@ export const DIALOG_CONTENT = {
|
||||||
[Dialog.RestoreRequest]: RestoreRequestDialog,
|
[Dialog.RestoreRequest]: RestoreRequestDialog,
|
||||||
[Dialog.RestorePassword]: RestorePasswordDialog,
|
[Dialog.RestorePassword]: RestorePasswordDialog,
|
||||||
[Dialog.Photoswipe]: PhotoSwipe,
|
[Dialog.Photoswipe]: PhotoSwipe,
|
||||||
|
[Dialog.CreateNode]: EditorCreateDialog,
|
||||||
|
[Dialog.EditNode]: EditorEditDialog,
|
||||||
|
[Dialog.TagSidebar]: TagSidebar,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
|
@ -1,30 +1,19 @@
|
||||||
import React, { FC, useCallback, useMemo, useRef } from 'react';
|
import React, { FC, useCallback, useMemo, useRef } from 'react';
|
||||||
import { EMPTY_NODE, NODE_TYPES } from '~/constants/node';
|
import { EMPTY_NODE, NODE_TYPES } from '~/constants/node';
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||||
import { useHistory, useRouteMatch } from 'react-router';
|
|
||||||
import { values } from 'ramda';
|
import { values } from 'ramda';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
import { useCreateNode } from '~/hooks/node/useCreateNode';
|
import { useCreateNode } from '~/hooks/node/useCreateNode';
|
||||||
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
const EditorCreateDialog: FC = () => {
|
export interface EditorCreateDialogProps extends DialogComponentProps {
|
||||||
const history = useHistory();
|
type: typeof NODE_TYPES[keyof typeof NODE_TYPES];
|
||||||
const {
|
isInLab: boolean;
|
||||||
params: { type },
|
}
|
||||||
url,
|
|
||||||
} = useRouteMatch<{ type: string }>();
|
|
||||||
|
|
||||||
const backUrl = useMemo(() => {
|
|
||||||
return (url && url.replace(/\/create\/(.*)$/, '')) || '/';
|
|
||||||
}, [url]);
|
|
||||||
|
|
||||||
const goBack = useCallback(() => {
|
|
||||||
history.replace(backUrl);
|
|
||||||
}, [backUrl, history]);
|
|
||||||
|
|
||||||
|
const EditorCreateDialog: FC<EditorCreateDialogProps> = ({ type, isInLab, onRequestClose }) => {
|
||||||
const isExist = useMemo(() => values(NODE_TYPES).some(el => el === type), [type]);
|
const isExist = useMemo(() => values(NODE_TYPES).some(el => el === type), [type]);
|
||||||
|
|
||||||
const isInLab = useMemo(() => !!url.match(/^\/lab/), [url]);
|
|
||||||
|
|
||||||
const data = useRef({ ...EMPTY_NODE, type, is_promoted: !isInLab });
|
const data = useRef({ ...EMPTY_NODE, type, is_promoted: !isInLab });
|
||||||
|
|
||||||
const createNode = useCreateNode();
|
const createNode = useCreateNode();
|
||||||
|
@ -32,16 +21,16 @@ const EditorCreateDialog: FC = () => {
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (node: INode) => {
|
async (node: INode) => {
|
||||||
await createNode(node);
|
await createNode(node);
|
||||||
goBack();
|
onRequestClose();
|
||||||
},
|
},
|
||||||
[goBack, createNode]
|
[onRequestClose, createNode]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!type || !isExist) {
|
if (!type || !isExist) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <EditorDialog node={data.current} onRequestClose={goBack} onSubmit={onSubmit} />;
|
return <EditorDialog node={data.current} onRequestClose={onRequestClose} onSubmit={onSubmit} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { EditorCreateDialog };
|
export { EditorCreateDialog };
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { createElement, FC, useCallback, useMemo, useState } from 'react';
|
import React, { createElement, FC, useCallback, useMemo, useState } from 'react';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { NODE_EDITORS } from '~/constants/node';
|
import { NODE_EDITORS } from '~/constants/node';
|
||||||
import { BetterScrollDialog } from '../../../components/dialogs/BetterScrollDialog';
|
import { BetterScrollDialog } from '~/components/dialogs/BetterScrollDialog';
|
||||||
import { CoverBackdrop } from '~/components/containers/CoverBackdrop';
|
import { CoverBackdrop } from '~/components/containers/CoverBackdrop';
|
||||||
import { prop } from 'ramda';
|
import { prop } from 'ramda';
|
||||||
import { useNodeFormFormik } from '~/hooks/node/useNodeFormFormik';
|
import { useNodeFormFormik } from '~/hooks/node/useNodeFormFormik';
|
||||||
|
@ -9,8 +9,6 @@ import { EditorButtons } from '~/components/editors/EditorButtons';
|
||||||
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
import { UploadSubject, UploadTarget } from '~/constants/uploads';
|
||||||
import { FormikProvider } from 'formik';
|
import { FormikProvider } from 'formik';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
|
||||||
import { useTranslatedError } from '~/hooks/data/useTranslatedError';
|
|
||||||
import { useCloseOnEscape } from '~/hooks';
|
import { useCloseOnEscape } from '~/hooks';
|
||||||
import { EditorConfirmClose } from '~/components/editors/EditorConfirmClose';
|
import { EditorConfirmClose } from '~/components/editors/EditorConfirmClose';
|
||||||
import { DialogComponentProps } from '~/types/modal';
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
@ -50,8 +48,6 @@ const EditorDialog: FC<Props> = observer(({ node, onRequestClose, onSubmit }) =>
|
||||||
setConfirmModalShown(true);
|
setConfirmModalShown(true);
|
||||||
}, [dirty, isConfirmModalShown, onRequestClose, closeConfirmModal]);
|
}, [dirty, isConfirmModalShown, onRequestClose, closeConfirmModal]);
|
||||||
|
|
||||||
const error = useTranslatedError(status);
|
|
||||||
|
|
||||||
useCloseOnEscape(onClose);
|
useCloseOnEscape(onClose);
|
||||||
|
|
||||||
if (!component) {
|
if (!component) {
|
||||||
|
@ -59,29 +55,26 @@ const EditorDialog: FC<Props> = observer(({ node, onRequestClose, onSubmit }) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalWrapper onOverlayClick={onClose}>
|
<UploaderContextProvider value={uploader}>
|
||||||
<UploaderContextProvider value={uploader}>
|
<FormikProvider value={formik}>
|
||||||
<FormikProvider value={formik}>
|
<form onSubmit={handleSubmit} className={styles.form}>
|
||||||
<form onSubmit={handleSubmit} className={styles.form}>
|
<BetterScrollDialog
|
||||||
<BetterScrollDialog
|
footer={<EditorButtons />}
|
||||||
footer={<EditorButtons />}
|
backdrop={<CoverBackdrop cover={values.cover} />}
|
||||||
backdrop={<CoverBackdrop cover={values.cover} />}
|
width={860}
|
||||||
width={860}
|
onClose={onClose}
|
||||||
error={error}
|
>
|
||||||
onClose={onClose}
|
<>
|
||||||
>
|
{isConfirmModalShown && (
|
||||||
<>
|
<EditorConfirmClose onApprove={onRequestClose} onDecline={closeConfirmModal} />
|
||||||
{isConfirmModalShown && (
|
)}
|
||||||
<EditorConfirmClose onApprove={onRequestClose} onDecline={closeConfirmModal} />
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={styles.editor}>{createElement(component)}</div>
|
<div className={styles.editor}>{createElement(component)}</div>
|
||||||
</>
|
</>
|
||||||
</BetterScrollDialog>
|
</BetterScrollDialog>
|
||||||
</form>
|
</form>
|
||||||
</FormikProvider>
|
</FormikProvider>
|
||||||
</UploaderContextProvider>
|
</UploaderContextProvider>
|
||||||
</ModalWrapper>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { FC, useCallback, useMemo } from 'react';
|
import React, { FC, useCallback } from 'react';
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||||
import { useHistory, useRouteMatch } from 'react-router';
|
|
||||||
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
|
@ -8,37 +7,27 @@ import { useLoadNode } from '~/hooks/node/useLoadNode';
|
||||||
import { useUpdateNode } from '~/hooks/node/useUpdateNode';
|
import { useUpdateNode } from '~/hooks/node/useUpdateNode';
|
||||||
import { INode } from '~/types';
|
import { INode } from '~/types';
|
||||||
import { observer } from 'mobx-react-lite';
|
import { observer } from 'mobx-react-lite';
|
||||||
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
const EditorEditDialog: FC = observer(() => {
|
export interface EditorEditDialogProps extends DialogComponentProps {
|
||||||
const history = useHistory();
|
nodeId: number;
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
const EditorEditDialog: FC<EditorEditDialogProps> = observer(({ nodeId, onRequestClose }) => {
|
||||||
params: { id },
|
const { node, isLoading } = useLoadNode(nodeId);
|
||||||
url,
|
const updateNode = useUpdateNode(nodeId);
|
||||||
} = useRouteMatch<{ id: string }>();
|
|
||||||
|
|
||||||
const backUrl = useMemo(() => {
|
|
||||||
return url.replace(/\/edit$/, '');
|
|
||||||
}, [url]);
|
|
||||||
|
|
||||||
const goBack = useCallback(() => {
|
|
||||||
history.replace(backUrl);
|
|
||||||
}, [backUrl, history]);
|
|
||||||
|
|
||||||
const { node, isLoading } = useLoadNode(parseInt(id, 10));
|
|
||||||
const updateNode = useUpdateNode(parseInt(id, 10));
|
|
||||||
|
|
||||||
const onSubmit = useCallback(
|
const onSubmit = useCallback(
|
||||||
async (node: INode) => {
|
async (node: INode) => {
|
||||||
await updateNode(node);
|
await updateNode(node);
|
||||||
goBack();
|
onRequestClose();
|
||||||
},
|
},
|
||||||
[updateNode, goBack]
|
[updateNode, onRequestClose]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isLoading || !node) {
|
if (isLoading || !node) {
|
||||||
return (
|
return (
|
||||||
<ModalWrapper onOverlayClick={goBack}>
|
<ModalWrapper onOverlayClick={onRequestClose}>
|
||||||
<div className={styles.loader}>
|
<div className={styles.loader}>
|
||||||
<LoaderCircle size={64} />
|
<LoaderCircle size={64} />
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,7 +35,7 @@ const EditorEditDialog: FC = observer(() => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return <EditorDialog node={node} onRequestClose={goBack} onSubmit={onSubmit} />;
|
return <EditorDialog node={node} onRequestClose={onRequestClose} onSubmit={onSubmit} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
export { EditorEditDialog };
|
export { EditorEditDialog };
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Route, Switch } from 'react-router';
|
||||||
import { TagSidebar } from '~/containers/sidebars/TagSidebar';
|
import { TagSidebar } from '~/containers/sidebars/TagSidebar';
|
||||||
import { Authorized } from '~/components/containers/Authorized';
|
import { Authorized } from '~/components/containers/Authorized';
|
||||||
import { SubmitBar } from '~/components/bars/SubmitBar';
|
import { SubmitBar } from '~/components/bars/SubmitBar';
|
||||||
import { EditorCreateDialog } from '~/containers/dialogs/EditorCreateDialog';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
prefix?: string;
|
prefix?: string;
|
||||||
|
@ -13,16 +12,9 @@ interface IProps {
|
||||||
|
|
||||||
const SidebarRouter: FC<IProps> = ({ prefix = '', isLab }) => {
|
const SidebarRouter: FC<IProps> = ({ prefix = '', isLab }) => {
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<>
|
<Authorized>
|
||||||
<Switch>
|
<SubmitBar isLab={isLab} />
|
||||||
<Route path={`${prefix}/create/:type`} component={EditorCreateDialog} />
|
</Authorized>,
|
||||||
<Route path={`${prefix}/tag/:tag`} component={TagSidebar} />
|
|
||||||
</Switch>
|
|
||||||
|
|
||||||
<Authorized>
|
|
||||||
<SubmitBar isLab={isLab} />
|
|
||||||
</Authorized>
|
|
||||||
</>,
|
|
||||||
document.body
|
document.body
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,13 +20,10 @@ const SidebarWrapper: FC<IProps> = ({ children, onClose }) => {
|
||||||
return () => clearAllBodyScrollLocks();
|
return () => clearAllBodyScrollLocks();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return createPortal(
|
return (
|
||||||
<div className={styles.wrapper} ref={ref}>
|
<div className={styles.wrapper} ref={ref}>
|
||||||
<div className={styles.clicker} onClick={onClose} />
|
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
</div>,
|
</div>
|
||||||
document.body
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,9 +10,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -22,19 +19,7 @@
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
animation: appear 0.25s forwards;
|
animation: appear 0.25s forwards;
|
||||||
|
|
||||||
@include sidebar;
|
|
||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clicker {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, { useCallback, useMemo, VFC } from 'react';
|
import React, { useMemo, VFC } from 'react';
|
||||||
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
|
import { SidebarWrapper } from '~/containers/sidebars/SidebarWrapper';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { useHistory, useRouteMatch } from 'react-router';
|
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { TagSidebarList } from '~/components/sidebar/TagSidebarList';
|
import { TagSidebarList } from '~/components/sidebar/TagSidebarList';
|
||||||
|
@ -9,21 +8,18 @@ import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
|
import { InfiniteScroll } from '~/components/containers/InfiniteScroll';
|
||||||
import { Tag } from '~/components/tags/Tag';
|
import { Tag } from '~/components/tags/Tag';
|
||||||
import { useTagNodes } from '~/hooks/tag/useTagNodes';
|
import { useTagNodes } from '~/hooks/tag/useTagNodes';
|
||||||
|
import { DialogComponentProps } from '~/types/modal';
|
||||||
|
|
||||||
const TagSidebar: VFC = () => {
|
interface TagSidebarProps extends DialogComponentProps {
|
||||||
const {
|
tag: string;
|
||||||
params: { tag },
|
}
|
||||||
url,
|
|
||||||
} = useRouteMatch<{ tag: string }>();
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const basePath = url.replace(new RegExp(`/tag/${tag}$`), '');
|
const TagSidebar: VFC<TagSidebarProps> = ({ tag, onRequestClose }) => {
|
||||||
const onClose = useCallback(() => history.push(basePath), [basePath, history]);
|
|
||||||
const { nodes, hasMore, isLoading, loadMore } = useTagNodes(tag);
|
const { nodes, hasMore, isLoading, loadMore } = useTagNodes(tag);
|
||||||
const title = useMemo(() => decodeURIComponent(tag), [tag]);
|
const title = useMemo(() => decodeURIComponent(tag), [tag]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarWrapper onClose={onClose}>
|
<SidebarWrapper onClose={onRequestClose}>
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.head}>
|
<div className={styles.head}>
|
||||||
|
@ -38,9 +34,9 @@ const TagSidebar: VFC = () => {
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className={styles.close}>
|
<div className={styles.close}>
|
||||||
<Link to={basePath}>
|
<button onClick={onRequestClose}>
|
||||||
<Icon icon="close" size={32} />
|
<Icon icon="close" size={32} />
|
||||||
</Link>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,12 @@ import { INode } from '~/types';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { apiLockNode, apiPostNodeHeroic, apiPostNodeLike } from '~/api/node';
|
import { apiLockNode, apiPostNodeHeroic, apiPostNodeLike } from '~/api/node';
|
||||||
import { showErrorToast } from '~/utils/errors/showToast';
|
import { showErrorToast } from '~/utils/errors/showToast';
|
||||||
|
import { useModal } from '~/hooks/modal/useModal';
|
||||||
|
import { Dialog } from '~/constants/modal';
|
||||||
|
|
||||||
export const useNodeActions = (node: INode, update: (node: Partial<INode>) => Promise<unknown>) => {
|
export const useNodeActions = (node: INode, update: (node: Partial<INode>) => Promise<unknown>) => {
|
||||||
|
const { showModal } = useModal();
|
||||||
|
|
||||||
const onLike = useCallback(async () => {
|
const onLike = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const result = await apiPostNodeLike({ id: node.id });
|
const result = await apiPostNodeLike({ id: node.id });
|
||||||
|
@ -37,5 +41,7 @@ export const useNodeActions = (node: INode, update: (node: Partial<INode>) => Pr
|
||||||
}
|
}
|
||||||
}, [node.deleted_at, node.id, update]);
|
}, [node.deleted_at, node.id, update]);
|
||||||
|
|
||||||
return { onLike, onStar, onLock };
|
const onEdit = useCallback(() => showModal(Dialog.EditNode, { nodeId: node.id! }), [node]);
|
||||||
|
|
||||||
|
return { onLike, onStar, onLock, onEdit };
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { useHistory } from 'react-router';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { ITag } from '~/types';
|
import { ITag } from '~/types';
|
||||||
import { URLS } from '~/constants/urls';
|
|
||||||
import { useLoadNode } from '~/hooks/node/useLoadNode';
|
import { useLoadNode } from '~/hooks/node/useLoadNode';
|
||||||
import { apiDeleteNodeTag, apiPostNodeTags } from '~/api/node';
|
import { apiDeleteNodeTag, apiPostNodeTags } from '~/api/node';
|
||||||
import { useGetNodeRelated } from '~/hooks/node/useGetNodeRelated';
|
import { useGetNodeRelated } from '~/hooks/node/useGetNodeRelated';
|
||||||
|
import { useShowModal } from '~/hooks/modal/useShowModal';
|
||||||
|
import { Dialog } from '~/constants/modal';
|
||||||
|
|
||||||
export const useNodeTags = (id: number) => {
|
export const useNodeTags = (id: number) => {
|
||||||
|
const showModal = useShowModal(Dialog.TagSidebar);
|
||||||
const { refresh: refreshRelated } = useGetNodeRelated(id);
|
const { refresh: refreshRelated } = useGetNodeRelated(id);
|
||||||
const { update } = useLoadNode(id);
|
const { update } = useLoadNode(id);
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
async (tags: string[]) => {
|
async (tags: string[]) => {
|
||||||
|
@ -30,9 +30,9 @@ export const useNodeTags = (id: number) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
history.push(URLS.NODE_TAG_URL(id, encodeURIComponent(tag.title)));
|
showModal({ tag: tag.title });
|
||||||
},
|
},
|
||||||
[history, id]
|
[showModal, id]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDelete = useCallback(
|
const onDelete = useCallback(
|
||||||
|
|
|
@ -24,7 +24,7 @@ const NodeLayout: FC<IProps> = () => {
|
||||||
const { node, isLoading, update } = useNodeContext();
|
const { node, isLoading, update } = useNodeContext();
|
||||||
const { head, block } = useNodeBlocks(node, isLoading);
|
const { head, block } = useNodeBlocks(node, isLoading);
|
||||||
const [canEdit, canLike, canStar] = useNodePermissions(node);
|
const [canEdit, canLike, canStar] = useNodePermissions(node);
|
||||||
const { onLike, onStar, onLock } = useNodeActions(node, update);
|
const { onLike, onStar, onLock, onEdit } = useNodeActions(node, update);
|
||||||
|
|
||||||
useNodeCoverImage(node);
|
useNodeCoverImage(node);
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ const NodeLayout: FC<IProps> = () => {
|
||||||
onLike={onLike}
|
onLike={onLike}
|
||||||
onStar={onStar}
|
onStar={onStar}
|
||||||
onLock={onLock}
|
onLock={onLock}
|
||||||
|
onEdit={onEdit}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,14 @@ import { makePersistable, isHydrated } from 'mobx-persist-store';
|
||||||
export class AuthStore {
|
export class AuthStore {
|
||||||
token: string = '';
|
token: string = '';
|
||||||
user: IUser = EMPTY_USER;
|
user: IUser = EMPTY_USER;
|
||||||
private isTesterInternal: boolean = false;
|
isTesterInternal: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
makeAutoObservable(this);
|
makeAutoObservable(this);
|
||||||
|
|
||||||
void makePersistable(this, {
|
void makePersistable(this, {
|
||||||
name: `vault48_auth_${process.env.REACT_APP_API_URL}`,
|
name: `vault48_auth_${process.env.REACT_APP_API_URL}`,
|
||||||
properties: ['token', 'user'],
|
properties: ['token', 'user', 'isTesterInternal'],
|
||||||
storage: window.localStorage,
|
storage: window.localStorage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,8 +164,9 @@
|
||||||
flex: 0 1 $width;
|
flex: 0 1 $width;
|
||||||
max-width: 100vw;
|
max-width: 100vw;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: transparentize($content_bg, 0.4);
|
background: transparentize($content_bg, 0.1);
|
||||||
box-shadow: transparentize(white, 0.95) -1px 0;
|
box-shadow: transparentize(white, 0.95) -1px 0;
|
||||||
|
border-radius: $radius 0 0 $radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin editor_round_button {
|
@mixin editor_round_button {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue