1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-25 12:56:41 +07:00

removed tag reducer

This commit is contained in:
Fedor Katurov 2022-01-03 13:15:17 +07:00
parent 11b39b8766
commit 31e433af3e
17 changed files with 41 additions and 192 deletions

View file

@ -5,7 +5,7 @@ import {
ApiGetNodesOfTagResult,
ApiGetTagSuggestionsRequest,
ApiGetTagSuggestionsResult,
} from '~/redux/tag/types';
} from '~/types/tags';
export const apiGetNodesOfTag = ({ tag, offset, limit }: ApiGetNodesOfTagRequest) =>
api

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,4 @@
@import "src/styles/variables";
@import "~/styles/variables";
.wrap {
position: relative;

View file

@ -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> & {

View 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 || [];
};

View file

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

View file

@ -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 => {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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