1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

completely removed flow-related sagas

This commit is contained in:
Fedor Katurov 2022-01-04 15:08:20 +07:00
parent 5f3accee48
commit 38eedab3c2
26 changed files with 326 additions and 310 deletions

View file

@ -20,6 +20,8 @@
"formik": "^2.2.6",
"insane": "^2.6.2",
"marked": "^2.0.0",
"mobx": "^6.3.10",
"mobx-react": "^7.2.1",
"node-sass": "^4.14.1",
"photoswipe": "^4.1.3",
"raleway-cyrillic": "^4.0.2",

View file

@ -8,17 +8,18 @@ import { PageCoverProvider } from '~/components/containers/PageCoverProvider';
import { BottomContainer } from '~/containers/main/BottomContainer';
import { MainRouter } from '~/containers/main/MainRouter';
import { DragDetectorProvider } from '~/hooks/dom/useDragDetector';
import { useUser } from '~/hooks/user/userUser';
import { UserContextProvider } from '~/utils/context/UserContextProvider';
import { SWRConfigProvider } from '~/utils/providers/SWRConfigProvider';
import { observer } from 'mobx-react';
import { useGlobalLoader } from '~/hooks/dom/useGlobalLoader';
const App: VFC = () => {
const user = useUser();
const App: VFC = observer(() => {
useGlobalLoader();
return (
<ConnectedRouter history={history}>
<SWRConfigProvider>
<UserContextProvider user={user}>
<UserContextProvider>
<DragDetectorProvider>
<PageCoverProvider>
<MainLayout>
@ -34,6 +35,6 @@ const App: VFC = () => {
</SWRConfigProvider>
</ConnectedRouter>
);
};
});
export { App };

View file

@ -7,8 +7,9 @@ import styles from './styles.module.scss';
import { useLoadNode } from '~/hooks/node/useLoadNode';
import { useUpdateNode } from '~/hooks/node/useUpdateNode';
import { INode } from '~/redux/types';
import { observer } from 'mobx-react';
const EditorEditDialog: FC = () => {
const EditorEditDialog: FC = observer(() => {
const history = useHistory();
const {
@ -46,6 +47,6 @@ const EditorEditDialog: FC = () => {
}
return <EditorDialog node={node} onRequestClose={goBack} onSubmit={onSubmit} />;
};
});
export { EditorEditDialog };

View file

@ -0,0 +1,16 @@
import { useEffect } from 'react';
import { useFlowStore } from '~/store/flow/useFlowStore';
import { hideLoader } from '~/utils/dom/hideLoader';
/** simply waits for all data to settle and then show the app */
export const useGlobalLoader = () => {
const flow = useFlowStore();
useEffect(() => {
if (!flow.isRefreshed) {
return;
}
hideLoader();
}, [flow.isRefreshed]);
};

View file

@ -1,20 +1,23 @@
import { useShallowSelect } from '~/hooks/data/useShallowSelect';
import { selectFlow } from '~/redux/flow/selectors';
import { useFlowLayout } from '~/hooks/flow/useFlowLayout';
import { selectLabUpdatesNodes } from '~/redux/lab/selectors';
import { useDispatch } from 'react-redux';
import { useFlowPagination } from '~/hooks/flow/useFlowPagination';
import { useCallback, useMemo } from 'react';
import { FlowDisplay, INode } from '~/redux/types';
import { flowSetCellView } from '~/redux/flow/actions';
import { useFlowLoader } from '~/hooks/flow/useFlowLoader';
import { useFlowStore } from '~/store/flow/useFlowStore';
import { useInfiniteLoader } from '~/hooks/dom/useInfiniteLoader';
export const useFlow = () => {
const { nodes, heroes, recent, updated, isLoading } = useShallowSelect(selectFlow);
const { loadMore, isSyncing } = useFlowLoader();
const { nodes, heroes, recent, updated } = useFlowStore();
const { isFluid, toggleLayout } = useFlowLayout();
const labUpdates = useShallowSelect(selectLabUpdatesNodes);
const dispatch = useDispatch();
useFlowPagination({ isLoading });
useInfiniteLoader(loadMore, isSyncing);
const updates = useMemo(() => [...updated, ...labUpdates].slice(0, 10), [updated, labUpdates]);

View file

@ -0,0 +1,94 @@
import { useCallback, useEffect, useState } from 'react';
import { getNodeDiff } from '~/api/node';
import { uniq } from 'ramda';
import { useFlowStore } from '~/store/flow/useFlowStore';
import { runInAction } from 'mobx';
import { showErrorToast } from '~/utils/errors/showToast';
import { delay } from 'redux-saga/effects';
export const useFlowLoader = () => {
const [isSyncing, setIsSyncing] = useState(false);
const flow = useFlowStore();
/** Loads initial nodes and puts to store */
const getInitialNodes = useCallback(async () => {
try {
setIsSyncing(true);
const { before, after, heroes, recent, updated } = await getNodeDiff({
start: new Date().toISOString(),
end: new Date().toISOString(),
with_heroes: true,
with_updated: true,
with_recent: true,
with_valid: false,
});
runInAction(() => {
flow.setNodes(uniq([...(before || []), ...(after || [])]));
flow.setHeroes(heroes || []);
flow.setUpdated(updated || []);
flow.setRecent(recent || []);
flow.setIsRefreshed(true);
});
} catch (error) {
showErrorToast(error);
} finally {
setIsSyncing(false);
}
}, [flow]);
/** Loads next nodes */
const loadMore = useCallback(async () => {
try {
setIsSyncing(true);
const start = flow.nodes[0].created_at;
const end = flow.nodes[flow.nodes.length - 1] && flow.nodes[flow.nodes.length - 1].created_at;
const data = await getNodeDiff({
start,
end,
with_heroes: false,
with_updated: true,
with_recent: true,
with_valid: true,
});
const nodes = uniq([
...(data.before || []),
...(data.valid ? flow.nodes.filter(node => data.valid.includes(node.id)) : flow.nodes),
...(data.after || []),
]);
runInAction(() => {
flow.setNodes(nodes);
if (data.recent?.length) {
flow.setRecent(data.recent);
}
if (data.updated?.length) {
flow.setUpdated(data.updated);
}
});
// wait a little to debounce
await delay(1000);
} catch (error) {
showErrorToast(error);
} finally {
setIsSyncing(false);
}
}, [flow]);
useEffect(() => {
if (flow.isRefreshed) {
return;
}
void getInitialNodes();
}, [flow, getInitialNodes]);
return { getInitialNodes, isSyncing, loadMore };
};

View file

@ -1,10 +0,0 @@
import { useCallback } from 'react';
import { flowGetMore } from '~/redux/flow/actions';
import { useDispatch } from 'react-redux';
import { useInfiniteLoader } from '~/hooks/dom/useInfiniteLoader';
export const useFlowPagination = ({ isLoading }) => {
const dispatch = useDispatch();
const loadMore = useCallback(() => dispatch(flowGetMore()), [dispatch]);
useInfiniteLoader(loadMore, isLoading);
};

View file

@ -0,0 +1,21 @@
import { useFlowStore } from '~/store/flow/useFlowStore';
import { useCallback } from 'react';
import { FlowDisplay } from '~/redux/types';
import { showErrorToast } from '~/utils/errors/showToast';
import { postCellView } from '~/redux/flow/api';
export const useFlowSetCellView = () => {
const { updateNode } = useFlowStore();
return useCallback(
async (id, flow: FlowDisplay) => {
try {
updateNode(id, { flow });
await postCellView({ id, flow });
} catch (error) {
showErrorToast(error);
}
},
[updateNode]
);
};

View file

@ -1,26 +1,23 @@
import { useCallback } from 'react';
import { INode } from '~/redux/types';
import { apiPostNode } from '~/api/node';
import { selectFlowNodes } from '~/redux/flow/selectors';
import { flowSetNodes } from '~/redux/flow/actions';
import { selectLabListNodes } from '~/redux/lab/selectors';
import { labSetList } from '~/redux/lab/actions';
import { useShallowSelect } from '~/hooks/data/useShallowSelect';
import { useDispatch } from 'react-redux';
import { useFlowStore } from '~/store/flow/useFlowStore';
export const useCreateNode = () => {
const dispatch = useDispatch();
const flowNodes = useShallowSelect(selectFlowNodes);
const flow = useFlowStore();
const labNodes = useShallowSelect(selectLabListNodes);
return useCallback(
async (node: INode) => {
const result = await apiPostNode({ node });
// TODO: use another store here someday
if (node.is_promoted) {
const updatedNodes = [result.node, ...flowNodes];
dispatch(flowSetNodes(updatedNodes));
flow.setNodes([result.node, ...flow.nodes]);
} else {
const updatedNodes = [
{ node: result.node, comment_count: 0, last_seen: node.created_at },
@ -29,6 +26,6 @@ export const useCreateNode = () => {
dispatch(labSetList({ nodes: updatedNodes }));
}
},
[flowNodes, labNodes, dispatch]
[flow, labNodes, dispatch]
);
};

View file

@ -1,11 +1,12 @@
import { INode } from '~/redux/types';
import { useDispatch } from 'react-redux';
import { labSeenNode } from '~/redux/lab/actions';
import { flowSeenNode } from '~/redux/flow/actions';
import { useEffect } from 'react';
import { useFlowStore } from '~/store/flow/useFlowStore';
// useOnNodeSeen updates node seen status across all needed places
export const useOnNodeSeen = (node?: INode) => {
const flow = useFlowStore();
const dispatch = useDispatch();
useEffect(() => {
@ -15,9 +16,9 @@ export const useOnNodeSeen = (node?: INode) => {
// Remove node from updated
if (node.is_promoted) {
dispatch(flowSeenNode(node.id));
flow.seenNode(node.id);
} else {
dispatch(labSeenNode(node.id));
}
}, [dispatch, node]);
}, [dispatch, flow, node]);
};

View file

@ -3,16 +3,16 @@ import { useCallback } from 'react';
import { INode } from '~/redux/types';
import { apiPostNode } from '~/api/node';
import { selectFlowNodes } from '~/redux/flow/selectors';
import { flowSetNodes } from '~/redux/flow/actions';
import { selectLabListNodes } from '~/redux/lab/selectors';
import { labSetList } from '~/redux/lab/actions';
import { useShallowSelect } from '~/hooks/data/useShallowSelect';
import { useDispatch } from 'react-redux';
import { useFlowStore } from '~/store/flow/useFlowStore';
export const useUpdateNode = (id: number) => {
const dispatch = useDispatch();
const { update } = useLoadNode(id);
const flowNodes = useShallowSelect(selectFlowNodes);
const flow = useFlowStore();
const labNodes = useShallowSelect(selectLabListNodes);
return useCallback(
@ -25,12 +25,8 @@ export const useUpdateNode = (id: number) => {
await update(result.node);
// TODO: use another store here someday
if (node.is_promoted) {
const updatedNodes = flowNodes.map(item =>
item.id === result.node.id ? result.node : item
);
dispatch(flowSetNodes(updatedNodes));
flow.updateNode(result.node.id!, result.node);
} else {
const updatedNodes = labNodes.map(item =>
item.node.id === result.node.id ? { ...item, node: result.node } : item
@ -38,6 +34,6 @@ export const useUpdateNode = (id: number) => {
dispatch(labSetList({ nodes: updatedNodes }));
}
},
[update, flowNodes, dispatch, labNodes]
[update, flow, labNodes, dispatch]
);
};

View file

@ -5,14 +5,19 @@ import { PersistGate } from 'redux-persist/integration/react';
import { configureStore } from '~/redux/store';
import { App } from '~/containers/App';
import '~/styles/main.scss';
import { Store } from '~/store';
import { StoreContextProvider } from '~/utils/context/StoreContextProvider';
const { store, persistor } = configureStore();
const mobxStore = new Store();
render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>,
<StoreContextProvider store={mobxStore}>
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</Provider>
</StoreContextProvider>,
document.getElementById('app')
);

View file

@ -6,8 +6,9 @@ import { useNodeComments } from '~/hooks/comments/useNodeComments';
import { useBoris } from '~/hooks/boris/useBoris';
import { NodeContextProvider } from '~/utils/context/NodeContextProvider';
import { useLoadNode } from '~/hooks/node/useLoadNode';
import { observer } from 'mobx-react';
const BorisPage: VFC = () => {
const BorisPage: VFC = observer(() => {
const { node, isLoading, update } = useLoadNode(696);
const onShowImageModal = useImageModal();
@ -42,6 +43,6 @@ const BorisPage: VFC = () => {
</CommentContextProvider>
</NodeContextProvider>
);
};
});
export default BorisPage;

View file

@ -3,10 +3,11 @@ import { FlowLayout } from '~/layouts/FlowLayout';
import { useFlow } from '~/hooks/flow/useFlow';
import { FlowContextProvider } from '~/utils/context/FlowContextProvider';
import { SearchContextProvider } from '~/utils/context/SearchContextProvider';
import { observer } from 'mobx-react';
interface Props {}
const FlowPage: FC<Props> = () => {
const FlowPage: FC<Props> = observer(() => {
const { updates, nodes, heroes, recent, isFluid, toggleLayout, onChangeCellView } = useFlow();
return (
@ -22,6 +23,6 @@ const FlowPage: FC<Props> = () => {
</SearchContextProvider>
</FlowContextProvider>
);
};
});
export default FlowPage;

View file

@ -12,66 +12,69 @@ import { TagsContextProvider } from '~/utils/context/TagsContextProvider';
import { useNodePermissions } from '~/hooks/node/useNodePermissions';
import { NodeRelatedProvider } from '~/utils/providers/NodeRelatedProvider';
import { useLoadNode } from '~/hooks/node/useLoadNode';
import { observer } from 'mobx-react';
type Props = RouteComponentProps<{ id: string }> & {};
const NodePage: FC<Props> = ({
match: {
params: { id },
},
}) => {
const { node, isLoading, update, lastSeen } = useLoadNode(parseInt(id, 10));
const NodePage: FC<Props> = observer(
({
match: {
params: { id },
},
}) => {
const { node, isLoading, update, lastSeen } = useLoadNode(parseInt(id, 10));
const onShowImageModal = useImageModal();
const {
onLoadMoreComments,
onDelete: onDeleteComment,
onEdit: onSaveComment,
comments,
hasMore,
isLoading: isLoadingComments,
} = useNodeComments(parseInt(id, 10));
const { onDelete: onTagDelete, onChange: onTagsChange, onClick: onTagClick } = useNodeTags(
parseInt(id, 10)
);
const user = useUser();
const [canEdit] = useNodePermissions(node);
const onShowImageModal = useImageModal();
const {
onLoadMoreComments,
onDelete: onDeleteComment,
onEdit: onSaveComment,
comments,
hasMore,
isLoading: isLoadingComments,
} = useNodeComments(parseInt(id, 10));
const { onDelete: onTagDelete, onChange: onTagsChange, onClick: onTagClick } = useNodeTags(
parseInt(id, 10)
);
const user = useUser();
const [canEdit] = useNodePermissions(node);
useScrollToTop([id, isLoadingComments]);
useScrollToTop([id, isLoadingComments]);
if (!node) {
// TODO: do something here
return null;
}
if (!node) {
// TODO: do something here
return null;
}
return (
<NodeContextProvider node={node} isLoading={isLoading} update={update}>
<NodeRelatedProvider id={parseInt(id, 10)} tags={node.tags}>
<CommentContextProvider
onSaveComment={onSaveComment}
comments={comments}
hasMore={hasMore}
lastSeenCurrent={lastSeen}
isLoading={isLoadingComments}
onShowImageModal={onShowImageModal}
onLoadMoreComments={onLoadMoreComments}
onDeleteComment={onDeleteComment}
>
<TagsContextProvider
tags={node.tags}
canAppend={user.is_user}
canDelete={canEdit}
isLoading={isLoading}
onChange={onTagsChange}
onTagClick={onTagClick}
onTagDelete={onTagDelete}
return (
<NodeContextProvider node={node} isLoading={isLoading} update={update}>
<NodeRelatedProvider id={parseInt(id, 10)} tags={node.tags}>
<CommentContextProvider
onSaveComment={onSaveComment}
comments={comments}
hasMore={hasMore}
lastSeenCurrent={lastSeen}
isLoading={isLoadingComments}
onShowImageModal={onShowImageModal}
onLoadMoreComments={onLoadMoreComments}
onDeleteComment={onDeleteComment}
>
<NodeLayout />
</TagsContextProvider>
</CommentContextProvider>
</NodeRelatedProvider>
</NodeContextProvider>
);
};
<TagsContextProvider
tags={node.tags}
canAppend={user.is_user}
canDelete={canEdit}
isLoading={isLoading}
onChange={onTagsChange}
onTagClick={onTagClick}
onTagDelete={onTagDelete}
>
<NodeLayout />
</TagsContextProvider>
</CommentContextProvider>
</NodeRelatedProvider>
</NodeContextProvider>
);
}
);
export default NodePage;

View file

@ -2,45 +2,12 @@ import { FLOW_ACTIONS } from './constants';
import { IFlowState } from './reducer';
import { INode } from '../types';
export const flowGetFlow = () => ({
type: FLOW_ACTIONS.GET_FLOW,
});
export const flowSetNodes = (nodes: IFlowState['nodes']) => ({
nodes,
type: FLOW_ACTIONS.SET_NODES,
});
export const flowSetHeroes = (heroes: IFlowState['heroes']) => ({
heroes,
type: FLOW_ACTIONS.SET_HEROES,
});
export const flowSetRecent = (recent: IFlowState['recent']) => ({
recent,
type: FLOW_ACTIONS.SET_RECENT,
});
export const flowSetUpdated = (updated: IFlowState['updated']) => ({
updated,
type: FLOW_ACTIONS.SET_UPDATED,
});
export const flowSetCellView = (id: INode['id'], flow: INode['flow']) => ({
type: FLOW_ACTIONS.SET_CELL_VIEW,
id,
flow,
});
export const flowGetMore = () => ({
type: FLOW_ACTIONS.GET_MORE,
});
export const flowSetFlow = (data: Partial<IFlowState>) => ({
type: FLOW_ACTIONS.SET_FLOW,
data,
});
export const flowSetSearch = (search: Partial<IFlowState['search']>) => ({
type: FLOW_ACTIONS.SET_SEARCH,
search,
@ -54,8 +21,3 @@ export const flowChangeSearch = (search: Partial<IFlowState['search']>) => ({
export const flowLoadMoreSearch = () => ({
type: FLOW_ACTIONS.LOAD_MORE_SEARCH,
});
export const flowSeenNode = (nodeId: INode['id']) => ({
type: FLOW_ACTIONS.SEEN_NODE,
nodeId,
});

View file

@ -1,19 +1,9 @@
const prefix = 'FLOW.';
export const FLOW_ACTIONS = {
GET_FLOW: `${prefix}GET_FLOW`,
SET_FLOW: `${prefix}SET_FLOW`,
SET_NODES: `${prefix}SET_NODES`,
SET_HEROES: `${prefix}SET_HEROES`,
SET_RECENT: `${prefix}SET_RECENT`,
SET_UPDATED: `${prefix}SET_UPDATED`,
SET_RANGE: `${prefix}SET_RANGE`,
SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`,
GET_MORE: `${prefix}GET_MORE`,
SET_SEARCH: `${prefix}SET_SEARCH`,
CHANGE_SEARCH: `${prefix}CHANGE_SEARCH`,
LOAD_MORE_SEARCH: `${prefix}LOAD_MORE_SEARCH`,
SEEN_NODE: `${prefix}SEEN_NODE`,
};

View file

@ -1,32 +1,7 @@
import { assocPath } from 'ramda';
import { FLOW_ACTIONS } from './constants';
import {
flowSetFlow,
flowSetHeroes,
flowSetNodes,
flowSetRecent,
flowSetSearch,
flowSetUpdated,
} from './actions';
import { flowSetSearch } from './actions';
import { IFlowState } from './reducer';
const setNodes = (state: IFlowState, { nodes }: ReturnType<typeof flowSetNodes>) =>
assocPath(['nodes'], nodes, state);
const setHeroes = (state: IFlowState, { heroes }: ReturnType<typeof flowSetHeroes>) =>
assocPath(['heroes'], heroes, state);
const setRecent = (state: IFlowState, { recent }: ReturnType<typeof flowSetRecent>) =>
assocPath(['recent'], recent, state);
const setUpdated = (state: IFlowState, { updated }: ReturnType<typeof flowSetUpdated>) =>
assocPath(['updated'], updated, state);
const setFlow = (state: IFlowState, { data }: ReturnType<typeof flowSetFlow>): IFlowState => ({
...state,
...data,
});
const setSearch = (
state: IFlowState,
{ search }: ReturnType<typeof flowSetSearch>
@ -39,10 +14,5 @@ const setSearch = (
});
export const FLOW_HANDLERS = {
[FLOW_ACTIONS.SET_NODES]: setNodes,
[FLOW_ACTIONS.SET_HEROES]: setHeroes,
[FLOW_ACTIONS.SET_RECENT]: setRecent,
[FLOW_ACTIONS.SET_UPDATED]: setUpdated,
[FLOW_ACTIONS.SET_FLOW]: setFlow,
[FLOW_ACTIONS.SET_SEARCH]: setSearch,
};

View file

@ -1,123 +1,9 @@
import { call, delay, put, race, select, take, takeLatest, takeLeading } from 'redux-saga/effects';
import { REHYDRATE } from 'redux-persist';
import { call, delay, put, race, select, take, takeLatest } from 'redux-saga/effects';
import { FLOW_ACTIONS } from './constants';
import { getNodeDiff } from '~/api/node';
import {
flowChangeSearch,
flowSetCellView,
flowSetFlow,
flowSetHeroes,
flowSetNodes,
flowSetRecent,
flowSetSearch,
flowSetUpdated,
} from './actions';
import { flowChangeSearch, flowSetSearch } from './actions';
import { Unwrap } from '../types';
import { selectFlow, selectFlowNodes } from './selectors';
import { getSearchResults, postCellView } from './api';
import { uniq } from 'ramda';
import { labSeenNode } from '~/redux/lab/actions';
function hideLoader() {
const loader = document.getElementById('main_loader');
if (!loader) {
return;
}
loader.style.display = 'none';
}
function* onGetFlow() {
try {
const {
flow: { _persist },
} = yield select();
if (!_persist.rehydrated) return;
const stored: ReturnType<typeof selectFlowNodes> = yield select(selectFlowNodes);
if (stored.length) {
hideLoader();
}
yield put(flowSetFlow({ isLoading: true }));
const {
before = [],
after = [],
heroes = [],
recent = [],
updated = [],
}: Unwrap<typeof getNodeDiff> = 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({ isLoading: 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<typeof flowSetCellView>) {
try {
const nodes: ReturnType<typeof selectFlowNodes> = 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({ isLoading: true }));
const nodes: ReturnType<typeof selectFlowNodes> = 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<typeof getNodeDiff> = 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({
isLoading: false,
nodes: result,
...(data.recent ? { recent: data.recent } : {}),
...(data.updated ? { updated: data.updated } : {}),
})
);
yield delay(1000);
} catch (error) {}
}
import { selectFlow } from './selectors';
import { getSearchResults } from './api';
function* changeSearch({ search }: ReturnType<typeof flowChangeSearch>) {
try {
@ -186,16 +72,7 @@ function* loadMoreSearch() {
}
}
function* seenNode({ nodeId }: ReturnType<typeof labSeenNode>) {
const { updated }: ReturnType<typeof selectFlow> = yield select(selectFlow);
yield put(flowSetUpdated(updated.filter(node => node.id != nodeId)));
}
export default function* nodeSaga() {
yield takeLatest([FLOW_ACTIONS.GET_FLOW, REHYDRATE], onGetFlow);
yield takeLatest(FLOW_ACTIONS.SET_CELL_VIEW, onSetCellView);
yield takeLeading(FLOW_ACTIONS.GET_MORE, getMore);
yield takeLatest(FLOW_ACTIONS.CHANGE_SEARCH, changeSearch);
yield takeLatest(FLOW_ACTIONS.LOAD_MORE_SEARCH, loadMoreSearch);
yield takeLatest(FLOW_ACTIONS.SEEN_NODE, seenNode);
}

View file

@ -0,0 +1,33 @@
import { makeAutoObservable } from 'mobx';
import { IFlowNode } from '~/redux/types';
export class FlowStore {
nodes: IFlowNode[] = [];
heroes: IFlowNode[] = [];
recent: IFlowNode[] = [];
updated: IFlowNode[] = [];
/** if store was updated after rehydration */
isRefreshed = false;
constructor() {
makeAutoObservable(this);
}
setNodes = (nodes: IFlowNode[]) => (this.nodes = nodes);
setHeroes = (heroes: IFlowNode[]) => (this.heroes = heroes);
setRecent = (recent: IFlowNode[]) => (this.recent = recent);
setUpdated = (updated: IFlowNode[]) => (this.updated = updated);
setIsRefreshed = (refreshed: boolean) => (this.isRefreshed = refreshed);
/** removes node from updated after user seen it */
seenNode = (nodeId: number) => {
this.setUpdated(this.updated.filter(node => node.id !== nodeId));
};
/** replaces node with value */
updateNode = (id: number, node: Partial<IFlowNode>) => {
this.setNodes(this.nodes.map(it => (it.id === id ? { ...it, ...node } : it)));
};
}

View file

@ -0,0 +1,3 @@
import { useStore } from '~/utils/context/StoreContextProvider';
export const useFlowStore = () => useStore().flow;

10
src/store/index.ts Normal file
View file

@ -0,0 +1,10 @@
import { makeAutoObservable } from 'mobx';
import { FlowStore } from '~/store/flow/FlowStore';
export class Store {
flow = new FlowStore();
constructor() {
makeAutoObservable(this);
}
}

View file

@ -0,0 +1,10 @@
import React, { createContext, FC, useContext } from 'react';
import { Store } from '~/store';
export const StoreContext = createContext<Store>(new Store());
export const StoreContextProvider: FC<{ store: Store }> = ({ children, store }) => {
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>;
};
export const useStore = () => useContext(StoreContext);

View file

@ -1,11 +1,14 @@
import React, { createContext, FC, useContext } from 'react';
import { IUser } from '~/redux/auth/types';
import { EMPTY_USER } from '~/redux/auth/constants';
import { useUser } from '~/hooks/user/userUser';
const UserContext = createContext<IUser>(EMPTY_USER);
export const UserContextProvider: FC<{ user: IUser }> = ({ children, user }) => (
<UserContext.Provider value={user}>{children}</UserContext.Provider>
);
export const UserContextProvider: FC = ({ children }) => {
const user = useUser();
return <UserContext.Provider value={user}>{children}</UserContext.Provider>;
};
export const useUserContext = () => useContext(UserContext);

View file

@ -0,0 +1,9 @@
export const hideLoader = () => {
const loader = document.getElementById('main_loader');
if (!loader) {
return;
}
loader.style.display = 'none';
};

View file

@ -7526,6 +7526,23 @@ mixin-object@^2.0.1:
dependencies:
minimist "^1.2.5"
mobx-react-lite@^3.2.0:
version "3.2.3"
resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.2.3.tgz#83d2b32ebf4383cd0dc0d397acbf53a8e9c66765"
integrity sha512-7exWp1FV0M9dP08H9PIeHlJqDw4IdkQVRMfLYaZFMmlbzSS6ZU6p/kx392KN+rVf81hH3IQYewvRGQ70oiwmbw==
mobx-react@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.2.1.tgz#e9d4c04dc63d05e1139ce773f5fee7a5b4cb7c78"
integrity sha512-LZS99KFLn75VWDXPdRJhILzVQ7qLcRjQbzkK+wVs0Qg4kWw5hOI2USp7tmu+9zP9KYsVBmKyx2k/8cTTBfsymw==
dependencies:
mobx-react-lite "^3.2.0"
mobx@^6.3.10:
version "6.3.10"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.3.10.tgz#c3bc715c8f03717b9a2329f9697d42b7998d42e0"
integrity sha512-lfuIN5TGXBNy/5s3ggr1L+IbD+LvfZVlj5q1ZuqyV9AfMtunYQvE8G0WfewS9tgIR3I1q8HJEEbcAOsxEgLwRw==
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"