mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
node edit and like buttons + actions
This commit is contained in:
parent
f4c337b255
commit
249a9b368c
7 changed files with 117 additions and 28 deletions
|
@ -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<IProps> = ({ node, layout }) => {
|
||||
const NodePanel: FC<IProps> = ({ node, layout, can_edit, can_like, onEdit, onLike }) => {
|
||||
const [stack, setStack] = useState(false);
|
||||
|
||||
const ref = useRef(null);
|
||||
|
@ -37,9 +42,25 @@ const NodePanel: FC<IProps> = ({ node, layout }) => {
|
|||
return (
|
||||
<div className={styles.place} ref={ref}>
|
||||
{stack ? (
|
||||
createPortal(<NodePanelInner node={node} stack />, document.body)
|
||||
createPortal(
|
||||
<NodePanelInner
|
||||
node={node}
|
||||
stack
|
||||
onEdit={onEdit}
|
||||
onLike={onLike}
|
||||
can_edit={can_edit}
|
||||
can_like={can_like}
|
||||
/>,
|
||||
document.body
|
||||
)
|
||||
) : (
|
||||
<NodePanelInner node={node} />
|
||||
<NodePanelInner
|
||||
node={node}
|
||||
onEdit={onEdit}
|
||||
onLike={onLike}
|
||||
can_edit={can_edit}
|
||||
can_like={can_like}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -9,9 +9,21 @@ import classNames from 'classnames';
|
|||
interface IProps {
|
||||
node: INode;
|
||||
stack?: boolean;
|
||||
|
||||
can_edit: boolean;
|
||||
can_like: boolean;
|
||||
onEdit: () => void;
|
||||
onLike: () => void;
|
||||
}
|
||||
|
||||
const NodePanelInner: FC<IProps> = ({ node: { title, user }, stack }) => {
|
||||
const NodePanelInner: FC<IProps> = ({
|
||||
node: { title, user },
|
||||
stack,
|
||||
can_edit,
|
||||
can_like,
|
||||
onEdit,
|
||||
onLike,
|
||||
}) => {
|
||||
return (
|
||||
<div className={classNames(styles.wrap, { stack })}>
|
||||
<div className={styles.content}>
|
||||
|
@ -23,11 +35,16 @@ const NodePanelInner: FC<IProps> = ({ node: { title, user }, stack }) => {
|
|||
</Group>
|
||||
|
||||
<div className={styles.buttons}>
|
||||
<Icon icon="edit" size={24} />
|
||||
|
||||
<div className={styles.sep} />
|
||||
|
||||
<Icon icon="heart" size={24} />
|
||||
{can_edit && (
|
||||
<div>
|
||||
<Icon icon="edit" size={24} onClick={onEdit} />
|
||||
</div>
|
||||
)}
|
||||
{can_like && (
|
||||
<div>
|
||||
<Icon icon="heart" size={24} onClick={onLike} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<typeof mapStateToProps> &
|
||||
|
@ -37,9 +39,12 @@ const NodeLayoutUnconnected: FC<IProps> = ({
|
|||
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<IProps> = ({
|
|||
[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 (
|
||||
<Card className={styles.node} seamless>
|
||||
{block && createElement(block, { node, is_loading, updateLayout, layout })}
|
||||
|
||||
<NodePanel node={node} layout={layout} />
|
||||
<NodePanel
|
||||
node={node}
|
||||
layout={layout}
|
||||
can_edit={can_edit}
|
||||
can_like={can_like}
|
||||
onEdit={onEdit}
|
||||
onLike={onLike}
|
||||
/>
|
||||
|
||||
<Group>
|
||||
<Padder>
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
||||
|
|
|
@ -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`,
|
||||
|
|
11
src/utils/node.ts
Normal file
11
src/utils/node.ts
Normal file
|
@ -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<INode>, user: Partial<IUser>): boolean =>
|
||||
path(['role'], user) === USER_ROLES.ADMIN ||
|
||||
(path(['user', 'id'], node) && path(['user', 'id'], node) === path(['id'], user));
|
||||
|
||||
export const canLikeNode = (node: Partial<INode>, user: Partial<IUser>): boolean =>
|
||||
path(['role'], user) && path(['role'], user) !== USER_ROLES.GUEST;
|
Loading…
Add table
Add a link
Reference in a new issue