mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +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`,
|
||||
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_HEROIC: (id: INode['id']) => `/node/${id}/heroic`,
|
||||
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`,
|
||||
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
||||
},
|
||||
|
|
|
@ -55,11 +55,6 @@ export const nodePostLocalComment = (
|
|||
type: NODE_ACTIONS.POST_COMMENT,
|
||||
});
|
||||
|
||||
export const nodeCancelCommentEdit = (id: number) => ({
|
||||
id,
|
||||
type: NODE_ACTIONS.CANCEL_COMMENT_EDIT,
|
||||
});
|
||||
|
||||
export const nodeSetSendingComment = (is_sending_comment: boolean) => ({
|
||||
is_sending_comment,
|
||||
type: NODE_ACTIONS.SET_SENDING_COMMENT,
|
||||
|
|
|
@ -1,22 +1,43 @@
|
|||
import { api, configWithToken, resultMiddleware, errorMiddleware, cleanResult } from '~/utils/api';
|
||||
import { INode, IResultWithStatus, IComment } from '../types';
|
||||
import { api, cleanResult, configWithToken, errorMiddleware, resultMiddleware } from '~/utils/api';
|
||||
import { IComment, INode, IResultWithStatus } from '../types';
|
||||
import { API } from '~/constants/api';
|
||||
import { nodeUpdateTags, nodeLike, nodeStar, nodeLock, nodeLockComment } from './actions';
|
||||
import { INodeState } from './reducer';
|
||||
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 = ({
|
||||
access,
|
||||
node,
|
||||
}: {
|
||||
access: string;
|
||||
export type ApiPostNodeRequest = { node: INode };
|
||||
export type ApiPostNodeResult = {
|
||||
node: INode;
|
||||
}): Promise<IResultWithStatus<INode>> =>
|
||||
api
|
||||
.post(API.NODE.SAVE, node, configWithToken(access))
|
||||
.then(resultMiddleware)
|
||||
.catch(errorMiddleware);
|
||||
errors: Record<string, string>;
|
||||
};
|
||||
|
||||
export type ApiGetNodeCommentsRequest = {
|
||||
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 = ({
|
||||
from,
|
||||
|
@ -53,113 +74,41 @@ export const getNodeDiff = ({
|
|||
})
|
||||
.then(cleanResult);
|
||||
|
||||
export const getNode = ({
|
||||
id,
|
||||
access,
|
||||
}: {
|
||||
id: string | number;
|
||||
access: string;
|
||||
}): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
||||
api.get(API.NODE.GET_NODE(id), configWithToken(access)).then(cleanResult);
|
||||
export const apiGetNode = ({ id }: ApiGetNodeRequest) =>
|
||||
api.get<ApiGetNodeResult>(API.NODE.GET_NODE(id)).then(cleanResult);
|
||||
|
||||
export const postNodeComment = ({
|
||||
id,
|
||||
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 apiPostComment = ({ id, data }: ApiPostCommentRequest) =>
|
||||
api.post<ApiPostCommentResult>(API.NODE.COMMENT(id), data).then(cleanResult);
|
||||
|
||||
export const getNodeComments = ({
|
||||
export const apiGetNodeComments = ({
|
||||
id,
|
||||
access,
|
||||
take = COMMENTS_DISPLAY,
|
||||
skip = 0,
|
||||
}: {
|
||||
id: number;
|
||||
access: string;
|
||||
take?: number;
|
||||
skip?: number;
|
||||
}): Promise<IResultWithStatus<{ comments: IComment[]; comment_count: number }>> =>
|
||||
}: ApiGetNodeCommentsRequest) =>
|
||||
api
|
||||
.get(API.NODE.COMMENT(id), configWithToken(access, { params: { take, skip } }))
|
||||
.then(resultMiddleware)
|
||||
.catch(errorMiddleware);
|
||||
.get<ApiGetNodeCommentsResponse>(API.NODE.COMMENT(id), { params: { take, skip } })
|
||||
.then(cleanResult);
|
||||
|
||||
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 apiGetNodeRelated = ({ id }: ApiGetNodeRelatedRequest) =>
|
||||
api.get<ApiGetNodeRelatedResult>(API.NODE.RELATED(id)).then(cleanResult);
|
||||
|
||||
export const updateNodeTags = ({
|
||||
id,
|
||||
tags,
|
||||
access,
|
||||
}: ReturnType<typeof nodeUpdateTags> & { access: string }): Promise<IResultWithStatus<{
|
||||
node: INode;
|
||||
}>> =>
|
||||
export const apiPostNodeTags = ({ id, tags }: ApiPostNodeTagsRequest) =>
|
||||
api
|
||||
.post(API.NODE.UPDATE_TAGS(id), { tags }, configWithToken(access))
|
||||
.then(resultMiddleware)
|
||||
.catch(errorMiddleware);
|
||||
.post<ApiPostNodeTagsResult>(API.NODE.UPDATE_TAGS(id), { tags })
|
||||
.then(cleanResult);
|
||||
|
||||
export const postNodeLike = ({
|
||||
id,
|
||||
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 apiPostNodeLike = ({ id }: ApiPostNodeLikeRequest) =>
|
||||
api.post<ApiPostNodeLikeResult>(API.NODE.POST_LIKE(id)).then(cleanResult);
|
||||
|
||||
export const postNodeStar = ({
|
||||
id,
|
||||
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 apiPostNodeHeroic = ({ id }: ApiPostNodeHeroicRequest) =>
|
||||
api.post<ApiPostNodeHeroicResponse>(API.NODE.POST_HEROIC(id)).then(cleanResult);
|
||||
|
||||
export const postNodeLock = ({
|
||||
id,
|
||||
is_locked,
|
||||
access,
|
||||
}: ReturnType<typeof nodeLock> & { access: string }): Promise<IResultWithStatus<{
|
||||
deleted_at: INode['deleted_at'];
|
||||
}>> =>
|
||||
export const apiLockNode = ({ id, is_locked }: ApiLockNodeRequest) =>
|
||||
api
|
||||
.post(API.NODE.POST_LOCK(id), { is_locked }, configWithToken(access))
|
||||
.then(resultMiddleware)
|
||||
.catch(errorMiddleware);
|
||||
.post<ApiLockNodeResult>(API.NODE.POST_LOCK(id), { is_locked })
|
||||
.then(cleanResult);
|
||||
|
||||
export const postNodeLockComment = ({
|
||||
id,
|
||||
is_locked,
|
||||
current,
|
||||
access,
|
||||
}: ReturnType<typeof nodeLockComment> & {
|
||||
access: string;
|
||||
current: INode['id'];
|
||||
}): Promise<IResultWithStatus<{ deleted_at: INode['deleted_at'] }>> =>
|
||||
export const apiLockComment = ({ id, is_locked, current }: ApiLockCommentRequest) =>
|
||||
api
|
||||
.post(API.NODE.POST_LOCK_COMMENT(current, id), { is_locked }, configWithToken(access))
|
||||
.then(resultMiddleware)
|
||||
.catch(errorMiddleware);
|
||||
.post<ApiLockcommentResult>(API.NODE.LOCK_COMMENT(current, id), { is_locked })
|
||||
.then(cleanResult);
|
||||
|
|
|
@ -29,7 +29,6 @@ export const NODE_ACTIONS = {
|
|||
LOCK: `${prefix}LOCK`,
|
||||
LOCK_COMMENT: `${prefix}LOCK_COMMENT`,
|
||||
EDIT_COMMENT: `${prefix}EDIT_COMMENT`,
|
||||
CANCEL_COMMENT_EDIT: `${prefix}CANCEL_COMMENT_EDIT`,
|
||||
CREATE: `${prefix}CREATE`,
|
||||
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 { omit } from 'ramda';
|
||||
|
||||
import {
|
||||
COMMENTS_DISPLAY,
|
||||
|
@ -10,10 +9,8 @@ import {
|
|||
NODE_EDITOR_DATA,
|
||||
} from './constants';
|
||||
import {
|
||||
nodeCancelCommentEdit,
|
||||
nodeCreate,
|
||||
nodeEdit,
|
||||
nodeEditComment,
|
||||
nodeGotoNode,
|
||||
nodeLike,
|
||||
nodeLoadNode,
|
||||
|
@ -34,35 +31,33 @@ import {
|
|||
nodeUpdateTags,
|
||||
} from './actions';
|
||||
import {
|
||||
getNode,
|
||||
getNodeComments,
|
||||
getNodeRelated,
|
||||
postNode,
|
||||
postNodeComment,
|
||||
postNodeLike,
|
||||
postNodeLock,
|
||||
postNodeLockComment,
|
||||
postNodeStar,
|
||||
updateNodeTags,
|
||||
apiGetNode,
|
||||
apiGetNodeComments,
|
||||
apiGetNodeRelated,
|
||||
apiLockComment,
|
||||
apiLockNode,
|
||||
apiPostComment,
|
||||
apiPostNode,
|
||||
apiPostNodeHeroic,
|
||||
apiPostNodeLike,
|
||||
apiPostNodeTags,
|
||||
} from './api';
|
||||
import { wrap } from '../auth/sagas';
|
||||
import { flowSetNodes, flowSetUpdated } from '../flow/actions';
|
||||
import { ERRORS } from '~/constants/errors';
|
||||
import { modalSetShown, modalShowDialog } from '../modal/actions';
|
||||
import { selectFlow, selectFlowNodes } from '../flow/selectors';
|
||||
import { URLS } from '~/constants/urls';
|
||||
import { selectNode } from './selectors';
|
||||
import { INode, IResultWithStatus, Unwrap } from '../types';
|
||||
import { Unwrap } from '../types';
|
||||
import { NODE_EDITOR_DIALOGS } from '~/constants/dialogs';
|
||||
import { DIALOGS } from '~/redux/modal/constants';
|
||||
import { INodeState } from './reducer';
|
||||
import { IFlowState } from '../flow/reducer';
|
||||
|
||||
export function* updateNodeEverywhere(node) {
|
||||
const {
|
||||
current: { id },
|
||||
}: INodeState = yield select(selectNode);
|
||||
const flow_nodes: IFlowState['nodes'] = yield select(selectFlowNodes);
|
||||
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
const flow_nodes: ReturnType<typeof selectFlowNodes> = yield select(selectFlowNodes);
|
||||
|
||||
if (id === node.id) {
|
||||
yield put(nodeSetCurrent(node));
|
||||
|
@ -78,35 +73,33 @@ export function* updateNodeEverywhere(node) {
|
|||
}
|
||||
|
||||
function* onNodeSave({ node }: ReturnType<typeof nodeSave>) {
|
||||
yield put(nodeSetSaveErrors({}));
|
||||
try {
|
||||
yield put(nodeSetSaveErrors({}));
|
||||
|
||||
const {
|
||||
error,
|
||||
data: { errors, node: result },
|
||||
} = yield call(wrap, postNode, { node });
|
||||
const { errors, node: result }: Unwrap<typeof apiPostNode> = yield call(apiPostNode, { node });
|
||||
|
||||
if (errors && Object.values(errors).length > 0) {
|
||||
return yield put(nodeSetSaveErrors(errors));
|
||||
if (errors && Object.values(errors).length > 0) {
|
||||
yield put(nodeSetSaveErrors(errors));
|
||||
return;
|
||||
}
|
||||
|
||||
const nodes: ReturnType<typeof selectFlowNodes> = yield select(selectFlowNodes);
|
||||
const updated_flow_nodes = node.id
|
||||
? nodes.map(item => (item.id === result.id ? result : item))
|
||||
: [result, ...nodes];
|
||||
|
||||
yield put(flowSetNodes(updated_flow_nodes));
|
||||
|
||||
const { current } = yield select(selectNode);
|
||||
|
||||
if (node.id && current.id === result.id) {
|
||||
yield put(nodeSetCurrent(result));
|
||||
}
|
||||
|
||||
return yield put(modalSetShown(false));
|
||||
} catch (error) {
|
||||
yield put(nodeSetSaveErrors({ error: error || ERRORS.CANT_SAVE_NODE }));
|
||||
}
|
||||
|
||||
if (error || !result || !result.id) {
|
||||
return yield put(nodeSetSaveErrors({ error: error || ERRORS.CANT_SAVE_NODE }));
|
||||
}
|
||||
|
||||
const nodes = yield select(selectFlowNodes);
|
||||
const updated_flow_nodes = node.id
|
||||
? nodes.map(item => (item.id === result.id ? result : item))
|
||||
: [result, ...nodes];
|
||||
|
||||
yield put(flowSetNodes(updated_flow_nodes));
|
||||
|
||||
const { current } = yield select(selectNode);
|
||||
|
||||
if (node.id && current.id === result.id) {
|
||||
yield put(nodeSetCurrent(result));
|
||||
}
|
||||
|
||||
return yield put(modalSetShown(false));
|
||||
}
|
||||
|
||||
function* onNodeGoto({ id, node_type }: ReturnType<typeof nodeGotoNode>) {
|
||||
|
@ -114,134 +107,120 @@ function* onNodeGoto({ id, node_type }: ReturnType<typeof nodeGotoNode>) {
|
|||
|
||||
yield put(nodeLoadNode(id));
|
||||
yield put(nodeSetCommentData(0, { ...EMPTY_COMMENT }));
|
||||
yield put(nodeSetRelated(null));
|
||||
yield put(nodeSetRelated({ albums: {}, similar: [] }));
|
||||
}
|
||||
|
||||
function* onNodeLoadMoreComments() {
|
||||
const {
|
||||
current: { id },
|
||||
comments,
|
||||
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
try {
|
||||
const {
|
||||
current: { id },
|
||||
comments,
|
||||
}: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
const { data, error }: Unwrap<typeof getNodeComments> = yield call(wrap, getNodeComments, {
|
||||
id,
|
||||
take: COMMENTS_DISPLAY,
|
||||
skip: comments.length,
|
||||
});
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const current: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
const data: Unwrap<typeof apiGetNodeComments> = yield call(apiGetNodeComments, {
|
||||
id,
|
||||
take: COMMENTS_DISPLAY,
|
||||
skip: comments.length,
|
||||
});
|
||||
|
||||
if (!data || error || current.current.id != id) {
|
||||
return;
|
||||
}
|
||||
const current: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments: [...comments, ...data.comments],
|
||||
comment_count: data.comment_count,
|
||||
})
|
||||
);
|
||||
if (!data || current.current.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments: [...comments, ...data.comments],
|
||||
comment_count: data.comment_count,
|
||||
})
|
||||
);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
function* onNodeLoad({ id, order = 'ASC' }: ReturnType<typeof nodeLoadNode>) {
|
||||
yield put(nodeSetLoading(true));
|
||||
yield put(nodeSetLoadingComments(true));
|
||||
function* onNodeLoad({ id }: ReturnType<typeof nodeLoadNode>) {
|
||||
// Get node body
|
||||
try {
|
||||
yield put(nodeSetLoading(true));
|
||||
yield put(nodeSetLoadingComments(true));
|
||||
|
||||
const {
|
||||
data: { node, error },
|
||||
} = yield call(wrap, getNode, { id });
|
||||
const { node }: Unwrap<typeof apiGetNode> = yield call(apiGetNode, { id });
|
||||
|
||||
if (error || !node || !node.id) {
|
||||
yield put(nodeSetCurrent(node));
|
||||
yield put(nodeSetLoading(false));
|
||||
} catch (error) {
|
||||
yield put(push(URLS.ERRORS.NOT_FOUND));
|
||||
yield put(nodeSetLoading(false));
|
||||
return;
|
||||
}
|
||||
|
||||
yield put(nodeSetCurrent(node));
|
||||
yield put(nodeSetLoading(false));
|
||||
// Comments and related
|
||||
try {
|
||||
const [{ comments, comment_count }, { related }]: [
|
||||
Unwrap<typeof apiGetNodeComments>,
|
||||
Unwrap<typeof apiGetNodeRelated>
|
||||
] = yield all([
|
||||
call(apiGetNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
|
||||
call(apiGetNodeRelated, { id }),
|
||||
]);
|
||||
|
||||
const {
|
||||
comments: {
|
||||
data: { comments, comment_count },
|
||||
},
|
||||
related: {
|
||||
data: { related },
|
||||
},
|
||||
} = yield all({
|
||||
comments: call(wrap, getNodeComments, { id, take: COMMENTS_DISPLAY, skip: 0 }),
|
||||
related: call(wrap, getNodeRelated, { id }),
|
||||
});
|
||||
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments,
|
||||
comment_count,
|
||||
related,
|
||||
is_loading_comments: false,
|
||||
comment_data: { 0: { ...EMPTY_COMMENT } },
|
||||
})
|
||||
);
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments,
|
||||
comment_count,
|
||||
related,
|
||||
is_loading_comments: false,
|
||||
})
|
||||
);
|
||||
} catch {}
|
||||
|
||||
// Remove current node from recently updated
|
||||
const { updated } = yield select(selectFlow);
|
||||
|
||||
if (updated.some(item => item.id === id)) {
|
||||
yield put(flowSetUpdated(updated.filter(item => item.id !== id)));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
function* onPostComment({ nodeId, comment, callback }: ReturnType<typeof nodePostLocalComment>) {
|
||||
const { data, error }: Unwrap<typeof postNodeComment> = yield call(wrap, postNodeComment, {
|
||||
data: comment,
|
||||
id: nodeId,
|
||||
});
|
||||
try {
|
||||
const data: Unwrap<typeof apiPostComment> = yield call(apiPostComment, {
|
||||
data: comment,
|
||||
id: nodeId,
|
||||
});
|
||||
|
||||
if (error || !data.comment) {
|
||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
if (current?.id === nodeId) {
|
||||
const { comments }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
if (!comment.id) {
|
||||
yield put(nodeSetComments([data.comment, ...comments]));
|
||||
} else {
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments: comments.map(item => (item.id === comment.id ? data.comment : item)),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
} catch (error) {
|
||||
return callback(error);
|
||||
}
|
||||
|
||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
if (current?.id === nodeId) {
|
||||
const { comments } = yield select(selectNode);
|
||||
|
||||
if (!comment.id) {
|
||||
yield put(nodeSetComments([data.comment, ...comments]));
|
||||
} else {
|
||||
yield put(
|
||||
nodeSet({
|
||||
comments: comments.map(item => (item.id === comment.id ? data.comment : item)),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
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>) {
|
||||
yield delay(100);
|
||||
|
||||
const {
|
||||
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;
|
||||
|
||||
yield put(nodeSetTags(node.tags));
|
||||
try {
|
||||
const { node }: Unwrap<typeof apiPostNodeTags> = yield call(apiPostNodeTags, { id, tags });
|
||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
if (!node || !node.id || node.id !== current.id) return;
|
||||
yield put(nodeSetTags(node.tags));
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function* onCreateSaga({ node_type: type }: ReturnType<typeof nodeCreate>) {
|
||||
|
@ -252,96 +231,118 @@ function* onCreateSaga({ node_type: type }: ReturnType<typeof nodeCreate>) {
|
|||
}
|
||||
|
||||
function* onEditSaga({ id }: ReturnType<typeof nodeEdit>) {
|
||||
yield put(modalShowDialog(DIALOGS.LOADING));
|
||||
try {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
data: { node },
|
||||
error,
|
||||
} = yield call(wrap, getNode, { id });
|
||||
yield put(modalShowDialog(DIALOGS.LOADING));
|
||||
|
||||
if (error || !node || !node.type || !NODE_EDITOR_DIALOGS[node.type])
|
||||
return yield put(modalSetShown(false));
|
||||
const { node }: Unwrap<typeof apiGetNode> = yield call(apiGetNode, { id });
|
||||
|
||||
yield put(nodeSetEditor(node));
|
||||
yield put(modalShowDialog(NODE_EDITOR_DIALOGS[node.type]));
|
||||
if (!NODE_EDITOR_DIALOGS[node?.type]) {
|
||||
throw new Error('Unknown node type');
|
||||
}
|
||||
|
||||
return true;
|
||||
yield put(nodeSetEditor(node));
|
||||
yield put(modalShowDialog(NODE_EDITOR_DIALOGS[node.type]));
|
||||
} catch (error) {
|
||||
yield put(modalSetShown(false));
|
||||
}
|
||||
}
|
||||
|
||||
function* onLikeSaga({ id }: ReturnType<typeof nodeLike>) {
|
||||
const {
|
||||
current,
|
||||
current: { is_liked, like_count },
|
||||
} = yield select(selectNode);
|
||||
try {
|
||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
yield call(updateNodeEverywhere, {
|
||||
...current,
|
||||
is_liked: !is_liked,
|
||||
like_count: is_liked ? Math.max(like_count - 1, 0) : like_count + 1,
|
||||
});
|
||||
const count = current.like_count || 0;
|
||||
|
||||
const { data, error } = yield call(wrap, postNodeLike, { id });
|
||||
yield call(updateNodeEverywhere, {
|
||||
...current,
|
||||
is_liked: !current.is_liked,
|
||||
like_count: current.is_liked ? Math.max(count - 1, 0) : count + 1,
|
||||
});
|
||||
|
||||
if (!error || data.is_liked === !is_liked) return; // ok and matches
|
||||
const data: Unwrap<typeof apiPostNodeLike> = yield call(apiPostNodeLike, { id });
|
||||
|
||||
yield call(updateNodeEverywhere, { ...current, is_liked, like_count });
|
||||
yield call(updateNodeEverywhere, {
|
||||
...current,
|
||||
is_liked: data.is_liked,
|
||||
});
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function* onStarSaga({ id }: ReturnType<typeof nodeLike>) {
|
||||
const {
|
||||
current,
|
||||
current: { is_heroic },
|
||||
} = yield select(selectNode);
|
||||
try {
|
||||
const {
|
||||
current,
|
||||
current: { is_heroic },
|
||||
} = yield select(selectNode);
|
||||
|
||||
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 });
|
||||
yield call(updateNodeEverywhere, { ...current, is_heroic: data.is_heroic });
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function* onLockSaga({ id, is_locked }: ReturnType<typeof nodeLock>) {
|
||||
const {
|
||||
current,
|
||||
current: { deleted_at },
|
||||
} = yield select(selectNode);
|
||||
const { current }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
yield call(updateNodeEverywhere, {
|
||||
...current,
|
||||
deleted_at: is_locked ? new Date().toISOString() : null,
|
||||
});
|
||||
try {
|
||||
yield call(updateNodeEverywhere, {
|
||||
...current,
|
||||
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>) {
|
||||
const { current, comments } = yield select(selectNode);
|
||||
const { current, comments }: ReturnType<typeof selectNode> = yield select(selectNode);
|
||||
|
||||
yield put(
|
||||
nodeSetComments(
|
||||
comments.map(comment =>
|
||||
comment.id === id
|
||||
? { ...comment, deleted_at: is_locked ? new Date().toISOString() : null }
|
||||
: comment
|
||||
try {
|
||||
yield put(
|
||||
nodeSetComments(
|
||||
comments.map(comment =>
|
||||
comment.id === id
|
||||
? { ...comment, deleted_at: is_locked ? new Date().toISOString() : undefined }
|
||||
: 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,
|
||||
});
|
||||
|
||||
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 }));
|
||||
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
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default function* nodeSaga() {
|
||||
|
@ -349,7 +350,6 @@ export default function* nodeSaga() {
|
|||
yield takeLatest(NODE_ACTIONS.GOTO_NODE, onNodeGoto);
|
||||
yield takeLatest(NODE_ACTIONS.LOAD_NODE, onNodeLoad);
|
||||
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.CREATE, onCreateSaga);
|
||||
yield takeLatest(NODE_ACTIONS.EDIT, onEditSaga);
|
||||
|
@ -357,6 +357,5 @@ export default function* nodeSaga() {
|
|||
yield takeLatest(NODE_ACTIONS.STAR, onStarSaga);
|
||||
yield takeLatest(NODE_ACTIONS.LOCK, onLockSaga);
|
||||
yield takeLatest(NODE_ACTIONS.LOCK_COMMENT, onLockCommentSaga);
|
||||
yield takeLatest(NODE_ACTIONS.EDIT_COMMENT, onEditCommentSaga);
|
||||
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 {
|
||||
data: INode;
|
||||
|
@ -31,3 +32,54 @@ export type PostCellViewRequest = {
|
|||
flow: INode['flow'];
|
||||
};
|
||||
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