From 8f0625f73403eda73fee37e25ed0950c56ccf7c1 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 29 Nov 2019 10:40:43 +0700 Subject: [PATCH] node deletion (lock) --- .../node/NodeImageSlideBlock/index.tsx | 2 +- src/components/node/NodePanel/index.tsx | 14 +++-- src/components/node/NodePanelInner/index.tsx | 46 ++++++++------- src/constants/api.ts | 1 + src/containers/node/NodeLayout/index.tsx | 6 +- src/index.tsx | 5 +- src/redux/node/actions.ts | 6 ++ src/redux/node/api.ts | 59 +++++++++++-------- src/redux/node/constants.ts | 1 + src/redux/node/sagas.ts | 20 +++++++ src/redux/types.ts | 1 + src/sprites/Sprites.tsx | 10 ++++ 12 files changed, 113 insertions(+), 58 deletions(-) diff --git a/src/components/node/NodeImageSlideBlock/index.tsx b/src/components/node/NodeImageSlideBlock/index.tsx index f3b4705a..a971b181 100644 --- a/src/components/node/NodeImageSlideBlock/index.tsx +++ b/src/components/node/NodeImageSlideBlock/index.tsx @@ -47,7 +47,7 @@ const NodeImageSlideBlock: FC = ({ node, is_loading, updateLayout }) => const images = useMemo( () => (node && node.files && node.files.filter(({ type }) => type === UPLOAD_TYPES.IMAGE)) || [], - [node] + [node.files] ); useEffect(() => { diff --git a/src/components/node/NodePanel/index.tsx b/src/components/node/NodePanel/index.tsx index 9a828841..22cf50e5 100644 --- a/src/components/node/NodePanel/index.tsx +++ b/src/components/node/NodePanel/index.tsx @@ -3,7 +3,6 @@ import * as styles from './styles.scss'; import { INode } from '~/redux/types'; import { createPortal } from 'react-dom'; import { NodePanelInner } from '~/components/node/NodePanelInner'; -import pick from 'ramda/es/pick'; interface IProps { node: Partial; @@ -18,10 +17,11 @@ interface IProps { onEdit: () => void; onLike: () => void; onStar: () => void; + onLock: () => void; } const NodePanel: FC = memo( - ({ node, layout, can_edit, can_like, can_star, is_loading, onEdit, onLike, onStar }) => { + ({ node, layout, can_edit, can_like, can_star, is_loading, onEdit, onLike, onStar, onLock }) => { const [stack, setStack] = useState(false); const ref = useRef(null); @@ -51,14 +51,15 @@ const NodePanel: FC = memo( createPortal( , document.body ) @@ -68,6 +69,7 @@ const NodePanel: FC = memo( onEdit={onEdit} onLike={onLike} onStar={onStar} + onLock={onLock} can_edit={can_edit} can_like={can_like} can_star={can_star} diff --git a/src/components/node/NodePanelInner/index.tsx b/src/components/node/NodePanelInner/index.tsx index 8d112757..1ae279ce 100644 --- a/src/components/node/NodePanelInner/index.tsx +++ b/src/components/node/NodePanelInner/index.tsx @@ -1,11 +1,11 @@ -import React, { FC } from "react"; -import * as styles from "./styles.scss"; -import { Group } from "~/components/containers/Group"; -import { Filler } from "~/components/containers/Filler"; -import { Icon } from "~/components/input/Icon"; -import { INode } from "~/redux/types"; -import classNames from "classnames"; -import { Placeholder } from "~/components/placeholders/Placeholder"; +import React, { FC } from 'react'; +import * as styles from './styles.scss'; +import { Group } from '~/components/containers/Group'; +import { Filler } from '~/components/containers/Filler'; +import { Icon } from '~/components/input/Icon'; +import { INode } from '~/redux/types'; +import classNames from 'classnames'; +import { Placeholder } from '~/components/placeholders/Placeholder'; interface IProps { node: Partial; @@ -20,10 +20,11 @@ interface IProps { onEdit: () => void; onLike: () => void; onStar: () => void; + onLock: () => void; } const NodePanelInner: FC = ({ - node: { title, user, is_liked, is_heroic }, + node: { title, user, is_liked, is_heroic, deleted_at }, stack, can_star, @@ -34,7 +35,8 @@ const NodePanelInner: FC = ({ onStar, onEdit, - onLike + onLike, + onLock, }) => { return (
@@ -42,15 +44,11 @@ const NodePanelInner: FC = ({
- {is_loading ? : title || "..."} + {is_loading ? : title || '...'}
{user && user.username && (
- {is_loading ? ( - - ) : ( - `~${user.username}` - )} + {is_loading ? : `~${user.username}`}
)}
@@ -66,11 +64,19 @@ const NodePanelInner: FC = ({ )}
)} + {can_edit && ( -
- -
+ <> +
+ +
+ +
+ +
+ )} + {can_like && (
{is_liked ? ( @@ -87,5 +93,3 @@ const NodePanelInner: FC = ({ }; export { NodePanelInner }; - -//
diff --git a/src/constants/api.ts b/src/constants/api.ts index 2fdc7c95..b0691569 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -25,6 +25,7 @@ export const API = { UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`, POST_LIKE: (id: INode['id']) => `/node/${id}/like`, POST_STAR: (id: INode['id']) => `/node/${id}/heroic`, + POST_LOCK: (id: INode['id']) => `/node/${id}/lock`, SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`, }, }; diff --git a/src/containers/node/NodeLayout/index.tsx b/src/containers/node/NodeLayout/index.tsx index ca16655e..2d000fcc 100644 --- a/src/containers/node/NodeLayout/index.tsx +++ b/src/containers/node/NodeLayout/index.tsx @@ -32,6 +32,7 @@ const mapDispatchToProps = { nodeEdit: NODE_ACTIONS.nodeEdit, nodeLike: NODE_ACTIONS.nodeLike, nodeStar: NODE_ACTIONS.nodeStar, + nodeLock: NODE_ACTIONS.nodeLock, }; type IProps = ReturnType & @@ -51,6 +52,7 @@ const NodeLayoutUnconnected: FC = memo( nodeEdit, nodeLike, nodeStar, + nodeLock, nodeSetCoverImage, }) => { // const is_loading = true; @@ -81,6 +83,7 @@ const NodeLayoutUnconnected: FC = memo( const onEdit = useCallback(() => nodeEdit(node.id), [nodeEdit, node]); const onLike = useCallback(() => nodeLike(node.id), [nodeLike, node]); const onStar = useCallback(() => nodeStar(node.id), [nodeStar, node]); + const onLock = useCallback(() => nodeLock(node.id, !node.deleted_at), [nodeStar, node]); useEffect(() => { if (!node.cover) return; @@ -93,7 +96,7 @@ const NodeLayoutUnconnected: FC = memo( {block && createElement(block, { node, is_loading, updateLayout, layout })} = memo( onEdit={onEdit} onLike={onLike} onStar={onStar} + onLock={onLock} is_loading={is_loading} /> diff --git a/src/index.tsx b/src/index.tsx index 35799244..68237153 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -19,7 +19,6 @@ render( ); /* - [Stage 0]: - set file target on comment save, node save, profile upload - check if email is registered at social login @@ -28,10 +27,10 @@ render( - delete nodes - illustrate 404 - - illustrate restoreRequestDialog - - illustrate login [stage 1] + - illustrate login + - illustrate restoreRequestDialog - friendship - signup? - import videos diff --git a/src/redux/node/actions.ts b/src/redux/node/actions.ts index 4262b48a..1cff016f 100644 --- a/src/redux/node/actions.ts +++ b/src/redux/node/actions.ts @@ -96,6 +96,12 @@ export const nodeStar = (id: INode['id']) => ({ id, }); +export const nodeLock = (id: INode['id'], is_locked: boolean) => ({ + type: NODE_ACTIONS.LOCK, + id, + is_locked, +}); + export const nodeSetEditor = (editor: INode) => ({ type: NODE_ACTIONS.SET_EDITOR, editor, diff --git a/src/redux/node/api.ts b/src/redux/node/api.ts index 5a79b79b..5a0f21e7 100644 --- a/src/redux/node/api.ts +++ b/src/redux/node/api.ts @@ -1,17 +1,12 @@ -import { - api, - configWithToken, - resultMiddleware, - errorMiddleware -} from "~/utils/api"; -import { INode, IResultWithStatus, IComment } from "../types"; -import { API } from "~/constants/api"; -import { nodeUpdateTags, nodeLike, nodeStar } from "./actions"; -import { INodeState } from "./reducer"; +import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api'; +import { INode, IResultWithStatus, IComment } from '../types'; +import { API } from '~/constants/api'; +import { nodeUpdateTags, nodeLike, nodeStar, nodeLock } from './actions'; +import { INodeState } from './reducer'; export const postNode = ({ access, - node + node, }: { access: string; node: INode; @@ -24,7 +19,7 @@ export const postNode = ({ export const getNodes = ({ from = null, - access + access, }: { from?: string; access: string; @@ -42,7 +37,7 @@ export const getNodeDiff = ({ with_updated, with_recent, with_valid, - access + access, }: { start?: string; end?: string; @@ -64,8 +59,8 @@ export const getNodeDiff = ({ with_heroes, with_updated, with_recent, - with_valid - } + with_valid, + }, }) ) .then(resultMiddleware) @@ -73,7 +68,7 @@ export const getNodeDiff = ({ export const getNode = ({ id, - access + access, }: { id: string | number; access: string; @@ -86,7 +81,7 @@ export const getNode = ({ export const postNodeComment = ({ id, data, - access + access, }: { access: string; id: number; @@ -100,11 +95,11 @@ export const postNodeComment = ({ export const getNodeComments = ({ id, access, - order = "ASC" + order = 'ASC', }: { id: number; access: string; - order: "ASC" | "DESC"; + order: 'ASC' | 'DESC'; }): Promise> => api .get(API.NODE.COMMENT(id), configWithToken(access, { params: { order } })) @@ -113,11 +108,11 @@ export const getNodeComments = ({ export const getNodeRelated = ({ id, - access + access, }: { id: number; access: string; -}): Promise> => +}): Promise> => api .get(API.NODE.RELATED(id), configWithToken(access)) .then(resultMiddleware) @@ -126,7 +121,7 @@ export const getNodeRelated = ({ export const updateNodeTags = ({ id, tags, - access + access, }: ReturnType & { access: string }): Promise< IResultWithStatus<{ node: INode }> > => @@ -137,9 +132,9 @@ export const updateNodeTags = ({ export const postNodeLike = ({ id, - access + access, }: ReturnType & { access: string }): Promise< - IResultWithStatus<{ is_liked: INode["is_liked"] }> + IResultWithStatus<{ is_liked: INode['is_liked'] }> > => api .post(API.NODE.POST_LIKE(id), {}, configWithToken(access)) @@ -148,11 +143,23 @@ export const postNodeLike = ({ export const postNodeStar = ({ id, - access + access, }: ReturnType & { access: string }): Promise< - IResultWithStatus<{ is_liked: INode["is_liked"] }> + IResultWithStatus<{ is_liked: INode['is_liked'] }> > => api .post(API.NODE.POST_STAR(id), {}, configWithToken(access)) .then(resultMiddleware) .catch(errorMiddleware); + +export const postNodeLock = ({ + id, + is_locked, + access, +}: ReturnType & { access: string }): Promise< + IResultWithStatus<{ deleted_at: INode['deleted_at'] }> +> => + api + .post(API.NODE.POST_LOCK(id), { is_locked }, configWithToken(access)) + .then(resultMiddleware) + .catch(errorMiddleware); diff --git a/src/redux/node/constants.ts b/src/redux/node/constants.ts index db8e5afc..6e858db0 100644 --- a/src/redux/node/constants.ts +++ b/src/redux/node/constants.ts @@ -23,6 +23,7 @@ export const NODE_ACTIONS = { EDIT: `${prefix}EDIT`, LIKE: `${prefix}LIKE`, STAR: `${prefix}STAR`, + LOCK: `${prefix}LOCK`, CREATE: `${prefix}CREATE`, SET_SAVE_ERRORS: `${prefix}SET_SAVE_ERRORS`, diff --git a/src/redux/node/sagas.ts b/src/redux/node/sagas.ts index bd6e3a61..1cfc8fdf 100644 --- a/src/redux/node/sagas.ts +++ b/src/redux/node/sagas.ts @@ -21,6 +21,7 @@ import { nodeLike, nodeSetRelated, nodeGotoNode, + nodeLock, } from './actions'; import { postNode, @@ -31,6 +32,7 @@ import { postNodeLike, postNodeStar, getNodeRelated, + postNodeLock, } from './api'; import { reqWrapper } from '../auth/sagas'; import { flowSetNodes, flowSetUpdated } from '../flow/actions'; @@ -234,6 +236,23 @@ function* onStarSaga({ id }: ReturnType) { yield call(updateNodeEverywhere, { ...current, is_heroic }); } +function* onLockSaga({ id, is_locked }: ReturnType) { + const { + current, + current: { deleted_at }, + } = yield select(selectNode); + + yield call(updateNodeEverywhere, { + ...current, + deleted_at: is_locked ? new Date().toISOString() : null, + }); + + const { data, error } = yield call(reqWrapper, postNodeLock, { id, is_locked }); + + if (error || !data.deleted_at) + return yield call(updateNodeEverywhere, { ...current, deleted_at }); // ok and matches +} + export default function* nodeSaga() { yield takeLatest(NODE_ACTIONS.SAVE, onNodeSave); yield takeLatest(NODE_ACTIONS.GOTO_NODE, onNodeGoto); @@ -244,4 +263,5 @@ export default function* nodeSaga() { yield takeLatest(NODE_ACTIONS.EDIT, onEditSaga); yield takeLatest(NODE_ACTIONS.LIKE, onLikeSaga); yield takeLatest(NODE_ACTIONS.STAR, onStarSaga); + yield takeLatest(NODE_ACTIONS.LOCK, onLockSaga); } diff --git a/src/redux/types.ts b/src/redux/types.ts index 4e21edc9..894a12f2 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -132,6 +132,7 @@ export interface INode { created_at?: string; updated_at?: string; + deleted_at?: string; commented_at?: string; } diff --git a/src/sprites/Sprites.tsx b/src/sprites/Sprites.tsx index 97fe173b..3ebcd592 100644 --- a/src/sprites/Sprites.tsx +++ b/src/sprites/Sprites.tsx @@ -192,6 +192,16 @@ const Sprites: FC<{}> = () => ( + + + + + + + + + +