From 31e433af3ea0a2cf382c94e87eadc4849329e600 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Mon, 3 Jan 2022 13:15:17 +0700 Subject: [PATCH] removed tag reducer --- src/{redux/tag/api.ts => api/tags/index.ts} | 2 +- src/components/node/NodeTags/index.tsx | 2 +- .../node/NodeTagsPlaceholder/index.tsx | 2 +- src/components/tags/TagAutocomplete/index.tsx | 44 ++++----------- .../tags/TagInput/index.tsx | 3 ++ .../tags/TagInput/styles.module.scss | 2 +- .../tags/Tags/index.tsx | 2 +- src/hooks/tag/useTagAutocomplete.ts | 23 ++++++++ src/hooks/tag/useTagNodes.ts | 3 +- src/redux/store.ts | 5 -- src/redux/tag/actions.ts | 23 -------- src/redux/tag/constants.ts | 8 --- src/redux/tag/handlers.ts | 27 ---------- src/redux/tag/index.ts | 29 ---------- src/redux/tag/sagas.ts | 53 ------------------- src/redux/tag/selectors.ts | 5 -- .../tag/types.ts => types/tags/index.ts} | 0 17 files changed, 41 insertions(+), 192 deletions(-) rename src/{redux/tag/api.ts => api/tags/index.ts} (95%) rename src/{components => containers}/tags/TagInput/index.tsx (95%) rename src/{components => containers}/tags/TagInput/styles.module.scss (60%) rename src/{components => containers}/tags/Tags/index.tsx (97%) create mode 100644 src/hooks/tag/useTagAutocomplete.ts delete mode 100644 src/redux/tag/actions.ts delete mode 100644 src/redux/tag/constants.ts delete mode 100644 src/redux/tag/handlers.ts delete mode 100644 src/redux/tag/index.ts delete mode 100644 src/redux/tag/sagas.ts delete mode 100644 src/redux/tag/selectors.ts rename src/{redux/tag/types.ts => types/tags/index.ts} (100%) diff --git a/src/redux/tag/api.ts b/src/api/tags/index.ts similarity index 95% rename from src/redux/tag/api.ts rename to src/api/tags/index.ts index b03f9400..62df00a1 100644 --- a/src/redux/tag/api.ts +++ b/src/api/tags/index.ts @@ -5,7 +5,7 @@ import { ApiGetNodesOfTagResult, ApiGetTagSuggestionsRequest, ApiGetTagSuggestionsResult, -} from '~/redux/tag/types'; +} from '~/types/tags'; export const apiGetNodesOfTag = ({ tag, offset, limit }: ApiGetNodesOfTagRequest) => api diff --git a/src/components/node/NodeTags/index.tsx b/src/components/node/NodeTags/index.tsx index 3a1c42d4..d167421d 100644 --- a/src/components/node/NodeTags/index.tsx +++ b/src/components/node/NodeTags/index.tsx @@ -1,6 +1,6 @@ import React, { FC, memo } from 'react'; import { ITag } from '~/redux/types'; -import { Tags } from '~/components/tags/Tags'; +import { Tags } from '~/containers/tags/Tags'; interface IProps { is_deletable?: boolean; diff --git a/src/components/node/NodeTagsPlaceholder/index.tsx b/src/components/node/NodeTagsPlaceholder/index.tsx index 4fb8e75d..4f6807f2 100644 --- a/src/components/node/NodeTagsPlaceholder/index.tsx +++ b/src/components/node/NodeTagsPlaceholder/index.tsx @@ -1,6 +1,6 @@ import React, { FC, memo } from 'react'; import { ITag } from '~/redux/types'; -import { Tags } from '~/components/tags/Tags'; +import { Tags } from '~/containers/tags/Tags'; interface IProps { is_editable?: boolean; diff --git a/src/components/tags/TagAutocomplete/index.tsx b/src/components/tags/TagAutocomplete/index.tsx index ff783313..af8941bb 100644 --- a/src/components/tags/TagAutocomplete/index.tsx +++ b/src/components/tags/TagAutocomplete/index.tsx @@ -1,34 +1,23 @@ -import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState, VFC } from 'react'; import styles from './styles.module.scss'; import classNames from 'classnames'; -import { connect } from 'react-redux'; -import * as TAG_ACTIONS from '~/redux/tag/actions'; -import { selectTagAutocomplete } from '~/redux/tag/selectors'; import { separateTagOptions } from '~/utils/tag'; import { TagAutocompleteRow } from '~/components/tags/TagAutocompleteRow'; import { usePopper } from 'react-popper'; -const mapStateToProps = selectTagAutocomplete; -const mapDispatchToProps = { - tagSetAutocomplete: TAG_ACTIONS.tagSetAutocomplete, - tagLoadAutocomplete: TAG_ACTIONS.tagLoadAutocomplete, -}; +interface TagAutocompleteProps { + exclude: string[]; + input: HTMLInputElement; + onSelect: (val: string) => void; + search: string; + options: string[]; +} -type Props = ReturnType & - typeof mapDispatchToProps & { - exclude: string[]; - input: HTMLInputElement; - onSelect: (val: string) => void; - search: string; - }; - -const TagAutocompleteUnconnected: FC = ({ +const TagAutocomplete: VFC = ({ exclude, input, onSelect, search, - tagSetAutocomplete, - tagLoadAutocomplete, options, }) => { const [selected, setSelected] = useState(-1); @@ -80,19 +69,6 @@ const TagAutocompleteUnconnected: FC = ({ return () => input.removeEventListener('keydown', onKeyDown); }, [input, onKeyDown]); - useEffect(() => { - setSelected(-1); - tagLoadAutocomplete(search, exclude); - }, [exclude, search, tagLoadAutocomplete]); - - useEffect(() => { - tagSetAutocomplete({ options: [] }); - - return () => { - tagSetAutocomplete({ options: [] }); - }; - }, [tagSetAutocomplete]); - useEffect(() => { if (!scroll.current || !scroll.current?.children[selected + 1]) return; const el = scroll.current?.children[selected + 1] as HTMLDivElement; @@ -143,6 +119,4 @@ const TagAutocompleteUnconnected: FC = ({ ); }; -const TagAutocomplete = connect(mapStateToProps, mapDispatchToProps)(TagAutocompleteUnconnected); - export { TagAutocomplete }; diff --git a/src/components/tags/TagInput/index.tsx b/src/containers/tags/TagInput/index.tsx similarity index 95% rename from src/components/tags/TagInput/index.tsx rename to src/containers/tags/TagInput/index.tsx index 17c00474..9fe0f0ec 100644 --- a/src/components/tags/TagInput/index.tsx +++ b/src/containers/tags/TagInput/index.tsx @@ -1,6 +1,7 @@ import React, { ChangeEvent, FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { TagAutocomplete } from '~/components/tags/TagAutocomplete'; import { TagWrapper } from '~/components/tags/TagWrapper'; +import { useTagAutocomplete } from '~/hooks/tag/useTagAutocomplete'; import styles from './styles.module.scss'; const placeholder = 'Добавить'; @@ -29,6 +30,7 @@ const TagInput: FC = ({ exclude, onAppend, onClearTag, onSubmit }) => { const [input, setInput] = useState(''); const ref = useRef(null); const wrapper = useRef(null); + const options = useTagAutocomplete(input, exclude); const onInput = useCallback( ({ target: { value } }: ChangeEvent) => { @@ -136,6 +138,7 @@ const TagInput: FC = ({ exclude, onAppend, onClearTag, onSubmit }) => { input={ref.current} onSelect={onAutocompleteSelect} search={input} + options={options} /> )} diff --git a/src/components/tags/TagInput/styles.module.scss b/src/containers/tags/TagInput/styles.module.scss similarity index 60% rename from src/components/tags/TagInput/styles.module.scss rename to src/containers/tags/TagInput/styles.module.scss index 9daa272c..050174bd 100644 --- a/src/components/tags/TagInput/styles.module.scss +++ b/src/containers/tags/TagInput/styles.module.scss @@ -1,4 +1,4 @@ -@import "src/styles/variables"; +@import "~/styles/variables"; .wrap { position: relative; diff --git a/src/components/tags/Tags/index.tsx b/src/containers/tags/Tags/index.tsx similarity index 97% rename from src/components/tags/Tags/index.tsx rename to src/containers/tags/Tags/index.tsx index 59e74d5e..4d3c191f 100644 --- a/src/components/tags/Tags/index.tsx +++ b/src/containers/tags/Tags/index.tsx @@ -3,7 +3,7 @@ import { TagField } from '~/components/containers/TagField'; import { ITag } from '~/redux/types'; import { uniq } from 'ramda'; import { Tag } from '~/components/tags/Tag'; -import { TagInput } from '~/components/tags/TagInput'; +import { TagInput } from '~/containers/tags/TagInput'; import { separateTags } from '~/utils/tag'; type IProps = HTMLAttributes & { diff --git a/src/hooks/tag/useTagAutocomplete.ts b/src/hooks/tag/useTagAutocomplete.ts new file mode 100644 index 00000000..3b66d1e8 --- /dev/null +++ b/src/hooks/tag/useTagAutocomplete.ts @@ -0,0 +1,23 @@ +import useSWR from 'swr'; +import { API } from '~/constants/api'; +import { apiGetTagSuggestions } from '~/api/tags'; +import { useEffect, useState } from 'react'; + +export const useTagAutocomplete = (input: string, exclude: string[]): string[] => { + const [search, setSearch] = useState(''); + + useEffect(() => { + const timeout = setTimeout(() => setSearch(input), 200); + return () => clearTimeout(timeout); + }, [input]); + + const { data } = useSWR( + `${API.TAG.AUTOCOMPLETE}?tag=${search}&exclude=${exclude.join(',')}`, + async () => { + const result = await apiGetTagSuggestions({ search, exclude }); + return result.tags || []; + } + ); + + return data || []; +}; diff --git a/src/hooks/tag/useTagNodes.ts b/src/hooks/tag/useTagNodes.ts index e97a58f6..76c5a4bf 100644 --- a/src/hooks/tag/useTagNodes.ts +++ b/src/hooks/tag/useTagNodes.ts @@ -4,8 +4,7 @@ import { API } from '~/constants/api'; import { flatten, isNil } from 'ramda'; import useSWRInfinite from 'swr/infinite'; import { useCallback } from 'react'; -import { apiGetNodesOfTag } from '~/redux/tag/api'; -import { COMMENTS_DISPLAY } from '~/constants/node'; +import { apiGetNodesOfTag } from '~/api/tags'; const PAGE_SIZE = 10; diff --git a/src/redux/store.ts b/src/redux/store.ts index 4eba1388..5275ae5e 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -32,8 +32,6 @@ import { authLogout, authOpenProfile, gotAuthPostMessage } from './auth/actions' import messages, { IMessagesState } from './messages'; import messagesSaga from './messages/sagas'; -import tag, { ITagState } from './tag'; -import tagSaga from './tag/sagas'; import { AxiosError } from 'axios'; import { api } from '~/utils/api'; import { assocPath } from 'ramda'; @@ -64,7 +62,6 @@ export interface IState { flow: IFlowState; player: IPlayerState; messages: IMessagesState; - tag: ITagState; lab: ILabState; } @@ -87,7 +84,6 @@ export const store = createStore( flow: persistReducer(flowPersistConfig, flow), player: persistReducer(playerPersistConfig, player), messages, - tag: tag, lab: lab, }), composeEnhancers(applyMiddleware(routerMiddleware(history), sagaMiddleware)) @@ -103,7 +99,6 @@ export function configureStore(): { sagaMiddleware.run(playerSaga); sagaMiddleware.run(modalSaga); sagaMiddleware.run(messagesSaga); - sagaMiddleware.run(tagSaga); sagaMiddleware.run(labSaga); window.addEventListener('message', message => { diff --git a/src/redux/tag/actions.ts b/src/redux/tag/actions.ts deleted file mode 100644 index 078f47a4..00000000 --- a/src/redux/tag/actions.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ITagState } from '~/redux/tag/index'; -import { TAG_ACTIONS } from '~/redux/tag/constants'; - -export const tagSetNodes = (nodes: Partial) => ({ - type: TAG_ACTIONS.SET_TAG_NODES, - nodes, -}); - -export const tagLoadNodes = (tag: string) => ({ - type: TAG_ACTIONS.LOAD_NODES, - tag, -}); - -export const tagSetAutocomplete = (autocomplete: Partial) => ({ - type: TAG_ACTIONS.SET_TAG_AUTOCOMPLETE, - autocomplete, -}); - -export const tagLoadAutocomplete = (search: string, exclude: string[]) => ({ - type: TAG_ACTIONS.LOAD_AUTOCOMPLETE, - search, - exclude, -}); diff --git a/src/redux/tag/constants.ts b/src/redux/tag/constants.ts deleted file mode 100644 index f205b441..00000000 --- a/src/redux/tag/constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -const prefix = 'TAG.'; - -export const TAG_ACTIONS = { - LOAD_NODES: `${prefix}LOAD_TAG_NODES`, - SET_TAG_NODES: `${prefix}SET_TAG_NODES`, - SET_TAG_AUTOCOMPLETE: `${prefix}SET_TAG_AUTOCOMPLETE`, - LOAD_AUTOCOMPLETE: `${prefix}LOAD_TAG_AUTOCOMPLETE`, -}; diff --git a/src/redux/tag/handlers.ts b/src/redux/tag/handlers.ts deleted file mode 100644 index d9c4ffe9..00000000 --- a/src/redux/tag/handlers.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { TAG_ACTIONS } from '~/redux/tag/constants'; -import { ITagState } from '~/redux/tag/index'; -import { tagSetAutocomplete, tagSetNodes } from '~/redux/tag/actions'; - -const setNodes = (state: ITagState, { nodes }: ReturnType) => ({ - ...state, - nodes: { - ...state.nodes, - ...nodes, - }, -}); - -const setAutocomplete = ( - state: ITagState, - { autocomplete }: ReturnType -) => ({ - ...state, - autocomplete: { - ...state.autocomplete, - ...autocomplete, - }, -}); - -export const TAG_HANDLERS = { - [TAG_ACTIONS.SET_TAG_NODES]: setNodes, - [TAG_ACTIONS.SET_TAG_AUTOCOMPLETE]: setAutocomplete, -}; diff --git a/src/redux/tag/index.ts b/src/redux/tag/index.ts deleted file mode 100644 index b55487f1..00000000 --- a/src/redux/tag/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { createReducer } from '~/utils/reducer'; -import { INode } from '~/redux/types'; -import { TAG_HANDLERS } from '~/redux/tag/handlers'; - -export interface ITagState { - nodes: { - list: INode[]; - count: number; - isLoading: boolean; - }; - autocomplete: { - isLoading: boolean; - options: string[]; - }; -} - -const INITIAL_STATE: ITagState = { - nodes: { - list: [], - count: 0, - isLoading: true, - }, - autocomplete: { - isLoading: true, - options: [], - }, -}; - -export default createReducer(INITIAL_STATE, TAG_HANDLERS); diff --git a/src/redux/tag/sagas.ts b/src/redux/tag/sagas.ts deleted file mode 100644 index 5b6d4875..00000000 --- a/src/redux/tag/sagas.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { TAG_ACTIONS } from '~/redux/tag/constants'; -import { call, delay, put, select, takeLatest } from 'redux-saga/effects'; -import { - tagLoadAutocomplete, - tagLoadNodes, - tagSetAutocomplete, - tagSetNodes, -} from '~/redux/tag/actions'; -import { selectTagNodes } from '~/redux/tag/selectors'; -import { apiGetNodesOfTag, apiGetTagSuggestions } from '~/redux/tag/api'; -import { Unwrap } from '~/redux/types'; - -function* loadTagNodes({ tag }: ReturnType) { - yield put(tagSetNodes({ isLoading: true })); - - try { - const { list }: ReturnType = yield select(selectTagNodes); - const data: Unwrap = yield call(apiGetNodesOfTag, { - tag, - limit: 18, - offset: list.length, - }); - - yield put(tagSetNodes({ list: [...list, ...data.nodes], count: data.count })); - } catch { - } finally { - yield put(tagSetNodes({ isLoading: false })); - } -} - -function* loadAutocomplete({ search, exclude }: ReturnType) { - if (search.length < 2) return; - - try { - yield put(tagSetAutocomplete({ isLoading: true })); - yield delay(200); - - const data: Unwrap = yield call(apiGetTagSuggestions, { - search, - exclude, - }); - - yield put(tagSetAutocomplete({ options: data.tags })); - } catch { - } finally { - yield put(tagSetAutocomplete({ isLoading: false })); - } -} - -export default function* tagSaga() { - yield takeLatest(TAG_ACTIONS.LOAD_NODES, loadTagNodes); - yield takeLatest(TAG_ACTIONS.LOAD_AUTOCOMPLETE, loadAutocomplete); -} diff --git a/src/redux/tag/selectors.ts b/src/redux/tag/selectors.ts deleted file mode 100644 index 74ffdc3e..00000000 --- a/src/redux/tag/selectors.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { IState } from '~/redux/store'; - -export const selectTag = (state: IState) => state.tag; -export const selectTagNodes = (state: IState) => state.tag.nodes; -export const selectTagAutocomplete = (state: IState) => state.tag.autocomplete; diff --git a/src/redux/tag/types.ts b/src/types/tags/index.ts similarity index 100% rename from src/redux/tag/types.ts rename to src/types/tags/index.ts