diff --git a/src/components/node/NodePanel/index.tsx b/src/components/node/NodePanel/index.tsx index 9d93e8a9..e3494792 100644 --- a/src/components/node/NodePanel/index.tsx +++ b/src/components/node/NodePanel/index.tsx @@ -7,9 +7,14 @@ import { NodePanelInner } from '~/components/node/NodePanelInner'; interface IProps { node: INode; layout: {}; + + can_edit: boolean; + can_like: boolean; + onEdit: () => void; + onLike: () => void; } -const NodePanel: FC = ({ node, layout }) => { +const NodePanel: FC = ({ node, layout, can_edit, can_like, onEdit, onLike }) => { const [stack, setStack] = useState(false); const ref = useRef(null); @@ -37,9 +42,25 @@ const NodePanel: FC = ({ node, layout }) => { return (
{stack ? ( - createPortal(, document.body) + createPortal( + , + document.body + ) ) : ( - + )}
); diff --git a/src/components/node/NodePanelInner/index.tsx b/src/components/node/NodePanelInner/index.tsx index 49d56bcd..3364f0f8 100644 --- a/src/components/node/NodePanelInner/index.tsx +++ b/src/components/node/NodePanelInner/index.tsx @@ -9,25 +9,42 @@ import classNames from 'classnames'; interface IProps { node: INode; stack?: boolean; + + can_edit: boolean; + can_like: boolean; + onEdit: () => void; + onLike: () => void; } -const NodePanelInner: FC = ({ node: { title, user }, stack }) => { +const NodePanelInner: FC = ({ + node: { title, user }, + stack, + can_edit, + can_like, + onEdit, + onLike, +}) => { return (
{title || '...'}
- {user && user.username &&
~ {user.username}
} + {user && user.username &&
~{user.username}
}
- - -
- - + {can_edit && ( +
+ +
+ )} + {can_like && ( +
+ +
+ )}
diff --git a/src/components/node/NodePanelInner/styles.scss b/src/components/node/NodePanelInner/styles.scss index cdbbcfd0..133cb699 100644 --- a/src/components/node/NodePanelInner/styles.scss +++ b/src/components/node/NodePanelInner/styles.scss @@ -66,22 +66,43 @@ & > * { margin: 0 $gap; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + + svg { + fill: darken(white, 50%); + transition: fill 0.25s; + } + + &:hover { + svg { + fill: $red; + } + } + + &::after { + content: ' '; + flex: 0 0 6px; + height: $gap; + width: 6px; + border-radius: 4px; + background: transparentize(black, 0.7); + margin-left: $gap * 2; + } &:first-child { margin-left: 0; } + &:last-child { margin-right: 0; + &::after { + display: none; + } } } - //height: 54px; - //border-radius: $radius $radius 0 0; - //background: linear-gradient(176deg, #f42a00, #5c1085); - //position: absolute; - //bottom: 0; - //right: 10px; - //width: 270px; - //display: flex; } .mark { @@ -102,9 +123,4 @@ } .sep { - flex: 0 0 6px; - height: 6px; - width: 6px; - border-radius: 4px; - background: transparentize(black, 0.7); } diff --git a/src/containers/node/NodeLayout/index.tsx b/src/containers/node/NodeLayout/index.tsx index cde602c5..9aa29fb9 100644 --- a/src/containers/node/NodeLayout/index.tsx +++ b/src/containers/node/NodeLayout/index.tsx @@ -1,7 +1,7 @@ -import React, { FC, createElement, useEffect, useCallback, useState } from 'react'; +import React, { FC, createElement, useEffect, useCallback, useState, useMemo } from 'react'; import { RouteComponentProps } from 'react-router'; import { connect } from 'react-redux'; - +import { canEditNode, canLikeNode } from '~/utils/node'; import { selectNode } from '~/redux/node/selectors'; import { Card } from '~/components/containers/Card'; @@ -26,6 +26,8 @@ const mapStateToProps = state => ({ const mapDispatchToProps = { nodeLoadNode: NODE_ACTIONS.nodeLoadNode, nodeUpdateTags: NODE_ACTIONS.nodeUpdateTags, + nodeEdit: NODE_ACTIONS.nodeEdit, + nodeLike: NODE_ACTIONS.nodeLike, }; type IProps = ReturnType & @@ -37,9 +39,12 @@ const NodeLayoutUnconnected: FC = ({ params: { id }, }, node: { is_loading, is_loading_comments, comments = [], current: node }, - user: { is_user }, + user, + user: { is_user, role }, nodeLoadNode, nodeUpdateTags, + nodeEdit, + nodeLike, }) => { const [layout, setLayout] = useState({}); @@ -57,14 +62,27 @@ const NodeLayoutUnconnected: FC = ({ [node, nodeUpdateTags] ); + const can_edit = useMemo(() => canEditNode(node, user), [node, user]); + const can_like = useMemo(() => canLikeNode(node, user), [node, user]); + const block = node && node.type && NODE_COMPONENTS[node.type]; const inline_block = node && node.type && NODE_INLINES[node.type]; + const onEdit = useCallback(() => nodeEdit(node.id), [nodeEdit, node]); + const onLike = useCallback(() => nodeLike(node.id), [nodeLike, node]); + return ( {block && createElement(block, { node, is_loading, updateLayout, layout })} - + diff --git a/src/redux/node/actions.ts b/src/redux/node/actions.ts index e34e9fee..4d3dd3d7 100644 --- a/src/redux/node/actions.ts +++ b/src/redux/node/actions.ts @@ -71,7 +71,12 @@ export const nodeCreate = (node_type: INode['type']) => ({ }); export const nodeEdit = (id: INode['id']) => ({ - type: NODE_ACTIONS.CREATE, + type: NODE_ACTIONS.EDIT, + id, +}); + +export const nodeLike = (id: INode['id']) => ({ + type: NODE_ACTIONS.LIKE, id, }); diff --git a/src/redux/node/constants.ts b/src/redux/node/constants.ts index 6aee481c..c5900ebb 100644 --- a/src/redux/node/constants.ts +++ b/src/redux/node/constants.ts @@ -18,6 +18,7 @@ export const NODE_ACTIONS = { LOAD_NODE: `${prefix}LOAD_NODE`, EDIT: `${prefix}EDIT`, + LIKE: `${prefix}LIKE`, CREATE: `${prefix}CREATE`, SET_SAVE_ERRORS: `${prefix}SET_SAVE_ERRORS`, diff --git a/src/utils/node.ts b/src/utils/node.ts new file mode 100644 index 00000000..2b81e9e8 --- /dev/null +++ b/src/utils/node.ts @@ -0,0 +1,11 @@ +import { USER_ROLES } from '~/redux/auth/constants'; +import { INode } from '~/redux/types'; +import { IUser } from '~/redux/auth/types'; +import path from 'ramda/es/path'; + +export const canEditNode = (node: Partial, user: Partial): boolean => + path(['role'], user) === USER_ROLES.ADMIN || + (path(['user', 'id'], node) && path(['user', 'id'], node) === path(['id'], user)); + +export const canLikeNode = (node: Partial, user: Partial): boolean => + path(['role'], user) && path(['role'], user) !== USER_ROLES.GUEST;