mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
#58 made dialogs as routes
This commit is contained in:
parent
d9af895558
commit
4dc8bea040
21 changed files with 230 additions and 172 deletions
|
@ -1,58 +1,44 @@
|
||||||
import React, { FC, useCallback } from 'react';
|
import React, { FC, useCallback, useState } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
import { nodeCreate } from '~/redux/node/actions';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import styles from './styles.module.scss';
|
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { useRouteMatch } from 'react-router';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
isLab?: boolean;
|
isLab?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SubmitBar: FC<Props> = ({ isLab }) => {
|
const SubmitBar: FC<Props> = ({ isLab }) => {
|
||||||
const dispatch = useDispatch();
|
const { url } = useRouteMatch();
|
||||||
|
const [focused, setFocused] = useState(false);
|
||||||
|
|
||||||
const onOpenImageEditor = useCallback(() => dispatch(nodeCreate(NODE_TYPES.IMAGE, isLab)), [
|
const onFocus = useCallback(() => setFocused(true), [setFocused]);
|
||||||
dispatch,
|
const onBlur = useCallback(() => setFocused(false), [setFocused]);
|
||||||
]);
|
|
||||||
|
|
||||||
const onOpenTextEditor = useCallback(() => dispatch(nodeCreate(NODE_TYPES.TEXT, isLab)), [
|
|
||||||
dispatch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onOpenVideoEditor = useCallback(() => dispatch(nodeCreate(NODE_TYPES.VIDEO, isLab)), [
|
|
||||||
dispatch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onOpenAudioEditor = useCallback(() => dispatch(nodeCreate(NODE_TYPES.AUDIO, isLab)), [
|
|
||||||
dispatch,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.wrap, { [styles.lab]: isLab })}>
|
<div className={classNames(styles.wrap, { [styles.lab]: isLab })}>
|
||||||
<div className={styles.panel}>
|
<div className={classNames(styles.panel, { [styles.active]: focused })}>
|
||||||
<div onClick={onOpenImageEditor}>
|
<Link to={`${url}/create/image`} className={styles.link}>
|
||||||
<Icon icon="image" />
|
<Icon icon="image" size={32} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link to={`${url}/create/text`} className={styles.link}>
|
||||||
|
<Icon icon="text" size={32} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link to={`${url}/create/video`} className={styles.link}>
|
||||||
|
<Icon icon="video" size={32} />
|
||||||
|
</Link>
|
||||||
|
|
||||||
|
<Link to={`${url}/create/audio`} className={styles.link}>
|
||||||
|
<Icon icon="audio" size={32} />
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div onClick={onOpenTextEditor}>
|
<button className={styles.button} onFocus={onFocus} onBlur={onBlur} type="button">
|
||||||
<Icon icon="text" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div onClick={onOpenVideoEditor}>
|
|
||||||
<Icon icon="video" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div onClick={onOpenAudioEditor}>
|
|
||||||
<Icon icon="audio" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.button}>
|
|
||||||
<Icon icon="plus" />
|
<Icon icon="plus" />
|
||||||
</div>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,13 +5,7 @@
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate($content_width / 2 + $gap, 0);
|
transform: translate($content_width / 2 + $gap, 0);
|
||||||
z-index: 4;
|
z-index: 14;
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.panel {
|
|
||||||
transform: translate(0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: $content_width + ($bar_height + $gap) * 2) {
|
@media (max-width: $content_width + ($bar_height + $gap) * 2) {
|
||||||
left: 100%;
|
left: 100%;
|
||||||
|
@ -31,6 +25,8 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
@ -50,16 +46,25 @@
|
||||||
padding-bottom: $bar_height;
|
padding-bottom: $bar_height;
|
||||||
border-radius: $radius $radius 0 0;
|
border-radius: $radius $radius 0 0;
|
||||||
transform: translate(0, 100%);
|
transform: translate(0, 100%);
|
||||||
transition: transform 250ms;
|
transition: transform 250ms 250ms;
|
||||||
|
|
||||||
div {
|
&.active {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
transition: transform 250ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.link {
|
||||||
@include outer_shadow;
|
@include outer_shadow;
|
||||||
|
|
||||||
height: $bar_height;
|
height: $bar_height;
|
||||||
width: $bar_height;
|
width: $bar_height;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
fill: white;
|
||||||
|
color: white;
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
|
@ -69,5 +74,4 @@
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-radius: $radius $radius 0 0;
|
border-radius: $radius $radius 0 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
19
src/components/dialogs/ModalWrapper/index.tsx
Normal file
19
src/components/dialogs/ModalWrapper/index.tsx
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import React, { FC, MouseEventHandler } from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
|
onOverlayClick: MouseEventHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModalWrapper: FC<IProps> = ({ children, onOverlayClick }) => {
|
||||||
|
return ReactDOM.createPortal(
|
||||||
|
<div className={styles.fixed}>
|
||||||
|
<div className={styles.overlay} onClick={onOverlayClick} />
|
||||||
|
<div className={styles.content}>{children}</div>
|
||||||
|
</div>,
|
||||||
|
document.body
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ModalWrapper };
|
|
@ -5,6 +5,8 @@ import { INode } from '~/redux/types';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||||
import { getPrettyDate } from '~/utils/dom';
|
import { getPrettyDate } from '~/utils/dom';
|
||||||
|
import { URLS } from '~/constants/urls';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
node: Partial<INode>;
|
node: Partial<INode>;
|
||||||
|
@ -24,7 +26,7 @@ interface IProps {
|
||||||
|
|
||||||
const NodePanelInner: FC<IProps> = memo(
|
const NodePanelInner: FC<IProps> = memo(
|
||||||
({
|
({
|
||||||
node: { title, user, is_liked, is_heroic, deleted_at, created_at, like_count },
|
node: { id, title, user, is_liked, is_heroic, deleted_at, created_at, like_count },
|
||||||
stack,
|
stack,
|
||||||
|
|
||||||
canStar,
|
canStar,
|
||||||
|
@ -78,9 +80,9 @@ const NodePanelInner: FC<IProps> = memo(
|
||||||
<Icon icon={deleted_at ? 'locked' : 'unlocked'} size={24} onClick={onLock} />
|
<Icon icon={deleted_at ? 'locked' : 'unlocked'} size={24} onClick={onLock} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<Link to={URLS.NODE_EDIT_URL(id)}>
|
||||||
<Icon icon="edit" size={24} onClick={onEdit} />
|
<Icon icon="edit" size={24} onClick={onEdit} />
|
||||||
</div>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,8 +1,4 @@
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
import { NODE_TYPES } from '~/redux/node/constants';
|
||||||
import { EditorDialogImage } from '~/containers/editors/EditorDialogImage';
|
|
||||||
import { EditorDialogText } from '~/containers/editors/EditorDialogText';
|
|
||||||
import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo';
|
|
||||||
import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio';
|
|
||||||
import { LoginDialog } from '~/containers/dialogs/LoginDialog';
|
import { LoginDialog } from '~/containers/dialogs/LoginDialog';
|
||||||
import { LoadingDialog } from '~/containers/dialogs/LoadingDialog';
|
import { LoadingDialog } from '~/containers/dialogs/LoadingDialog';
|
||||||
import { TestDialog } from '~/containers/dialogs/TestDialog';
|
import { TestDialog } from '~/containers/dialogs/TestDialog';
|
||||||
|
@ -16,10 +12,6 @@ import { IDialogProps } from '~/redux/types';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
|
|
||||||
export const DIALOG_CONTENT: Record<string, FC<IDialogProps>> = {
|
export const DIALOG_CONTENT: Record<string, FC<IDialogProps>> = {
|
||||||
[DIALOGS.EDITOR_IMAGE]: EditorDialogImage,
|
|
||||||
[DIALOGS.EDITOR_TEXT]: EditorDialogText,
|
|
||||||
[DIALOGS.EDITOR_VIDEO]: EditorDialogVideo,
|
|
||||||
[DIALOGS.EDITOR_AUDIO]: EditorDialogAudio,
|
|
||||||
[DIALOGS.LOGIN]: LoginDialog,
|
[DIALOGS.LOGIN]: LoginDialog,
|
||||||
[DIALOGS.LOGIN_SOCIAL_REGISTER]: LoginSocialRegisterDialog,
|
[DIALOGS.LOGIN_SOCIAL_REGISTER]: LoginSocialRegisterDialog,
|
||||||
[DIALOGS.LOADING]: LoadingDialog,
|
[DIALOGS.LOADING]: LoadingDialog,
|
||||||
|
|
|
@ -16,6 +16,8 @@ export const URLS = {
|
||||||
BACKEND_DOWN: '/oopsie',
|
BACKEND_DOWN: '/oopsie',
|
||||||
},
|
},
|
||||||
NODE_URL: (id: INode['id'] | string) => `/post${id}`,
|
NODE_URL: (id: INode['id'] | string) => `/post${id}`,
|
||||||
|
NODE_EDIT_URL: (id: INode['id'] | string) => `/post${id}/edit`,
|
||||||
|
NODE_CREATE_URL: (type: string) => `/`,
|
||||||
NODE_TAG_URL: (id: number, tagName: string) => `/post${id}/tag/${tagName}`,
|
NODE_TAG_URL: (id: number, tagName: string) => `/post${id}/tag/${tagName}`,
|
||||||
PROFILE: (username: string) => `/~${username}`,
|
PROFILE: (username: string) => `/~${username}`,
|
||||||
PROFILE_PAGE: (username: string) => `/profile/${username}`,
|
PROFILE_PAGE: (username: string) => `/profile/${username}`,
|
||||||
|
|
38
src/containers/dialogs/EditorCreateDialog/index.tsx
Normal file
38
src/containers/dialogs/EditorCreateDialog/index.tsx
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import React, { FC, useCallback, useMemo, useRef } from 'react';
|
||||||
|
import { EMPTY_NODE, NODE_TYPES } from '~/redux/node/constants';
|
||||||
|
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||||
|
import { useHistory, useRouteMatch } from 'react-router';
|
||||||
|
import { values } from 'ramda';
|
||||||
|
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||||
|
|
||||||
|
const EditorCreateDialog: FC = () => {
|
||||||
|
const history = useHistory();
|
||||||
|
const {
|
||||||
|
params: { type },
|
||||||
|
url,
|
||||||
|
} = useRouteMatch<{ type: string }>();
|
||||||
|
|
||||||
|
const backUrl = useMemo(() => {
|
||||||
|
return url.replace(/\/create\/(.*)$/, '');
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
const goBack = useCallback(() => {
|
||||||
|
history.replace(backUrl);
|
||||||
|
}, [backUrl, history]);
|
||||||
|
|
||||||
|
const isExist = useMemo(() => values(NODE_TYPES).some(el => el === type), [type]);
|
||||||
|
|
||||||
|
const data = useRef({ ...EMPTY_NODE, type });
|
||||||
|
|
||||||
|
if (!type || !isExist) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper onOverlayClick={goBack}>
|
||||||
|
<EditorDialog node={data.current} onRequestClose={goBack} />
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { EditorCreateDialog };
|
|
@ -11,22 +11,20 @@ import { EditorButtons } from '~/components/editors/EditorButtons';
|
||||||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader';
|
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader';
|
||||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants';
|
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants';
|
||||||
import { FormikProvider } from 'formik';
|
import { FormikProvider } from 'formik';
|
||||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
import { INode } from '~/redux/types';
|
||||||
import { selectNodeEditor } from '~/redux/node/selectors';
|
|
||||||
|
|
||||||
interface Props extends IDialogProps {
|
interface Props extends IDialogProps {
|
||||||
type: string;
|
node: INode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const EditorDialog: FC<Props> = ({ type, onRequestClose }) => {
|
const EditorDialog: FC<Props> = ({ node, onRequestClose }) => {
|
||||||
const editor = useShallowSelect(selectNodeEditor);
|
|
||||||
const uploader = useFileUploader(UPLOAD_SUBJECTS.EDITOR, UPLOAD_TARGETS.NODES, []);
|
const uploader = useFileUploader(UPLOAD_SUBJECTS.EDITOR, UPLOAD_TARGETS.NODES, []);
|
||||||
const formik = useNodeFormFormik({ ...editor, type }, uploader, onRequestClose);
|
const formik = useNodeFormFormik(node, uploader, onRequestClose);
|
||||||
const { values, handleSubmit } = formik;
|
const { values, handleSubmit } = formik;
|
||||||
|
|
||||||
useCloseOnEscape(onRequestClose);
|
useCloseOnEscape(onRequestClose);
|
||||||
|
|
||||||
const component = useMemo(() => prop(type, NODE_EDITORS), [type]);
|
const component = useMemo(() => node.type && prop(node.type, NODE_EDITORS), [node.type]);
|
||||||
|
|
||||||
if (!component) {
|
if (!component) {
|
||||||
return null;
|
return null;
|
||||||
|
|
57
src/containers/dialogs/EditorEditDialog/index.tsx
Normal file
57
src/containers/dialogs/EditorEditDialog/index.tsx
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
import { EMPTY_NODE } from '~/redux/node/constants';
|
||||||
|
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||||
|
import { useHistory, useRouteMatch } from 'react-router';
|
||||||
|
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||||
|
import { apiGetNodeWithCancel } from '~/redux/node/api';
|
||||||
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
|
import styles from './styles.module.scss';
|
||||||
|
|
||||||
|
const EditorEditDialog: FC = () => {
|
||||||
|
const [data, setData] = useState(EMPTY_NODE);
|
||||||
|
const [isLoading, setLoading] = useState(false);
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const {
|
||||||
|
params: { id },
|
||||||
|
url,
|
||||||
|
} = useRouteMatch<{ id: string }>();
|
||||||
|
|
||||||
|
const backUrl = useMemo(() => {
|
||||||
|
return url.replace(/\/edit$/, '');
|
||||||
|
}, [url]);
|
||||||
|
|
||||||
|
const goBack = useCallback(() => {
|
||||||
|
history.replace(backUrl);
|
||||||
|
}, [backUrl, history]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { request, cancel } = apiGetNodeWithCancel({ id });
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
request
|
||||||
|
.then(data => setData(data.node))
|
||||||
|
.then(() => setLoading(false))
|
||||||
|
.catch(console.log);
|
||||||
|
|
||||||
|
return () => cancel();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalWrapper onOverlayClick={console.log}>
|
||||||
|
{isLoading ? (
|
||||||
|
<div className={styles.loader}>
|
||||||
|
<LoaderCircle size={64} />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<EditorDialog node={data} onRequestClose={goBack} />
|
||||||
|
)}
|
||||||
|
</ModalWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { EditorEditDialog };
|
|
@ -0,0 +1,4 @@
|
||||||
|
.loader {
|
||||||
|
fill: white;
|
||||||
|
color: white;
|
||||||
|
}
|
|
@ -1,57 +1,34 @@
|
||||||
import React, { Attributes, FC, useCallback } from 'react';
|
import React, { FC, useCallback } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import ReactDOM from 'react-dom';
|
|
||||||
import styles from './styles.module.scss';
|
|
||||||
import { IState } from '~/redux/store';
|
|
||||||
import * as ACTIONS from '~/redux/modal/actions';
|
|
||||||
import { DIALOG_CONTENT } from '~/constants/dialogs';
|
import { DIALOG_CONTENT } from '~/constants/dialogs';
|
||||||
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
|
import { selectModal } from '~/redux/modal/selectors';
|
||||||
|
import { modalSetDialog, modalSetShown, modalShowDialog } from '~/redux/modal/actions';
|
||||||
|
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||||
|
|
||||||
const mapStateToProps = ({ modal }: IState) => ({ ...modal });
|
type IProps = {};
|
||||||
const mapDispatchToProps = {
|
|
||||||
modalSetShown: ACTIONS.modalSetShown,
|
|
||||||
modalSetDialog: ACTIONS.modalSetDialog,
|
|
||||||
modalShowDialog: ACTIONS.modalShowDialog,
|
|
||||||
};
|
|
||||||
|
|
||||||
type IProps = typeof mapDispatchToProps & ReturnType<typeof mapStateToProps> & {};
|
const Modal: FC<IProps> = ({}) => {
|
||||||
|
const { is_shown, dialog } = useShallowSelect(selectModal);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
const ModalUnconnected: FC<IProps> = ({
|
|
||||||
modalSetShown,
|
|
||||||
modalSetDialog,
|
|
||||||
modalShowDialog,
|
|
||||||
is_shown,
|
|
||||||
dialog,
|
|
||||||
}) => {
|
|
||||||
const onRequestClose = useCallback(() => {
|
const onRequestClose = useCallback(() => {
|
||||||
modalSetShown(false);
|
dispatch(modalSetShown(false));
|
||||||
modalSetDialog('');
|
dispatch(modalSetDialog(''));
|
||||||
}, [modalSetShown, modalSetDialog]);
|
}, [dispatch]);
|
||||||
|
|
||||||
|
const onDialogChange = useCallback((val: string) => dispatch(modalShowDialog(val)), [dispatch]);
|
||||||
|
|
||||||
if (!dialog || !DIALOG_CONTENT[dialog] || !is_shown) return null;
|
if (!dialog || !DIALOG_CONTENT[dialog] || !is_shown) return null;
|
||||||
|
|
||||||
return ReactDOM.createPortal(
|
return (
|
||||||
<div className={styles.fixed}>
|
<ModalWrapper onOverlayClick={onRequestClose}>
|
||||||
<div className={styles.overlay} onClick={onRequestClose} />
|
|
||||||
<div className={styles.content}>
|
|
||||||
{React.createElement(DIALOG_CONTENT[dialog], {
|
{React.createElement(DIALOG_CONTENT[dialog], {
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
onDialogChange: modalShowDialog,
|
onDialogChange,
|
||||||
})}
|
})}
|
||||||
</div>
|
</ModalWrapper>
|
||||||
</div>,
|
|
||||||
document.body
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Modal = connect(mapStateToProps, mapDispatchToProps)(ModalUnconnected);
|
export { Modal };
|
||||||
|
|
||||||
export { ModalUnconnected, Modal };
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
<div className={styles.content_scroller}>
|
|
||||||
<div className={styles.content_padder}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
|
||||||
import { IDialogProps } from '~/redux/types';
|
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
|
||||||
|
|
||||||
type IProps = IDialogProps & {};
|
|
||||||
|
|
||||||
const EditorDialogAudio: FC<IProps> = props => <EditorDialog type={NODE_TYPES.AUDIO} {...props} />;
|
|
||||||
|
|
||||||
export { EditorDialogAudio };
|
|
|
@ -1,10 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
|
||||||
import { IDialogProps } from '~/redux/types';
|
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
|
||||||
|
|
||||||
type IProps = IDialogProps & {};
|
|
||||||
|
|
||||||
const EditorDialogImage: FC<IProps> = props => <EditorDialog type={NODE_TYPES.IMAGE} {...props} />;
|
|
||||||
|
|
||||||
export { EditorDialogImage };
|
|
|
@ -1,10 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
|
||||||
import { IDialogProps } from '~/redux/types';
|
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
|
||||||
|
|
||||||
type IProps = IDialogProps & {};
|
|
||||||
|
|
||||||
const EditorDialogText: FC<IProps> = props => <EditorDialog type={NODE_TYPES.TEXT} {...props} />;
|
|
||||||
|
|
||||||
export { EditorDialogText };
|
|
|
@ -1,10 +0,0 @@
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
|
||||||
import { IDialogProps } from '~/redux/types';
|
|
||||||
import { NODE_TYPES } from '~/redux/node/constants';
|
|
||||||
|
|
||||||
type IProps = IDialogProps & {};
|
|
||||||
|
|
||||||
const EditorDialogVideo: FC<IProps> = props => <EditorDialog type={NODE_TYPES.VIDEO} {...props} />;
|
|
||||||
|
|
||||||
export { EditorDialogVideo };
|
|
|
@ -5,6 +5,7 @@ import { TagSidebar } from '~/containers/sidebars/TagSidebar';
|
||||||
import { ProfileSidebar } from '~/containers/sidebars/ProfileSidebar';
|
import { ProfileSidebar } from '~/containers/sidebars/ProfileSidebar';
|
||||||
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;
|
||||||
|
@ -15,6 +16,7 @@ const SidebarRouter: FC<IProps> = ({ prefix = '', isLab }) => {
|
||||||
return createPortal(
|
return createPortal(
|
||||||
<>
|
<>
|
||||||
<Switch>
|
<Switch>
|
||||||
|
<Route path={`${prefix}/create/:type`} component={EditorCreateDialog} />
|
||||||
<Route path={`${prefix}/tag/:tag`} component={TagSidebar} />
|
<Route path={`${prefix}/tag/:tag`} component={TagSidebar} />
|
||||||
<Route path={`${prefix}/~:username`} component={ProfileSidebar} />
|
<Route path={`${prefix}/~:username`} component={ProfileSidebar} />
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React, { FC, memo } from 'react';
|
import React, { FC, memo } from 'react';
|
||||||
import { RouteComponentProps } from 'react-router';
|
import { Route, RouteComponentProps } from 'react-router';
|
||||||
import { selectNode } from '~/redux/node/selectors';
|
import { selectNode } from '~/redux/node/selectors';
|
||||||
import { Card } from '~/components/containers/Card';
|
import { Card } from '~/components/containers/Card';
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ import { NodeBottomBlock } from '~/components/node/NodeBottomBlock';
|
||||||
import { useNodeCoverImage } from '~/utils/hooks/node/useNodeCoverImage';
|
import { useNodeCoverImage } from '~/utils/hooks/node/useNodeCoverImage';
|
||||||
import { useScrollToTop } from '~/utils/hooks/useScrollToTop';
|
import { useScrollToTop } from '~/utils/hooks/useScrollToTop';
|
||||||
import { useLoadNode } from '~/utils/hooks/node/useLoadNode';
|
import { useLoadNode } from '~/utils/hooks/node/useLoadNode';
|
||||||
|
import { URLS } from '~/constants/urls';
|
||||||
|
import { EditorEditDialog } from '~/containers/dialogs/EditorEditDialog';
|
||||||
|
|
||||||
type IProps = RouteComponentProps<{ id: string }> & {};
|
type IProps = RouteComponentProps<{ id: string }> & {};
|
||||||
|
|
||||||
|
@ -64,6 +66,8 @@ const NodeLayout: FC<IProps> = memo(
|
||||||
</Container>
|
</Container>
|
||||||
|
|
||||||
<SidebarRouter prefix="/post:id" />
|
<SidebarRouter prefix="/post:id" />
|
||||||
|
|
||||||
|
<Route path={URLS.NODE_EDIT_URL(':id')} component={EditorEditDialog} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,5 +27,5 @@ export const MODAL_ACTIONS = {
|
||||||
|
|
||||||
export interface IDialogProps {
|
export interface IDialogProps {
|
||||||
onRequestClose: () => void;
|
onRequestClose: () => void;
|
||||||
onDialogChange: (dialog: ValueOf<typeof DIALOGS>) => void;
|
onDialogChange?: (dialog: ValueOf<typeof DIALOGS>) => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { api, cleanResult, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api';
|
import { api, cleanResult } from '~/utils/api';
|
||||||
import { IComment, INode, IResultWithStatus } from '../types';
|
import { IComment, INode } from '../types';
|
||||||
import { API } from '~/constants/api';
|
import { API } from '~/constants/api';
|
||||||
import { COMMENTS_DISPLAY } from './constants';
|
import { COMMENTS_DISPLAY } from './constants';
|
||||||
import {
|
import {
|
||||||
|
@ -22,6 +22,7 @@ import {
|
||||||
GetNodeDiffRequest,
|
GetNodeDiffRequest,
|
||||||
GetNodeDiffResult,
|
GetNodeDiffResult,
|
||||||
} from '~/redux/node/types';
|
} from '~/redux/node/types';
|
||||||
|
import axios, { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
export type ApiPostNodeRequest = { node: INode };
|
export type ApiPostNodeRequest = { node: INode };
|
||||||
export type ApiPostNodeResult = {
|
export type ApiPostNodeResult = {
|
||||||
|
@ -62,8 +63,20 @@ export const getNodeDiff = ({
|
||||||
})
|
})
|
||||||
.then(cleanResult);
|
.then(cleanResult);
|
||||||
|
|
||||||
export const apiGetNode = ({ id }: ApiGetNodeRequest) =>
|
export const apiGetNode = ({ id }: ApiGetNodeRequest, config?: AxiosRequestConfig) =>
|
||||||
api.get<ApiGetNodeResult>(API.NODE.GET_NODE(id)).then(cleanResult);
|
api.get<ApiGetNodeResult>(API.NODE.GET_NODE(id), config).then(cleanResult);
|
||||||
|
|
||||||
|
export const apiGetNodeWithCancel = ({ id }: ApiGetNodeRequest) => {
|
||||||
|
const cancelToken = axios.CancelToken.source();
|
||||||
|
return {
|
||||||
|
request: api
|
||||||
|
.get<ApiGetNodeResult>(API.NODE.GET_NODE(id), {
|
||||||
|
cancelToken: cancelToken.token,
|
||||||
|
})
|
||||||
|
.then(cleanResult),
|
||||||
|
cancel: cancelToken.cancel,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const apiPostComment = ({ id, data }: ApiPostCommentRequest) =>
|
export const apiPostComment = ({ id, data }: ApiPostCommentRequest) =>
|
||||||
api.post<ApiPostCommentResult>(API.NODE.COMMENT(id), data).then(cleanResult);
|
api.post<ApiPostCommentResult>(API.NODE.COMMENT(id), data).then(cleanResult);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue