mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
fixed pushing tags
This commit is contained in:
parent
6836e00de1
commit
99426172b2
12 changed files with 79 additions and 22 deletions
|
@ -2,12 +2,14 @@ import React, { FC, HTMLAttributes } from 'react';
|
||||||
import { range } from 'ramda';
|
import { range } from 'ramda';
|
||||||
import * as styles from './styles.scss';
|
import * as styles from './styles.scss';
|
||||||
import { Group } from '~/components/containers/Group';
|
import { Group } from '~/components/containers/Group';
|
||||||
|
import { INode } from '~/redux/types';
|
||||||
|
import { getURL } from '~/utils/dom';
|
||||||
|
|
||||||
type IProps = HTMLAttributes<HTMLDivElement> & {}
|
type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
|
items: Partial<INode>[];
|
||||||
|
};
|
||||||
|
|
||||||
const NodeRelated: FC<IProps> = ({
|
const NodeRelated: FC<IProps> = ({ title, items }) => (
|
||||||
title,
|
|
||||||
}) => (
|
|
||||||
<Group className={styles.wrap}>
|
<Group className={styles.wrap}>
|
||||||
<div className={styles.title}>
|
<div className={styles.title}>
|
||||||
<div className={styles.line} />
|
<div className={styles.line} />
|
||||||
|
@ -15,9 +17,13 @@ const NodeRelated: FC<IProps> = ({
|
||||||
<div className={styles.line} />
|
<div className={styles.line} />
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{
|
{items.map(item => (
|
||||||
range(1, 7).map(el => (<div className={styles.item} key={el} />))
|
<div
|
||||||
}
|
className={styles.item}
|
||||||
|
key={item.id}
|
||||||
|
style={{ backgroundImage: `url("${getURL({ url: item.thumbnail })}")` }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,15 +7,20 @@
|
||||||
|
|
||||||
.grid {
|
.grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
|
grid-template-columns: repeat(3, 1fr);
|
||||||
grid-template-rows: auto;
|
grid-template-rows: auto;
|
||||||
grid-auto-rows: auto;
|
grid-auto-rows: auto;
|
||||||
grid-column-gap: $gap;
|
grid-column-gap: $gap;
|
||||||
grid-row-gap: $gap;
|
grid-row-gap: $gap;
|
||||||
|
|
||||||
|
@include tablet {
|
||||||
|
grid-template-columns: repeat(6, 1fr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
background: darken($content_bg, 2%);
|
background: darken($content_bg, 2%) 50% 50% no-repeat;
|
||||||
|
background-size: cover;
|
||||||
padding-bottom: 100%;
|
padding-bottom: 100%;
|
||||||
border-radius: $cell_radius;
|
border-radius: $cell_radius;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { TagField } from '~/components/containers/TagField';
|
||||||
import { ITag } from '~/redux/types';
|
import { ITag } from '~/redux/types';
|
||||||
import { Tag } from '~/components/node/Tag';
|
import { Tag } from '~/components/node/Tag';
|
||||||
import uniq from 'ramda/es/uniq';
|
import uniq from 'ramda/es/uniq';
|
||||||
|
import assocPath from 'ramda/es/assocPath';
|
||||||
|
|
||||||
type IProps = HTMLAttributes<HTMLDivElement> & {
|
type IProps = HTMLAttributes<HTMLDivElement> & {
|
||||||
tags: Partial<ITag>[];
|
tags: Partial<ITag>[];
|
||||||
|
@ -65,9 +66,14 @@ export const Tags: FC<IProps> = ({ tags, is_editable, onTagsChange, ...props })
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSubmit = useCallback(() => {
|
const onSubmit = useCallback(() => {
|
||||||
if (!data.length) return;
|
const title = input && input.trim();
|
||||||
onTagsChange(uniq([...tags, ...data]).map(tag => tag.title));
|
const items = title ? [...data, { title }] : data;
|
||||||
}, [tags, data, onTagsChange]);
|
|
||||||
|
if (!items.length) return;
|
||||||
|
setData(items);
|
||||||
|
setInput('');
|
||||||
|
onTagsChange(uniq([...tags, ...items]).map(tag => tag.title));
|
||||||
|
}, [tags, data, onTagsChange, input, setInput]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setData(data.filter(({ title }) => !tags.some(tag => tag.title.trim() === title.trim())));
|
setData(data.filter(({ title }) => !tags.some(tag => tag.title.trim() === title.trim())));
|
||||||
|
|
|
@ -13,6 +13,7 @@ export const API = {
|
||||||
GET_NODE: (id: number | string) => `/node/${id}`,
|
GET_NODE: (id: number | string) => `/node/${id}`,
|
||||||
|
|
||||||
COMMENT: (id: INode['id']) => `/node/${id}/comment`,
|
COMMENT: (id: INode['id']) => `/node/${id}/comment`,
|
||||||
|
RELATED: (id: INode['id']) => `/node/${id}/related`,
|
||||||
UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`,
|
UPDATE_TAGS: (id: INode['id']) => `/node/${id}/tags`,
|
||||||
POST_LIKE: (id: INode['id']) => `/node/${id}/like`,
|
POST_LIKE: (id: INode['id']) => `/node/${id}/like`,
|
||||||
POST_STAR: (id: INode['id']) => `/node/${id}/heroic`,
|
POST_STAR: (id: INode['id']) => `/node/${id}/heroic`,
|
||||||
|
|
|
@ -42,7 +42,7 @@ const NodeLayoutUnconnected: FC<IProps> = memo(
|
||||||
match: {
|
match: {
|
||||||
params: { id },
|
params: { id },
|
||||||
},
|
},
|
||||||
node: { is_loading, is_loading_comments, comments = [], current: node },
|
node: { is_loading, is_loading_comments, comments = [], current: node, related },
|
||||||
user,
|
user,
|
||||||
user: { is_user },
|
user: { is_user },
|
||||||
nodeLoadNode,
|
nodeLoadNode,
|
||||||
|
@ -123,9 +123,9 @@ const NodeLayoutUnconnected: FC<IProps> = memo(
|
||||||
<Group style={{ flex: 1, minWidth: 0 }}>
|
<Group style={{ flex: 1, minWidth: 0 }}>
|
||||||
<NodeTags is_editable={is_user} tags={node.tags} onChange={onTagsChange} />
|
<NodeTags is_editable={is_user} tags={node.tags} onChange={onTagsChange} />
|
||||||
|
|
||||||
<NodeRelated title="First album" />
|
{related && related.similar && (
|
||||||
|
<NodeRelated title="ПОХОЖИЕ" items={related.similar} />
|
||||||
<NodeRelated title="Second album" />
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
</div>
|
</div>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
@ -48,6 +48,11 @@ export const nodeSetComments = (comments: IComment[]) => ({
|
||||||
type: NODE_ACTIONS.SET_COMMENTS,
|
type: NODE_ACTIONS.SET_COMMENTS,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const nodeSetRelated = (related: INodeState['related']) => ({
|
||||||
|
related,
|
||||||
|
type: NODE_ACTIONS.SET_RELATED,
|
||||||
|
});
|
||||||
|
|
||||||
export const nodeSetCommentData = (id: number, comment: IComment) => ({
|
export const nodeSetCommentData = (id: number, comment: IComment) => ({
|
||||||
id,
|
id,
|
||||||
comment,
|
comment,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils
|
||||||
import { INode, IResultWithStatus, IComment } from '../types';
|
import { INode, IResultWithStatus, IComment } from '../types';
|
||||||
import { API } from '~/constants/api';
|
import { API } from '~/constants/api';
|
||||||
import { nodeUpdateTags, nodeLike, nodeStar } from './actions';
|
import { nodeUpdateTags, nodeLike, nodeStar } from './actions';
|
||||||
|
import { INodeState } from './reducer';
|
||||||
|
|
||||||
export const postNode = ({
|
export const postNode = ({
|
||||||
access,
|
access,
|
||||||
|
@ -58,12 +59,24 @@ export const getNodeComments = ({
|
||||||
}: {
|
}: {
|
||||||
id: number;
|
id: number;
|
||||||
access: string;
|
access: string;
|
||||||
}): Promise<IResultWithStatus<{ comment: Comment }>> =>
|
}): Promise<IResultWithStatus<{ comments: Comment[] }>> =>
|
||||||
api
|
api
|
||||||
.get(API.NODE.COMMENT(id), configWithToken(access))
|
.get(API.NODE.COMMENT(id), configWithToken(access))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
|
export const getNodeRelated = ({
|
||||||
|
id,
|
||||||
|
access,
|
||||||
|
}: {
|
||||||
|
id: number;
|
||||||
|
access: string;
|
||||||
|
}): Promise<IResultWithStatus<{ related: INodeState['related'] }>> =>
|
||||||
|
api
|
||||||
|
.get(API.NODE.RELATED(id), configWithToken(access))
|
||||||
|
.then(resultMiddleware)
|
||||||
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
export const updateNodeTags = ({
|
export const updateNodeTags = ({
|
||||||
id,
|
id,
|
||||||
tags,
|
tags,
|
||||||
|
|
|
@ -34,6 +34,7 @@ export const NODE_ACTIONS = {
|
||||||
|
|
||||||
POST_COMMENT: `${prefix}POST_COMMENT`,
|
POST_COMMENT: `${prefix}POST_COMMENT`,
|
||||||
SET_COMMENTS: `${prefix}SET_COMMENTS`,
|
SET_COMMENTS: `${prefix}SET_COMMENTS`,
|
||||||
|
SET_RELATED: `${prefix}SET_RELATED`,
|
||||||
|
|
||||||
UPDATE_TAGS: `${prefix}UPDATE_TAGS`,
|
UPDATE_TAGS: `${prefix}UPDATE_TAGS`,
|
||||||
SET_TAGS: `${prefix}SET_TAGS`,
|
SET_TAGS: `${prefix}SET_TAGS`,
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
nodeSetTags,
|
nodeSetTags,
|
||||||
nodeSetEditor,
|
nodeSetEditor,
|
||||||
nodeSetCoverImage,
|
nodeSetCoverImage,
|
||||||
|
nodeSetRelated,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { INodeState } from './reducer';
|
import { INodeState } from './reducer';
|
||||||
|
|
||||||
|
@ -36,6 +37,9 @@ const setSendingComment = (
|
||||||
const setComments = (state: INodeState, { comments }: ReturnType<typeof nodeSetComments>) =>
|
const setComments = (state: INodeState, { comments }: ReturnType<typeof nodeSetComments>) =>
|
||||||
assocPath(['comments'], comments, state);
|
assocPath(['comments'], comments, state);
|
||||||
|
|
||||||
|
const setRelated = (state: INodeState, { related }: ReturnType<typeof nodeSetRelated>) =>
|
||||||
|
assocPath(['related'], related, state);
|
||||||
|
|
||||||
const setCommentData = (
|
const setCommentData = (
|
||||||
state: INodeState,
|
state: INodeState,
|
||||||
{ id, comment }: ReturnType<typeof nodeSetCommentData>
|
{ id, comment }: ReturnType<typeof nodeSetCommentData>
|
||||||
|
@ -59,6 +63,7 @@ export const NODE_HANDLERS = {
|
||||||
[NODE_ACTIONS.SET_CURRENT]: setCurrent,
|
[NODE_ACTIONS.SET_CURRENT]: setCurrent,
|
||||||
[NODE_ACTIONS.SET_SENDING_COMMENT]: setSendingComment,
|
[NODE_ACTIONS.SET_SENDING_COMMENT]: setSendingComment,
|
||||||
[NODE_ACTIONS.SET_COMMENTS]: setComments,
|
[NODE_ACTIONS.SET_COMMENTS]: setComments,
|
||||||
|
[NODE_ACTIONS.SET_RELATED]: setRelated,
|
||||||
[NODE_ACTIONS.SET_COMMENT_DATA]: setCommentData,
|
[NODE_ACTIONS.SET_COMMENT_DATA]: setCommentData,
|
||||||
[NODE_ACTIONS.SET_TAGS]: setTags,
|
[NODE_ACTIONS.SET_TAGS]: setTags,
|
||||||
[NODE_ACTIONS.SET_EDITOR]: setEditor,
|
[NODE_ACTIONS.SET_EDITOR]: setEditor,
|
||||||
|
|
|
@ -7,6 +7,10 @@ export type INodeState = Readonly<{
|
||||||
editor: INode;
|
editor: INode;
|
||||||
current: INode;
|
current: INode;
|
||||||
comments: IComment[];
|
comments: IComment[];
|
||||||
|
related: {
|
||||||
|
albums: Record<string, Partial<INode[]>>;
|
||||||
|
similar: Partial<INode[]>;
|
||||||
|
};
|
||||||
comment_data: Record<number, IComment>;
|
comment_data: Record<number, IComment>;
|
||||||
current_cover_image: IFile;
|
current_cover_image: IFile;
|
||||||
|
|
||||||
|
@ -28,6 +32,7 @@ const INITIAL_STATE: INodeState = {
|
||||||
current: { ...EMPTY_NODE },
|
current: { ...EMPTY_NODE },
|
||||||
comment_data: { 0: { ...EMPTY_COMMENT } },
|
comment_data: { 0: { ...EMPTY_COMMENT } },
|
||||||
comments: [],
|
comments: [],
|
||||||
|
related: null,
|
||||||
current_cover_image: null,
|
current_cover_image: null,
|
||||||
|
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { takeLatest, call, put, select, delay } from 'redux-saga/effects';
|
import { takeLatest, call, put, select, delay, all } from 'redux-saga/effects';
|
||||||
import { push } from 'connected-react-router';
|
import { push } from 'connected-react-router';
|
||||||
|
|
||||||
import { NODE_ACTIONS, EMPTY_NODE, EMPTY_COMMENT, NODE_EDITOR_DATA } from './constants';
|
import { NODE_ACTIONS, EMPTY_NODE, EMPTY_COMMENT, NODE_EDITOR_DATA } from './constants';
|
||||||
|
@ -19,6 +19,7 @@ import {
|
||||||
nodeSetEditor,
|
nodeSetEditor,
|
||||||
nodeEdit,
|
nodeEdit,
|
||||||
nodeLike,
|
nodeLike,
|
||||||
|
nodeSetRelated,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
import {
|
||||||
postNode,
|
postNode,
|
||||||
|
@ -28,6 +29,7 @@ import {
|
||||||
updateNodeTags,
|
updateNodeTags,
|
||||||
postNodeLike,
|
postNodeLike,
|
||||||
postNodeStar,
|
postNodeStar,
|
||||||
|
getNodeRelated,
|
||||||
} from './api';
|
} from './api';
|
||||||
import { reqWrapper } from '../auth/sagas';
|
import { reqWrapper } from '../auth/sagas';
|
||||||
import { flowSetNodes } from '../flow/actions';
|
import { flowSetNodes } from '../flow/actions';
|
||||||
|
@ -112,11 +114,19 @@ function* onNodeLoad({ id, node_type }: ReturnType<typeof nodeLoadNode>) {
|
||||||
|
|
||||||
// todo: load comments
|
// todo: load comments
|
||||||
const {
|
const {
|
||||||
data: { comments },
|
comments: {
|
||||||
} = yield call(reqWrapper, getNodeComments, { id });
|
data: { comments },
|
||||||
|
},
|
||||||
|
related: {
|
||||||
|
data: { related },
|
||||||
|
},
|
||||||
|
} = yield all({
|
||||||
|
comments: call(reqWrapper, getNodeComments, { id }),
|
||||||
|
related: call(reqWrapper, getNodeRelated, { id }),
|
||||||
|
});
|
||||||
|
|
||||||
yield put(nodeSetComments(comments || []));
|
yield put(nodeSetComments(comments || []));
|
||||||
|
yield put(nodeSetRelated(related || []));
|
||||||
yield put(nodeSetLoadingComments(false));
|
yield put(nodeSetLoadingComments(false));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue