From 31b03f9eae8ad66edccae2f8d8c02c3ae975856f Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Tue, 2 Mar 2021 16:09:45 +0700 Subject: [PATCH] flow: refactored sagas --- src/redux/flow/api.ts | 38 ++--- src/redux/flow/sagas.ts | 315 +++++++++++++++++++++------------------- src/redux/flow/types.ts | 10 ++ src/redux/node/api.ts | 54 +++---- src/redux/node/types.ts | 25 ++++ 5 files changed, 228 insertions(+), 214 deletions(-) create mode 100644 src/redux/flow/types.ts diff --git a/src/redux/flow/api.ts b/src/redux/flow/api.ts index f23a8101..d826fb6b 100644 --- a/src/redux/flow/api.ts +++ b/src/redux/flow/api.ts @@ -1,8 +1,8 @@ -import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api'; +import { api, cleanResult, configWithToken } from '~/utils/api'; import { INode, IResultWithStatus } from '../types'; import { API } from '~/constants/api'; -import { flowSetCellView } from '~/redux/flow/actions'; -import { IFlowState } from './reducer'; +import { PostCellViewRequest, PostCellViewResult } from '~/redux/node/types'; +import { GetSearchResultsRequest, GetSearchResultsResult } from '~/redux/flow/types'; export const postNode = ({ access, @@ -11,32 +11,14 @@ export const postNode = ({ access: string; node: INode; }): Promise> => - api - .post(API.NODE.SAVE, { node }, configWithToken(access)) - .then(resultMiddleware) - .catch(errorMiddleware); + api.post(API.NODE.SAVE, { node }, configWithToken(access)).then(cleanResult); -export const postCellView = ({ - id, - flow, - access, -}: ReturnType & { access: string }): Promise> => +export const postCellView = ({ id, flow }: PostCellViewRequest) => api - .post(API.NODE.SET_CELL_VIEW(id), { flow }, configWithToken(access)) - .then(resultMiddleware) - .catch(errorMiddleware); + .post(API.NODE.SET_CELL_VIEW(id), { flow }) + .then(cleanResult); -export const getSearchResults = ({ - access, - text, - skip = 0, -}: IFlowState['search'] & { - access: string; - skip: number; -}): Promise> => +export const getSearchResults = ({ text, skip = 0 }: GetSearchResultsRequest) => api - .get(API.SEARCH.NODES, configWithToken(access, { params: { text, skip } })) - .then(resultMiddleware) - .catch(errorMiddleware); + .get(API.SEARCH.NODES, { params: { text, skip } }) + .then(cleanResult); diff --git a/src/redux/flow/sagas.ts b/src/redux/flow/sagas.ts index d8e5ec36..09f4ac8d 100644 --- a/src/redux/flow/sagas.ts +++ b/src/redux/flow/sagas.ts @@ -1,175 +1,188 @@ -import { takeLatest, call, put, select, takeLeading, delay, race, take } from 'redux-saga/effects'; +import { call, delay, put, race, select, take, takeLatest, takeLeading } from 'redux-saga/effects'; import { REHYDRATE } from 'redux-persist'; import { FLOW_ACTIONS } from './constants'; import { getNodeDiff } from '../node/api'; import { - flowSetNodes, - flowSetCellView, - flowSetHeroes, - flowSetRecent, - flowSetUpdated, - flowSetFlow, flowChangeSearch, + flowSetCellView, + flowSetFlow, + flowSetHeroes, + flowSetNodes, + flowSetRecent, flowSetSearch, + flowSetUpdated, } from './actions'; -import { IResultWithStatus, INode, Unwrap } from '../types'; -import { selectFlowNodes, selectFlow } from './selectors'; -import { wrap } from '../auth/sagas'; -import { postCellView, getSearchResults } from './api'; -import { IFlowState } from './reducer'; +import { Unwrap } from '../types'; +import { selectFlow, selectFlowNodes } from './selectors'; +import { getSearchResults, postCellView } from './api'; import { uniq } from 'ramda'; function hideLoader() { - document.getElementById('main_loader').style.display = 'none'; -} + const loader = document.getElementById('main_loader'); -function* onGetFlow() { - const { - flow: { _persist }, - } = yield select(); - - if (!_persist.rehydrated) return; - - const stored: IFlowState['nodes'] = yield select(selectFlowNodes); - - if (stored.length) { - hideLoader(); - } - - yield put(flowSetFlow({ is_loading: true })); - - const { - data: { before = [], after = [], heroes = [], recent = [], updated = [], valid = null }, - }: IResultWithStatus<{ - before: IFlowState['nodes']; - after: IFlowState['nodes']; - heroes: IFlowState['heroes']; - recent: IFlowState['recent']; - updated: IFlowState['updated']; - valid: INode['id'][]; - }> = yield call(wrap, getNodeDiff, { - start: new Date().toISOString(), - end: new Date().toISOString(), - with_heroes: true, - with_updated: true, - with_recent: true, - with_valid: false, - }); - - const result = uniq([...(before || []), ...(after || [])]); - - yield put(flowSetFlow({ is_loading: false, nodes: result })); - - if (heroes.length) yield put(flowSetHeroes(heroes)); - if (recent.length) yield put(flowSetRecent(recent)); - if (updated.length) yield put(flowSetUpdated(updated)); - - if (!stored.length) hideLoader(); -} - -function* onSetCellView({ id, flow }: ReturnType) { - const nodes = yield select(selectFlowNodes); - yield put(flowSetNodes(nodes.map(node => (node.id === id ? { ...node, flow } : node)))); - - const { data, error } = yield call(wrap, postCellView, { id, flow }); - - // TODO: error handling -} - -function* getMore() { - yield put(flowSetFlow({ is_loading: true })); - const nodes: IFlowState['nodes'] = yield select(selectFlowNodes); - - const start = nodes && nodes[0] && nodes[0].created_at; - const end = nodes && nodes[nodes.length - 1] && nodes[nodes.length - 1].created_at; - - const { error, data } = yield call(wrap, getNodeDiff, { - start, - end, - with_heroes: false, - with_updated: true, - with_recent: true, - with_valid: true, - }); - - if (error || !data) return; - - const result = uniq([ - ...(data.before || []), - ...(data.valid ? nodes.filter(node => data.valid.includes(node.id)) : nodes), - ...(data.after || []), - ]); - - yield put( - flowSetFlow({ - is_loading: false, - nodes: result, - ...(data.recent ? { recent: data.recent } : {}), - ...(data.updated ? { updated: data.updated } : {}), - }) - ); - - yield delay(1000); -} - -function* changeSearch({ search }: ReturnType) { - yield put( - flowSetSearch({ - ...search, - is_loading: !!search.text, - }) - ); - - if (!search.text) return; - - yield delay(500); - - const { data, error }: Unwrap = yield call(wrap, getSearchResults, { - ...search, - }); - - if (error) { - yield put(flowSetSearch({ is_loading: false, results: [], total: 0 })); + if (!loader) { return; } - yield put( - flowSetSearch({ - is_loading: false, - results: data.nodes, - total: data.total, - }) - ); + loader.style.display = 'none'; +} + +function* onGetFlow() { + try { + const { + flow: { _persist }, + } = yield select(); + + if (!_persist.rehydrated) return; + + const stored: ReturnType = yield select(selectFlowNodes); + + if (stored.length) { + hideLoader(); + } + + yield put(flowSetFlow({ is_loading: true })); + + const { + before = [], + after = [], + heroes = [], + recent = [], + updated = [], + }: Unwrap = yield call(getNodeDiff, { + start: new Date().toISOString(), + end: new Date().toISOString(), + with_heroes: true, + with_updated: true, + with_recent: true, + with_valid: false, + }); + + const result = uniq([...(before || []), ...(after || [])]); + + yield put(flowSetFlow({ is_loading: false, nodes: result })); + + if (heroes.length) yield put(flowSetHeroes(heroes)); + if (recent.length) yield put(flowSetRecent(recent)); + if (updated.length) yield put(flowSetUpdated(updated)); + + if (!stored.length) hideLoader(); + } catch (error) { + console.log(error); + } +} + +function* onSetCellView({ id, flow }: ReturnType) { + try { + const nodes: ReturnType = yield select(selectFlowNodes); + yield put(flowSetNodes(nodes.map(node => (node.id === id ? { ...node, flow } : node)))); + yield call(postCellView, { id, flow }); + } catch (error) { + console.log(error); + } +} + +function* getMore() { + try { + yield put(flowSetFlow({ is_loading: true })); + const nodes: ReturnType = yield select(selectFlowNodes); + + const start = nodes && nodes[0] && nodes[0].created_at; + const end = nodes && nodes[nodes.length - 1] && nodes[nodes.length - 1].created_at; + + const data: Unwrap = yield call(getNodeDiff, { + start, + end, + with_heroes: false, + with_updated: true, + with_recent: true, + with_valid: true, + }); + + const result = uniq([ + ...(data.before || []), + ...(data.valid ? nodes.filter(node => data.valid.includes(node.id)) : nodes), + ...(data.after || []), + ]); + + yield put( + flowSetFlow({ + is_loading: false, + nodes: result, + ...(data.recent ? { recent: data.recent } : {}), + ...(data.updated ? { updated: data.updated } : {}), + }) + ); + + yield delay(1000); + } catch (error) {} +} + +function* changeSearch({ search }: ReturnType) { + try { + yield put( + flowSetSearch({ + ...search, + is_loading: !!search.text, + }) + ); + + if (!search.text) return; + + yield delay(500); + + const data: Unwrap = yield call(getSearchResults, { + text: search.text, + }); + + yield put( + flowSetSearch({ + results: data.nodes, + total: data.total, + }) + ); + } catch (error) { + yield put(flowSetSearch({ results: [], total: 0 })); + } finally { + yield put(flowSetSearch({ is_loading: false })); + } } function* loadMoreSearch() { - yield put( - flowSetSearch({ - is_loading_more: true, - }) - ); + try { + yield put( + flowSetSearch({ + is_loading_more: true, + }) + ); - const { search }: ReturnType = yield select(selectFlow); + const { search }: ReturnType = yield select(selectFlow); - const { result, delay }: { result: Unwrap; delay: any } = yield race({ - result: call(wrap, getSearchResults, { - ...search, - skip: search.results.length, - }), - delay: take(FLOW_ACTIONS.CHANGE_SEARCH), - }); + const { result, delay }: { result: Unwrap; delay: any } = yield race({ + result: call(getSearchResults, { + ...search, + skip: search.results.length, + }), + delay: take(FLOW_ACTIONS.CHANGE_SEARCH), + }); - if (delay || result.error) { - return put(flowSetSearch({ is_loading_more: false })); + if (delay) { + return; + } + + yield put( + flowSetSearch({ + results: [...search.results, ...result.nodes], + total: result.total, + }) + ); + } catch (error) { + yield put( + flowSetSearch({ + is_loading_more: false, + }) + ); } - - yield put( - flowSetSearch({ - results: [...search.results, ...result.data.nodes], - total: result.data.total, - is_loading_more: false, - }) - ); } export default function* nodeSaga() { diff --git a/src/redux/flow/types.ts b/src/redux/flow/types.ts new file mode 100644 index 00000000..8a5fc788 --- /dev/null +++ b/src/redux/flow/types.ts @@ -0,0 +1,10 @@ +import { INode } from '~/redux/types'; + +export type GetSearchResultsRequest = { + text: string; + skip?: number; +}; +export type GetSearchResultsResult = { + nodes: INode[]; + total: number; +}; diff --git a/src/redux/node/api.ts b/src/redux/node/api.ts index dc13ccc7..2748abf7 100644 --- a/src/redux/node/api.ts +++ b/src/redux/node/api.ts @@ -1,9 +1,10 @@ -import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api'; +import { api, configWithToken, resultMiddleware, errorMiddleware, cleanResult } from '~/utils/api'; import { INode, IResultWithStatus, IComment } 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'; export const postNode = ({ access, @@ -18,7 +19,7 @@ export const postNode = ({ .catch(errorMiddleware); export const getNodes = ({ - from = null, + from, access, }: { from?: string; @@ -30,41 +31,27 @@ export const getNodes = ({ .catch(errorMiddleware); export const getNodeDiff = ({ - start = null, - end = null, + start, + end, take, with_heroes, with_updated, with_recent, with_valid, - access, -}: { - start?: string; - end?: string; - take?: number; - access: string; - with_heroes: boolean; - with_updated: boolean; - with_recent: boolean; - with_valid: boolean; -}): Promise> => +}: GetNodeDiffRequest) => api - .get( - API.NODE.GET_DIFF, - configWithToken(access, { - params: { - start, - end, - take, - with_heroes, - with_updated, - with_recent, - with_valid, - }, - }) - ) - .then(resultMiddleware) - .catch(errorMiddleware); + .get(API.NODE.GET_DIFF, { + params: { + start, + end, + take, + with_heroes, + with_updated, + with_recent, + with_valid, + }, + }) + .then(cleanResult); export const getNode = ({ id, @@ -73,10 +60,7 @@ export const getNode = ({ id: string | number; access: string; }): Promise> => - api - .get(API.NODE.GET_NODE(id), configWithToken(access)) - .then(resultMiddleware) - .catch(errorMiddleware); + api.get(API.NODE.GET_NODE(id), configWithToken(access)).then(cleanResult); export const postNodeComment = ({ id, diff --git a/src/redux/node/types.ts b/src/redux/node/types.ts index dc3ad0ed..25af91fc 100644 --- a/src/redux/node/types.ts +++ b/src/redux/node/types.ts @@ -6,3 +6,28 @@ export interface IEditorComponentProps { temp: string[]; setTemp: (val: string[]) => void; } + +export type GetNodeDiffRequest = { + start?: string; + end?: string; + take?: number; + with_heroes: boolean; + with_updated: boolean; + with_recent: boolean; + with_valid: boolean; +}; + +export type GetNodeDiffResult = { + before?: INode[]; + after?: INode[]; + heroes?: INode[]; + recent?: INode[]; + updated?: INode[]; + valid: INode['id'][]; +}; + +export type PostCellViewRequest = { + id: INode['id']; + flow: INode['flow']; +}; +export type PostCellViewResult = unknown; // TODO: update it with actual type