mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
node: refactored sagas
This commit is contained in:
parent
be7829f130
commit
5102c738b3
6 changed files with 318 additions and 324 deletions
|
@ -31,9 +31,9 @@ export const API = {
|
||||||
RELATED: (id: INode['id']) => `/node/${id}/related`,
|
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_HEROIC: (id: INode['id']) => `/node/${id}/heroic`,
|
||||||
POST_LOCK: (id: INode['id']) => `/node/${id}/lock`,
|
POST_LOCK: (id: INode['id']) => `/node/${id}/lock`,
|
||||||
POST_LOCK_COMMENT: (id: INode['id'], comment_id: IComment['id']) =>
|
LOCK_COMMENT: (id: INode['id'], comment_id: IComment['id']) =>
|
||||||
`/node/${id}/comment/${comment_id}/lock`,
|
`/node/${id}/comment/${comment_id}/lock`,
|
||||||
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
||||||
},
|
},
|
||||||
|
|
|
@ -55,11 +55,6 @@ export const nodePostLocalComment = (
|
||||||
type: NODE_ACTIONS.POST_COMMENT,
|
type: NODE_ACTIONS.POST_COMMENT,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const nodeCancelCommentEdit = (id: number) => ({
|
|
||||||
id,
|
|
||||||
type: NODE_ACTIONS.CANCEL_COMMENT_EDIT,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const nodeSetSendingComment = (is_sending_comment: boolean) => ({
|
export const nodeSetSendingComment = (is_sending_comment: boolean) => ({
|
||||||
is_sending_comment,
|
is_sending_comment,
|
||||||
type: NODE_ACTIONS.SET_SENDING_COMMENT,
|
type: NODE_ACTIONS.SET_SENDING_COMMENT,
|
||||||
|
|
|
@ -1,22 +1,43 @@
|
||||||
import { api, configWithToken, resultMiddleware, errorMiddleware, cleanResult } from '~/utils/api';
|
import { api, cleanResult, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api';
|
||||||
import { INode, IResultWithStatus, IComment } from '../types';
|
import { IComment, INode, IResultWithStatus } from '../types';
|
||||||
import { API } from '~/constants/api';
|
import { API } from '~/constants/api';
|
||||||
import { nodeUpdateTags, nodeLike, nodeStar, nodeLock, nodeLockComment } from './actions';
|
|
||||||
import { INodeState } from './reducer';
|
|
||||||
import { COMMENTS_DISPLAY } from './constants';
|
import { COMMENTS_DISPLAY } from './constants';
|
||||||
import { GetNodeDiffRequest, GetNodeDiffResult } from '~/redux/node/types';
|
import {
|
||||||
|
ApiGetNodeRelatedRequest,
|
||||||
|
ApiGetNodeRelatedResult,
|
||||||
|
ApiGetNodeRequest,
|
||||||
|
ApiGetNodeResult,
|
||||||
|
ApiLockCommentRequest,
|
||||||
|
ApiLockcommentResult,
|
||||||
|
ApiLockNodeRequest,
|
||||||
|
ApiLockNodeResult,
|
||||||
|
ApiPostCommentRequest,
|
||||||
|
ApiPostCommentResult,
|
||||||
|
ApiPostNodeHeroicRequest,
|
||||||
|
ApiPostNodeHeroicResponse,
|
||||||
|
ApiPostNodeLikeRequest,
|
||||||
|
ApiPostNodeLikeResult,
|
||||||
|
ApiPostNodeTagsRequest,
|
||||||
|
ApiPostNodeTagsResult,
|
||||||
|
GetNodeDiffRequest,
|
||||||
|
GetNodeDiffResult,
|
||||||
|
} from '~/redux/node/types';
|
||||||
|
|
||||||
export const postNode = ({
|
export type ApiPostNodeRequest = { node: INode };
|
||||||
access,
|
export type ApiPostNodeResult = {
|
||||||
node,
|
|
||||||
}: {
|
|
||||||
access: string;
|
|
||||||
node: INode;
|
node: INode;
|
||||||
}): Promise<IResultWithStatus<INode>> =>
|
errors: Record<string, string>;
|
||||||
api
|
};
|
||||||
.post(API.NODE.SAVE, node, configWithToken(access))
|
|
||||||
.then(resultMiddleware)
|
export type ApiGetNodeCommentsRequest = {
|
||||||
.catch(errorMiddleware);
|
id: number;
|
||||||
|
take?: number;
|
||||||
|
skip?: number;
|
||||||
|
};
|
||||||
|
export type ApiGetNodeCommentsResponse = { comments: IComment[]; comment_count: number };
|
||||||
|
|
||||||
|
export const apiPostNode = ({ node }: ApiPostNodeRequest) =>
|
||||||
|
api.post<ApiPostNodeResult>(API.NODE.SAVE, node).then(cleanResult);
|
||||||
|
|
||||||
export const getNodes = ({
|
export const getNodes = ({
|
||||||
from,
|
from,
|
||||||
|
@ -53,113 +74,41 @@ export const getNodeDiff = ({
|
||||||
})
|
})
|
||||||
.then(cleanResult);
|
.then(cleanResult);
|
||||||
|
|
||||||
export const getNode = ({
|
export const apiGetNode = ({ id }: ApiGetNodeRequest) =>
|
||||||
id,
|
api.get<ApiGetNodeResult>(API.NODE.GET_NODE(id)).then(cleanResult);
|
||||||
access,
|
|
||||||
}: {
|
|
||||||
id: string | number;
|
|
||||||
access: string;
|
|
||||||
}): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
|
||||||
api.get(API.NODE.GET_NODE(id), configWithToken(access)).then(cleanResult);
|
|
||||||
|
|
||||||
export const postNodeComment = ({
|
export const apiPostComment = ({ id, data }: ApiPostCommentRequest) =>
|
||||||
id,
|
api.post<ApiPostCommentResult>(API.NODE.COMMENT(id), data).then(cleanResult);
|
||||||
data,
|
|
||||||
access,
|
|
||||||
}: {
|
|
||||||
access: string;
|
|
||||||
id: number;
|
|
||||||
data: IComment;
|
|
||||||
}): Promise<IResultWithStatus<{ comment: Comment }>> =>
|
|
||||||
api
|
|
||||||
.post(API.NODE.COMMENT(id), data, configWithToken(access))
|
|
||||||
.then(resultMiddleware)
|
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const getNodeComments = ({
|
export const apiGetNodeComments = ({
|
||||||
id,
|
id,
|
||||||
access,
|
|
||||||
take = COMMENTS_DISPLAY,
|
take = COMMENTS_DISPLAY,
|
||||||
skip = 0,
|
skip = 0,
|
||||||
}: {
|
}: ApiGetNodeCommentsRequest) =>
|
||||||
id: number;
|
|
||||||
access: string;
|
|
||||||
take?: number;
|
|
||||||
skip?: number;
|
|
||||||
}): Promise<IResultWithStatus<{ comments: IComment[]; comment_count: number }>> =>
|
|
||||||
api
|
api
|
||||||
.get(API.NODE.COMMENT(id), configWithToken(access, { params: { take, skip } }))
|
.get<ApiGetNodeCommentsResponse>(API.NODE.COMMENT(id), { params: { take, skip } })
|
||||||
.then(resultMiddleware)
|
.then(cleanResult);
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const getNodeRelated = ({
|
export const apiGetNodeRelated = ({ id }: ApiGetNodeRelatedRequest) =>
|
||||||
id,
|
api.get<ApiGetNodeRelatedResult>(API.NODE.RELATED(id)).then(cleanResult);
|
||||||
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 apiPostNodeTags = ({ id, tags }: ApiPostNodeTagsRequest) =>
|
||||||
id,
|
|
||||||
tags,
|
|
||||||
access,
|
|
||||||
}: ReturnType<typeof nodeUpdateTags> & { access: string }): Promise<IResultWithStatus<{
|
|
||||||
node: INode;
|
|
||||||
}>> =>
|
|
||||||
api
|
api
|
||||||
.post(API.NODE.UPDATE_TAGS(id), { tags }, configWithToken(access))
|
.post<ApiPostNodeTagsResult>(API.NODE.UPDATE_TAGS(id), { tags })
|
||||||
.then(resultMiddleware)
|
.then(cleanResult);
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const postNodeLike = ({
|
export const apiPostNodeLike = ({ id }: ApiPostNodeLikeRequest) =>
|
||||||
id,
|
api.post<ApiPostNodeLikeResult>(API.NODE.POST_LIKE(id)).then(cleanResult);
|
||||||
access,
|
|
||||||
}: ReturnType<typeof nodeLike> & { access: string }): Promise<IResultWithStatus<{
|
|
||||||
is_liked: INode['is_liked'];
|
|
||||||
}>> =>
|
|
||||||
api
|
|
||||||
.post(API.NODE.POST_LIKE(id), {}, configWithToken(access))
|
|
||||||
.then(resultMiddleware)
|
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const postNodeStar = ({
|
export const apiPostNodeHeroic = ({ id }: ApiPostNodeHeroicRequest) =>
|
||||||
id,
|
api.post<ApiPostNodeHeroicResponse>(API.NODE.POST_HEROIC(id)).then(cleanResult);
|
||||||
access,
|
|
||||||
}: ReturnType<typeof nodeStar> & { access: string }): Promise<IResultWithStatus<{
|
|
||||||
is_liked: INode['is_liked'];
|
|
||||||
}>> =>
|
|
||||||
api
|
|
||||||
.post(API.NODE.POST_STAR(id), {}, configWithToken(access))
|
|
||||||
.then(resultMiddleware)
|
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const postNodeLock = ({
|
export const apiLockNode = ({ id, is_locked }: ApiLockNodeRequest) =>
|
||||||
id,
|
|
||||||
is_locked,
|
|
||||||
access,
|
|
||||||
}: ReturnType<typeof nodeLock> & { access: string }): Promise<IResultWithStatus<{
|
|
||||||
deleted_at: INode['deleted_at'];
|
|
||||||
}>> =>
|
|
||||||
api
|
api
|
||||||
.post(API.NODE.POST_LOCK(id), { is_locked }, configWithToken(access))
|
.post<ApiLockNodeResult>(API.NODE.POST_LOCK(id), { is_locked })
|
||||||
.then(resultMiddleware)
|
.then(cleanResult);
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const postNodeLockComment = ({
|
export const apiLockComment = ({ id, is_locked, current }: ApiLockCommentRequest) =>
|
||||||
id,
|
|
||||||
is_locked,
|
|
||||||
current,
|
|
||||||
access,
|
|
||||||
}: ReturnType<typeof nodeLockComment> & {
|
|
||||||
access: string;
|
|
||||||
current: INode['id'];
|
|
||||||
}): Promise<IResultWithStatus<{ deleted_at: INode['deleted_at'] }>> =>
|
|
||||||
api
|
api
|
||||||
.post(API.NODE.POST_LOCK_COMMENT(current, id), { is_locked }, configWithToken(access))
|
.post<ApiLockcommentResult>(API.NODE.LOCK_COMMENT(current, id), { is_locked })
|
||||||
.then(resultMiddleware)
|
.then(cleanResult);
|
||||||
.catch(errorMiddleware);
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ export const NODE_ACTIONS = {
|
||||||
LOCK: `${prefix}LOCK`,
|
LOCK: `${prefix}LOCK`,
|
||||||
LOCK_COMMENT: `${prefix}LOCK_COMMENT`,
|
LOCK_COMMENT: `${prefix}LOCK_COMMENT`,
|
||||||
EDIT_COMMENT: `${prefix}EDIT_COMMENT`,
|
EDIT_COMMENT: `${prefix}EDIT_COMMENT`,
|
||||||
CANCEL_COMMENT_EDIT: `${prefix}CANCEL_COMMENT_EDIT`,
|
|
||||||
CREATE: `${prefix}CREATE`,
|
CREATE: `${prefix}CREATE`,
|
||||||
LOAD_MORE_COMMENTS: `${prefix}LOAD_MORE_COMMENTS`,
|
LOAD_MORE_COMMENTS: `${prefix}LOAD_MORE_COMMENTS`,
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { all, call, delay, put, select, takeLatest, takeLeading } from 'redux-saga/effects';
|
import { all, call, put, select, takeLatest, takeLeading } from 'redux-saga/effects';
|
||||||
import { push } from 'connected-react-router';
|
import { push } from 'connected-react-router';
|
||||||
import { omit } from 'ramda';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
COMMENTS_DISPLAY,
|
COMMENTS_DISPLAY,
|
||||||
|
@ -10,10 +9,8 @@ import {
|
||||||
NODE_EDITOR_DATA,
|
NODE_EDITOR_DATA,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import {
|
import {
|
||||||
nodeCancelCommentEdit,
|
|
||||||
nodeCreate,
|
nodeCreate,
|
||||||
nodeEdit,
|
nodeEdit,
|
||||||
nodeEditComment,
|
|
||||||
nodeGotoNode,
|
nodeGotoNode,
|
||||||
nodeLike,
|
nodeLike,
|
||||||
nodeLoadNode,
|
nodeLoadNode,
|
||||||
|
@ -34,35 +31,33 @@ import {
|
||||||
nodeUpdateTags,
|
nodeUpdateTags,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import {
|
import {
|
||||||
getNode,
|
apiGetNode,
|
||||||
getNodeComments,
|
apiGetNodeComments,
|
||||||
getNodeRelated,
|
apiGetNodeRelated,
|
||||||
postNode,
|
apiLockComment,
|
||||||
postNodeComment,
|
apiLockNode,
|
||||||
postNodeLike,
|
apiPostComment,
|
||||||
postNodeLock,
|
apiPostNode,
|
||||||
postNodeLockComment,
|
apiPostNodeHeroic,
|
||||||
postNodeStar,
|
apiPostNodeLike,
|
||||||
updateNodeTags,
|
apiPostNodeTags,
|
||||||
} from './api';
|
} from './api';
|
||||||
import { wrap } from '../auth/sagas';
|
|
||||||
import { flowSetNodes, flowSetUpdated } from '../flow/actions';
|
import { flowSetNodes, flowSetUpdated } from '../flow/actions';
|
||||||
import { ERRORS } from '~/constants/errors';
|
import { ERRORS } from '~/constants/errors';
|
||||||
import { modalSetShown, modalShowDialog } from '../modal/actions';
|
import { modalSetShown, modalShowDialog } from '../modal/actions';
|
||||||
import { selectFlow, selectFlowNodes } from '../flow/selectors';
|
import { selectFlow, selectFlowNodes } from '../flow/selectors';
|
||||||
import { URLS } from '~/constants/urls';
|
import { URLS } from '~/constants/urls';
|
||||||
import { selectNode } from './selectors';
|
import { selectNode } from './selectors';
|
||||||
import { INode, IResultWithStatus, Unwrap } from '../types';
|
import { Unwrap } from '../types';
|
||||||
import { NODE_EDITOR_DIALOGS } from '~/constants/dialogs';
|
import { NODE_EDITOR_DIALOGS } from '~/constants/dialogs';
|
||||||
import { DIALOGS } from '~/redux/modal/constants';
|
import { DIALOGS } from '~/redux/modal/constants';
|
||||||
import { INodeState } from './reducer';
|
|
||||||
import { IFlowState } from '../flow/reducer';
|
|
||||||
|
|
||||||
export function* updateNodeEverywhere(node) {
|
export function* updateNodeEverywhere(node) {
|
||||||
const {
|
const {
|
||||||
current: { id },
|
current: { id },
|
||||||
}: INodeState = yield select(selectNode);
|
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
const flow_nodes: IFlowState['nodes'] = yield select(selectFlowNodes);
|
|
||||||
|
const flow_nodes: ReturnType<typeof selectFlowNodes> = yield select(selectFlowNodes);
|
||||||
|
|
||||||
if (id === node.id) {
|
if (id === node.id) {
|
||||||
yield put(nodeSetCurrent(node));
|
yield put(nodeSetCurrent(node));
|
||||||
|
@ -78,22 +73,17 @@ export function* updateNodeEverywhere(node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onNodeSave({ node }: ReturnType<typeof nodeSave>) {
|
function* onNodeSave({ node }: ReturnType<typeof nodeSave>) {
|
||||||
|
try {
|
||||||
yield put(nodeSetSaveErrors({}));
|
yield put(nodeSetSaveErrors({}));
|
||||||
|
|
||||||
const {
|
const { errors, node: result }: Unwrap<typeof apiPostNode> = yield call(apiPostNode, { node });
|
||||||
error,
|
|
||||||
data: { errors, node: result },
|
|
||||||
} = yield call(wrap, postNode, { node });
|
|
||||||
|
|
||||||
if (errors && Object.values(errors).length > 0) {
|
if (errors && Object.values(errors).length > 0) {
|
||||||
return yield put(nodeSetSaveErrors(errors));
|
yield put(nodeSetSaveErrors(errors));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error || !result || !result.id) {
|
const nodes: ReturnType<typeof selectFlowNodes> = yield select(selectFlowNodes);
|
||||||
return yield put(nodeSetSaveErrors({ error: error || ERRORS.CANT_SAVE_NODE }));
|
|
||||||
}
|
|
||||||
|
|
||||||
const nodes = yield select(selectFlowNodes);
|
|
||||||
const updated_flow_nodes = node.id
|
const updated_flow_nodes = node.id
|
||||||
? nodes.map(item => (item.id === result.id ? result : item))
|
? nodes.map(item => (item.id === result.id ? result : item))
|
||||||
: [result, ...nodes];
|
: [result, ...nodes];
|
||||||
|
@ -107,6 +97,9 @@ function* onNodeSave({ node }: ReturnType<typeof nodeSave>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return yield put(modalSetShown(false));
|
return yield put(modalSetShown(false));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(nodeSetSaveErrors({ error: error || ERRORS.CANT_SAVE_NODE }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onNodeGoto({ id, node_type }: ReturnType<typeof nodeGotoNode>) {
|
function* onNodeGoto({ id, node_type }: ReturnType<typeof nodeGotoNode>) {
|
||||||
|
@ -114,16 +107,21 @@ function* onNodeGoto({ id, node_type }: ReturnType<typeof nodeGotoNode>) {
|
||||||
|
|
||||||
yield put(nodeLoadNode(id));
|
yield put(nodeLoadNode(id));
|
||||||
yield put(nodeSetCommentData(0, { ...EMPTY_COMMENT }));
|
yield put(nodeSetCommentData(0, { ...EMPTY_COMMENT }));
|
||||||
yield put(nodeSetRelated(null));
|
yield put(nodeSetRelated({ albums: {}, similar: [] }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onNodeLoadMoreComments() {
|
function* onNodeLoadMoreComments() {
|
||||||
|
try {
|
||||||
const {
|
const {
|
||||||
current: { id },
|
current: { id },
|
||||||
comments,
|
comments,
|
||||||
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
|
|
||||||
const { data, error }: Unwrap<typeof getNodeComments> = yield call(wrap, getNodeComments, {
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data: Unwrap<typeof apiGetNodeComments> = yield call(apiGetNodeComments, {
|
||||||
id,
|
id,
|
||||||
take: COMMENTS_DISPLAY,
|
take: COMMENTS_DISPLAY,
|
||||||
skip: comments.length,
|
skip: comments.length,
|
||||||
|
@ -131,7 +129,7 @@ function* onNodeLoadMoreComments() {
|
||||||
|
|
||||||
const current: ReturnType<typeof selectNode> = yield select(selectNode);
|
const current: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
|
|
||||||
if (!data || error || current.current.id != id) {
|
if (!data || current.current.id != id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,36 +139,33 @@ function* onNodeLoadMoreComments() {
|
||||||
comment_count: data.comment_count,
|
comment_count: data.comment_count,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
|
function* onNodeLoad({ id }: ReturnType<typeof nodeLoadNode>) {
|
||||||
|
// Get node body
|
||||||
|
try {
|
||||||
yield put(nodeSetLoading(true));
|
yield put(nodeSetLoading(true));
|
||||||
yield put(nodeSetLoadingComments(true));
|
yield put(nodeSetLoadingComments(true));
|
||||||
|
|
||||||
const {
|
const { node }: Unwrap<typeof apiGetNode> = yield call(apiGetNode, { id });
|
||||||
data: { node, error },
|
|
||||||
} = yield call(wrap, getNode, { id });
|
|
||||||
|
|
||||||
if (error || !node || !node.id) {
|
|
||||||
yield put(push(URLS.ERRORS.NOT_FOUND));
|
|
||||||
yield put(nodeSetLoading(false));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield put(nodeSetCurrent(node));
|
yield put(nodeSetCurrent(node));
|
||||||
yield put(nodeSetLoading(false));
|
yield put(nodeSetLoading(false));
|
||||||
|
} catch (error) {
|
||||||
|
yield put(push(URLS.ERRORS.NOT_FOUND));
|
||||||
|
yield put(nodeSetLoading(false));
|
||||||
|
}
|
||||||
|
|
||||||
const {
|
// Comments and related
|
||||||
comments: {
|
try {
|
||||||
data: { comments, comment_count },
|
const [{ comments, comment_count }, { related }]: [
|
||||||
},
|
Unwrap<typeof apiGetNodeComments>,
|
||||||
related: {
|
Unwrap<typeof apiGetNodeRelated>
|
||||||
data: { related },
|
] = yield all([
|
||||||
},
|
call(apiGetNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
|
||||||
} = yield all({
|
call(apiGetNodeRelated, { id }),
|
||||||
comments: call(wrap, getNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
|
]);
|
||||||
related: call(wrap, getNodeRelated, { id }),
|
|
||||||
});
|
|
||||||
|
|
||||||
yield put(
|
yield put(
|
||||||
nodeSet({
|
nodeSet({
|
||||||
|
@ -178,33 +173,29 @@ function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
|
||||||
comment_count,
|
comment_count,
|
||||||
related,
|
related,
|
||||||
is_loading_comments: false,
|
is_loading_comments: false,
|
||||||
comment_data: { 0: { ...EMPTY_COMMENT } },
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
// Remove current node from recently updated
|
||||||
const { updated } = yield select(selectFlow);
|
const { updated } = yield select(selectFlow);
|
||||||
|
|
||||||
if (updated.some(item => item.id === id)) {
|
if (updated.some(item => item.id === id)) {
|
||||||
yield put(flowSetUpdated(updated.filter(item => item.id !== id)));
|
yield put(flowSetUpdated(updated.filter(item => item.id !== id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onPostComment({ nodeId, comment, callback }: ReturnType<typeof nodePostLocalComment>) {
|
function* onPostComment({ nodeId, comment, callback }: ReturnType<typeof nodePostLocalComment>) {
|
||||||
const { data, error }: Unwrap<typeof postNodeComment> = yield call(wrap, postNodeComment, {
|
try {
|
||||||
|
const data: Unwrap<typeof apiPostComment> = yield call(apiPostComment, {
|
||||||
data: comment,
|
data: comment,
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (error || !data.comment) {
|
|
||||||
return callback(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
|
|
||||||
if (current?.id === nodeId) {
|
if (current?.id === nodeId) {
|
||||||
const { comments } = yield select(selectNode);
|
const { comments }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
|
|
||||||
if (!comment.id) {
|
if (!comment.id) {
|
||||||
yield put(nodeSetComments([data.comment, ...comments]));
|
yield put(nodeSetComments([data.comment, ...comments]));
|
||||||
|
@ -218,30 +209,18 @@ function* onPostComment({ nodeId, comment, callback }: ReturnType<typeof nodePos
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return callback(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onCancelCommentEdit({ id }: ReturnType<typeof nodeCancelCommentEdit>) {
|
|
||||||
const { comment_data } = yield select(selectNode);
|
|
||||||
|
|
||||||
yield put(
|
|
||||||
nodeSet({
|
|
||||||
comment_data: omit([id.toString()], comment_data),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onUpdateTags({ id, tags }: ReturnType<typeof nodeUpdateTags>) {
|
function* onUpdateTags({ id, tags }: ReturnType<typeof nodeUpdateTags>) {
|
||||||
yield delay(100);
|
try {
|
||||||
|
const { node }: Unwrap<typeof apiPostNodeTags> = yield call(apiPostNodeTags, { id, tags });
|
||||||
const {
|
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
data: { node },
|
|
||||||
}: IResultWithStatus<{ node: INode }> = yield call(wrap, updateNodeTags, { id, tags });
|
|
||||||
|
|
||||||
const { current } = yield select(selectNode);
|
|
||||||
|
|
||||||
if (!node || !node.id || node.id !== current.id) return;
|
if (!node || !node.id || node.id !== current.id) return;
|
||||||
|
|
||||||
yield put(nodeSetTags(node.tags));
|
yield put(nodeSetTags(node.tags));
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onCreateSaga({ node_type: type }: ReturnType<typeof nodeCreate>) {
|
function* onCreateSaga({ node_type: type }: ReturnType<typeof nodeCreate>) {
|
||||||
|
@ -252,42 +231,49 @@ function* onCreateSaga({ node_type: type }: ReturnType<typeof nodeCreate>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onEditSaga({ id }: ReturnType<typeof nodeEdit>) {
|
function* onEditSaga({ id }: ReturnType<typeof nodeEdit>) {
|
||||||
|
try {
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
yield put(modalShowDialog(DIALOGS.LOADING));
|
yield put(modalShowDialog(DIALOGS.LOADING));
|
||||||
|
|
||||||
const {
|
const { node }: Unwrap<typeof apiGetNode> = yield call(apiGetNode, { id });
|
||||||
data: { node },
|
|
||||||
error,
|
|
||||||
} = yield call(wrap, getNode, { id });
|
|
||||||
|
|
||||||
if (error || !node || !node.type || !NODE_EDITOR_DIALOGS[node.type])
|
if (!NODE_EDITOR_DIALOGS[node?.type]) {
|
||||||
return yield put(modalSetShown(false));
|
throw new Error('Unknown node type');
|
||||||
|
}
|
||||||
|
|
||||||
yield put(nodeSetEditor(node));
|
yield put(nodeSetEditor(node));
|
||||||
yield put(modalShowDialog(NODE_EDITOR_DIALOGS[node.type]));
|
yield put(modalShowDialog(NODE_EDITOR_DIALOGS[node.type]));
|
||||||
|
} catch (error) {
|
||||||
return true;
|
yield put(modalSetShown(false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onLikeSaga({ id }: ReturnType<typeof nodeLike>) {
|
function* onLikeSaga({ id }: ReturnType<typeof nodeLike>) {
|
||||||
const {
|
try {
|
||||||
current,
|
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
current: { is_liked, like_count },
|
|
||||||
} = yield select(selectNode);
|
const count = current.like_count || 0;
|
||||||
|
|
||||||
yield call(updateNodeEverywhere, {
|
yield call(updateNodeEverywhere, {
|
||||||
...current,
|
...current,
|
||||||
is_liked: !is_liked,
|
is_liked: !current.is_liked,
|
||||||
like_count: is_liked ? Math.max(like_count - 1, 0) : like_count + 1,
|
like_count: current.is_liked ? Math.max(count - 1, 0) : count + 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { data, error } = yield call(wrap, postNodeLike, { id });
|
const data: Unwrap<typeof apiPostNodeLike> = yield call(apiPostNodeLike, { id });
|
||||||
|
|
||||||
if (!error || data.is_liked === !is_liked) return; // ok and matches
|
yield call(updateNodeEverywhere, {
|
||||||
|
...current,
|
||||||
yield call(updateNodeEverywhere, { ...current, is_liked, like_count });
|
is_liked: data.is_liked,
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onStarSaga({ id }: ReturnType<typeof nodeLike>) {
|
function* onStarSaga({ id }: ReturnType<typeof nodeLike>) {
|
||||||
|
try {
|
||||||
const {
|
const {
|
||||||
current,
|
current,
|
||||||
current: { is_heroic },
|
current: { is_heroic },
|
||||||
|
@ -295,53 +281,68 @@ function* onStarSaga({ id }: ReturnType<typeof nodeLike>) {
|
||||||
|
|
||||||
yield call(updateNodeEverywhere, { ...current, is_heroic: !is_heroic });
|
yield call(updateNodeEverywhere, { ...current, is_heroic: !is_heroic });
|
||||||
|
|
||||||
const { data, error } = yield call(wrap, postNodeStar, { id });
|
const data: Unwrap<typeof apiPostNodeHeroic> = yield call(apiPostNodeHeroic, { id });
|
||||||
|
|
||||||
if (!error || data.is_heroic === !is_heroic) return; // ok and matches
|
yield call(updateNodeEverywhere, { ...current, is_heroic: data.is_heroic });
|
||||||
|
} catch {}
|
||||||
yield call(updateNodeEverywhere, { ...current, is_heroic });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onLockSaga({ id, is_locked }: ReturnType<typeof nodeLock>) {
|
function* onLockSaga({ id, is_locked }: ReturnType<typeof nodeLock>) {
|
||||||
const {
|
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
current,
|
|
||||||
current: { deleted_at },
|
|
||||||
} = yield select(selectNode);
|
|
||||||
|
|
||||||
|
try {
|
||||||
yield call(updateNodeEverywhere, {
|
yield call(updateNodeEverywhere, {
|
||||||
...current,
|
...current,
|
||||||
deleted_at: is_locked ? new Date().toISOString() : null,
|
deleted_at: is_locked ? new Date().toISOString() : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { error } = yield call(wrap, postNodeLock, { id, is_locked });
|
const data: Unwrap<typeof apiLockNode> = yield call(apiLockNode, { id, is_locked });
|
||||||
|
|
||||||
if (error) return yield call(updateNodeEverywhere, { ...current, deleted_at });
|
yield call(updateNodeEverywhere, {
|
||||||
|
...current,
|
||||||
|
deleted_at: data.deleted_at || undefined,
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
yield call(updateNodeEverywhere, { ...current, deleted_at: current.deleted_at });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onLockCommentSaga({ id, is_locked }: ReturnType<typeof nodeLockComment>) {
|
function* onLockCommentSaga({ id, is_locked }: ReturnType<typeof nodeLockComment>) {
|
||||||
const { current, comments } = yield select(selectNode);
|
const { current, comments }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||||
|
|
||||||
|
try {
|
||||||
yield put(
|
yield put(
|
||||||
nodeSetComments(
|
nodeSetComments(
|
||||||
comments.map(comment =>
|
comments.map(comment =>
|
||||||
comment.id === id
|
comment.id === id
|
||||||
? { ...comment, deleted_at: is_locked ? new Date().toISOString() : null }
|
? { ...comment, deleted_at: is_locked ? new Date().toISOString() : undefined }
|
||||||
: comment
|
: comment
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
yield call(wrap, postNodeLockComment, { current: current.id, id, is_locked });
|
const data: Unwrap<typeof apiLockComment> = yield call(apiLockComment, {
|
||||||
|
current: current.id,
|
||||||
|
id,
|
||||||
|
is_locked,
|
||||||
|
});
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
nodeSetComments(
|
||||||
|
comments.map(comment =>
|
||||||
|
comment.id === id ? { ...comment, deleted_at: data.deleted_at || undefined } : comment
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
yield put(
|
||||||
|
nodeSetComments(
|
||||||
|
comments.map(comment =>
|
||||||
|
comment.id === id ? { ...comment, deleted_at: current.deleted_at } : comment
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onEditCommentSaga({ id }: ReturnType<typeof nodeEditComment>) {
|
|
||||||
const { comments } = yield select(selectNode);
|
|
||||||
|
|
||||||
const comment = comments.find(item => item.id === id);
|
|
||||||
|
|
||||||
if (!comment) return;
|
|
||||||
|
|
||||||
yield put(nodeSetCommentData(id, { ...EMPTY_COMMENT, ...comment }));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function* nodeSaga() {
|
export default function* nodeSaga() {
|
||||||
|
@ -349,7 +350,6 @@ export default function* nodeSaga() {
|
||||||
yield takeLatest(NODE_ACTIONS.GOTO_NODE, onNodeGoto);
|
yield takeLatest(NODE_ACTIONS.GOTO_NODE, onNodeGoto);
|
||||||
yield takeLatest(NODE_ACTIONS.LOAD_NODE, onNodeLoad);
|
yield takeLatest(NODE_ACTIONS.LOAD_NODE, onNodeLoad);
|
||||||
yield takeLatest(NODE_ACTIONS.POST_COMMENT, onPostComment);
|
yield takeLatest(NODE_ACTIONS.POST_COMMENT, onPostComment);
|
||||||
yield takeLatest(NODE_ACTIONS.CANCEL_COMMENT_EDIT, onCancelCommentEdit);
|
|
||||||
yield takeLatest(NODE_ACTIONS.UPDATE_TAGS, onUpdateTags);
|
yield takeLatest(NODE_ACTIONS.UPDATE_TAGS, onUpdateTags);
|
||||||
yield takeLatest(NODE_ACTIONS.CREATE, onCreateSaga);
|
yield takeLatest(NODE_ACTIONS.CREATE, onCreateSaga);
|
||||||
yield takeLatest(NODE_ACTIONS.EDIT, onEditSaga);
|
yield takeLatest(NODE_ACTIONS.EDIT, onEditSaga);
|
||||||
|
@ -357,6 +357,5 @@ export default function* nodeSaga() {
|
||||||
yield takeLatest(NODE_ACTIONS.STAR, onStarSaga);
|
yield takeLatest(NODE_ACTIONS.STAR, onStarSaga);
|
||||||
yield takeLatest(NODE_ACTIONS.LOCK, onLockSaga);
|
yield takeLatest(NODE_ACTIONS.LOCK, onLockSaga);
|
||||||
yield takeLatest(NODE_ACTIONS.LOCK_COMMENT, onLockCommentSaga);
|
yield takeLatest(NODE_ACTIONS.LOCK_COMMENT, onLockCommentSaga);
|
||||||
yield takeLatest(NODE_ACTIONS.EDIT_COMMENT, onEditCommentSaga);
|
|
||||||
yield takeLeading(NODE_ACTIONS.LOAD_MORE_COMMENTS, onNodeLoadMoreComments);
|
yield takeLeading(NODE_ACTIONS.LOAD_MORE_COMMENTS, onNodeLoadMoreComments);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { INode } from '~/redux/types';
|
import { IComment, INode } from '~/redux/types';
|
||||||
|
import { INodeState } from '~/redux/node/reducer';
|
||||||
|
|
||||||
export interface IEditorComponentProps {
|
export interface IEditorComponentProps {
|
||||||
data: INode;
|
data: INode;
|
||||||
|
@ -31,3 +32,54 @@ export type PostCellViewRequest = {
|
||||||
flow: INode['flow'];
|
flow: INode['flow'];
|
||||||
};
|
};
|
||||||
export type PostCellViewResult = unknown; // TODO: update it with actual type
|
export type PostCellViewResult = unknown; // TODO: update it with actual type
|
||||||
|
|
||||||
|
export type ApiGetNodeRequest = {
|
||||||
|
id: string | number;
|
||||||
|
};
|
||||||
|
export type ApiGetNodeResult = { node: INode };
|
||||||
|
|
||||||
|
export type ApiGetNodeRelatedRequest = {
|
||||||
|
id: INode['id'];
|
||||||
|
};
|
||||||
|
export type ApiGetNodeRelatedResult = {
|
||||||
|
related: INodeState['related'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiPostCommentRequest = {
|
||||||
|
id: INode['id'];
|
||||||
|
data: IComment;
|
||||||
|
};
|
||||||
|
export type ApiPostCommentResult = {
|
||||||
|
comment: IComment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiPostNodeTagsRequest = {
|
||||||
|
id: INode['id'];
|
||||||
|
tags: string[];
|
||||||
|
};
|
||||||
|
export type ApiPostNodeTagsResult = {
|
||||||
|
node: INode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiPostNodeLikeRequest = { id: INode['id'] };
|
||||||
|
export type ApiPostNodeLikeResult = { is_liked: boolean };
|
||||||
|
|
||||||
|
export type ApiPostNodeHeroicRequest = { id: INode['id'] };
|
||||||
|
export type ApiPostNodeHeroicResponse = { is_heroic: boolean };
|
||||||
|
|
||||||
|
export type ApiLockNodeRequest = {
|
||||||
|
id: INode['id'];
|
||||||
|
is_locked: boolean;
|
||||||
|
};
|
||||||
|
export type ApiLockNodeResult = {
|
||||||
|
deleted_at: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiLockCommentRequest = {
|
||||||
|
id: IComment['id'];
|
||||||
|
current: INode['id'];
|
||||||
|
is_locked: boolean;
|
||||||
|
};
|
||||||
|
export type ApiLockcommentResult = {
|
||||||
|
deleted_at: string;
|
||||||
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue