mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
removed tag reducer
This commit is contained in:
parent
11b39b8766
commit
31e433af3e
17 changed files with 41 additions and 192 deletions
|
@ -5,7 +5,7 @@ import {
|
|||
ApiGetNodesOfTagResult,
|
||||
ApiGetTagSuggestionsRequest,
|
||||
ApiGetTagSuggestionsResult,
|
||||
} from '~/redux/tag/types';
|
||||
} from '~/types/tags';
|
||||
|
||||
export const apiGetNodesOfTag = ({ tag, offset, limit }: ApiGetNodesOfTagRequest) =>
|
||||
api
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 mapStateToProps> &
|
||||
typeof mapDispatchToProps & {
|
||||
exclude: string[];
|
||||
input: HTMLInputElement;
|
||||
onSelect: (val: string) => void;
|
||||
search: string;
|
||||
};
|
||||
|
||||
const TagAutocompleteUnconnected: FC<Props> = ({
|
||||
const TagAutocomplete: VFC<TagAutocompleteProps> = ({
|
||||
exclude,
|
||||
input,
|
||||
onSelect,
|
||||
search,
|
||||
tagSetAutocomplete,
|
||||
tagLoadAutocomplete,
|
||||
options,
|
||||
}) => {
|
||||
const [selected, setSelected] = useState(-1);
|
||||
|
@ -80,19 +69,6 @@ const TagAutocompleteUnconnected: FC<Props> = ({
|
|||
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<Props> = ({
|
|||
);
|
||||
};
|
||||
|
||||
const TagAutocomplete = connect(mapStateToProps, mapDispatchToProps)(TagAutocompleteUnconnected);
|
||||
|
||||
export { TagAutocomplete };
|
||||
|
|
|
@ -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<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
|||
const [input, setInput] = useState('');
|
||||
const ref = useRef<HTMLInputElement>(null);
|
||||
const wrapper = useRef<HTMLDivElement>(null);
|
||||
const options = useTagAutocomplete(input, exclude);
|
||||
|
||||
const onInput = useCallback(
|
||||
({ target: { value } }: ChangeEvent<HTMLInputElement>) => {
|
||||
|
@ -136,6 +138,7 @@ const TagInput: FC<IProps> = ({ exclude, onAppend, onClearTag, onSubmit }) => {
|
|||
input={ref.current}
|
||||
onSelect={onAutocompleteSelect}
|
||||
search={input}
|
||||
options={options}
|
||||
/>
|
||||
)}
|
||||
</div>
|
|
@ -1,4 +1,4 @@
|
|||
@import "src/styles/variables";
|
||||
@import "~/styles/variables";
|
||||
|
||||
.wrap {
|
||||
position: relative;
|
|
@ -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<HTMLDivElement> & {
|
23
src/hooks/tag/useTagAutocomplete.ts
Normal file
23
src/hooks/tag/useTagAutocomplete.ts
Normal file
|
@ -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 || [];
|
||||
};
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
import { ITagState } from '~/redux/tag/index';
|
||||
import { TAG_ACTIONS } from '~/redux/tag/constants';
|
||||
|
||||
export const tagSetNodes = (nodes: Partial<ITagState['nodes']>) => ({
|
||||
type: TAG_ACTIONS.SET_TAG_NODES,
|
||||
nodes,
|
||||
});
|
||||
|
||||
export const tagLoadNodes = (tag: string) => ({
|
||||
type: TAG_ACTIONS.LOAD_NODES,
|
||||
tag,
|
||||
});
|
||||
|
||||
export const tagSetAutocomplete = (autocomplete: Partial<ITagState['autocomplete']>) => ({
|
||||
type: TAG_ACTIONS.SET_TAG_AUTOCOMPLETE,
|
||||
autocomplete,
|
||||
});
|
||||
|
||||
export const tagLoadAutocomplete = (search: string, exclude: string[]) => ({
|
||||
type: TAG_ACTIONS.LOAD_AUTOCOMPLETE,
|
||||
search,
|
||||
exclude,
|
||||
});
|
|
@ -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`,
|
||||
};
|
|
@ -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<typeof tagSetNodes>) => ({
|
||||
...state,
|
||||
nodes: {
|
||||
...state.nodes,
|
||||
...nodes,
|
||||
},
|
||||
});
|
||||
|
||||
const setAutocomplete = (
|
||||
state: ITagState,
|
||||
{ autocomplete }: ReturnType<typeof tagSetAutocomplete>
|
||||
) => ({
|
||||
...state,
|
||||
autocomplete: {
|
||||
...state.autocomplete,
|
||||
...autocomplete,
|
||||
},
|
||||
});
|
||||
|
||||
export const TAG_HANDLERS = {
|
||||
[TAG_ACTIONS.SET_TAG_NODES]: setNodes,
|
||||
[TAG_ACTIONS.SET_TAG_AUTOCOMPLETE]: setAutocomplete,
|
||||
};
|
|
@ -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);
|
|
@ -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<typeof tagLoadNodes>) {
|
||||
yield put(tagSetNodes({ isLoading: true }));
|
||||
|
||||
try {
|
||||
const { list }: ReturnType<typeof selectTagNodes> = yield select(selectTagNodes);
|
||||
const data: Unwrap<typeof apiGetNodesOfTag> = 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<typeof tagLoadAutocomplete>) {
|
||||
if (search.length < 2) return;
|
||||
|
||||
try {
|
||||
yield put(tagSetAutocomplete({ isLoading: true }));
|
||||
yield delay(200);
|
||||
|
||||
const data: Unwrap<typeof apiGetTagSuggestions> = 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);
|
||||
}
|
|
@ -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;
|
Loading…
Add table
Add a link
Reference in a new issue