1
0
Fork 0
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:
Fedor Katurov 2021-03-03 11:10:28 +07:00
parent be7829f130
commit 5102c738b3
6 changed files with 318 additions and 324 deletions

View file

@ -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`,
},

View file

@ -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,

View file

@ -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);

View file

@ -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`,

View file

@ -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);
}

View file

@ -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;
};