mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
refactored flow page
This commit is contained in:
parent
93a1d4104b
commit
0b77e87778
10 changed files with 182 additions and 88 deletions
|
@ -1,7 +1,5 @@
|
||||||
import React, { FC, Fragment } from 'react';
|
import React, { FC, Fragment } from 'react';
|
||||||
|
import { FlowDisplay, IFlowNode, INode } from '~/redux/types';
|
||||||
import { IFlowState } from '~/redux/flow/reducer';
|
|
||||||
import { FlowDisplay, INode } from '~/redux/types';
|
|
||||||
import { IUser } from '~/redux/auth/types';
|
import { IUser } from '~/redux/auth/types';
|
||||||
import { PRESETS, URLS } from '~/constants/urls';
|
import { PRESETS, URLS } from '~/constants/urls';
|
||||||
import { FlowCell } from '~/components/flow/FlowCell';
|
import { FlowCell } from '~/components/flow/FlowCell';
|
||||||
|
@ -10,7 +8,8 @@ import styles from './styles.module.scss';
|
||||||
import { getURLFromString } from '~/utils/dom';
|
import { getURLFromString } from '~/utils/dom';
|
||||||
import { canEditNode } from '~/utils/node';
|
import { canEditNode } from '~/utils/node';
|
||||||
|
|
||||||
type IProps = Partial<IFlowState> & {
|
type IProps = {
|
||||||
|
nodes: IFlowNode[];
|
||||||
user: Partial<IUser>;
|
user: Partial<IUser>;
|
||||||
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void;
|
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,16 +1,17 @@
|
||||||
import React, { FC, useCallback } from 'react';
|
import React, { FC, useCallback } from 'react';
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { IFlowState } from '~/redux/flow/reducer';
|
|
||||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
import { FlowRecentItem } from '../FlowRecentItem';
|
import { FlowRecentItem } from '../FlowRecentItem';
|
||||||
import { Icon } from '~/components/input/Icon';
|
import { Icon } from '~/components/input/Icon';
|
||||||
|
import { INode } from '~/redux/types';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
search: IFlowState['search'];
|
isLoading: boolean;
|
||||||
|
results: INode[];
|
||||||
onLoadMore: () => void;
|
onLoadMore: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowSearchResults: FC<IProps> = ({ search, onLoadMore }) => {
|
const FlowSearchResults: FC<IProps> = ({ results, isLoading, onLoadMore }) => {
|
||||||
const onScroll = useCallback(
|
const onScroll = useCallback(
|
||||||
event => {
|
event => {
|
||||||
const el = event.target;
|
const el = event.target;
|
||||||
|
@ -23,7 +24,7 @@ const FlowSearchResults: FC<IProps> = ({ search, onLoadMore }) => {
|
||||||
[onLoadMore]
|
[onLoadMore]
|
||||||
);
|
);
|
||||||
|
|
||||||
if (search.is_loading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.loading}>
|
<div className={styles.loading}>
|
||||||
<LoaderCircle size={64} />
|
<LoaderCircle size={64} />
|
||||||
|
@ -31,7 +32,7 @@ const FlowSearchResults: FC<IProps> = ({ search, onLoadMore }) => {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!search.results.length) {
|
if (!results.length) {
|
||||||
return (
|
return (
|
||||||
<div className={styles.loading}>
|
<div className={styles.loading}>
|
||||||
<Icon size={96} icon="search" />
|
<Icon size={96} icon="search" />
|
||||||
|
@ -42,7 +43,7 @@ const FlowSearchResults: FC<IProps> = ({ search, onLoadMore }) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap} onScroll={onScroll}>
|
<div className={styles.wrap} onScroll={onScroll}>
|
||||||
{search.results.map(node => (
|
{results.map(node => (
|
||||||
<FlowRecentItem node={node} key={node.id} />
|
<FlowRecentItem node={node} key={node.id} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import React, { FC, FormEvent, useCallback, useMemo } from 'react';
|
import React, { FC, FormEvent, useCallback, useMemo } from 'react';
|
||||||
import { IFlowState } from '~/redux/flow/reducer';
|
|
||||||
import { InputText } from '~/components/input/InputText';
|
import { InputText } from '~/components/input/InputText';
|
||||||
import { FlowRecent } from '../FlowRecent';
|
import { FlowRecent } from '../FlowRecent';
|
||||||
|
|
||||||
|
@ -11,25 +10,34 @@ import { Toggle } from '~/components/input/Toggle';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Superpower } from '~/components/boris/Superpower';
|
import { Superpower } from '~/components/boris/Superpower';
|
||||||
import { experimentalFeatures } from '~/constants/features';
|
import { experimentalFeatures } from '~/constants/features';
|
||||||
|
import { IFlowNode, INode } from '~/redux/types';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
recent: IFlowState['recent'];
|
searchText: string;
|
||||||
updated: IFlowState['updated'];
|
searchTotal: number;
|
||||||
search: IFlowState['search'];
|
searchIsLoading: boolean;
|
||||||
isFluid: boolean;
|
searchResults: INode[];
|
||||||
onSearchChange: (text: string) => void;
|
onSearchChange: (text: string) => void;
|
||||||
onLoadMore: () => void;
|
onSearchLoadMore: () => void;
|
||||||
toggleLayout: () => void;
|
|
||||||
|
recent: IFlowNode[];
|
||||||
|
updated: IFlowNode[];
|
||||||
|
isFluid: boolean;
|
||||||
|
onToggleLayout: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowStamp: FC<IProps> = ({
|
const FlowStamp: FC<IProps> = ({
|
||||||
|
searchText,
|
||||||
|
searchIsLoading,
|
||||||
|
searchTotal,
|
||||||
|
searchResults,
|
||||||
|
onSearchChange,
|
||||||
|
onSearchLoadMore,
|
||||||
|
|
||||||
recent,
|
recent,
|
||||||
updated,
|
updated,
|
||||||
search,
|
|
||||||
onSearchChange,
|
|
||||||
onLoadMore,
|
|
||||||
isFluid,
|
isFluid,
|
||||||
toggleLayout,
|
onToggleLayout,
|
||||||
}) => {
|
}) => {
|
||||||
const onSearchSubmit = useCallback((event: FormEvent) => {
|
const onSearchSubmit = useCallback((event: FormEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -48,12 +56,12 @@ const FlowStamp: FC<IProps> = ({
|
||||||
|
|
||||||
const after = useMemo(
|
const after = useMemo(
|
||||||
() =>
|
() =>
|
||||||
search.text ? (
|
searchText ? (
|
||||||
<Icon icon="close" size={24} className={styles.close_icon} onClick={onClearSearch} />
|
<Icon icon="close" size={24} className={styles.close_icon} onClick={onClearSearch} />
|
||||||
) : (
|
) : (
|
||||||
<Icon icon="search" size={24} className={styles.search_icon} />
|
<Icon icon="search" size={24} className={styles.search_icon} />
|
||||||
),
|
),
|
||||||
[search.text]
|
[searchText]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -61,7 +69,7 @@ const FlowStamp: FC<IProps> = ({
|
||||||
<form className={styles.search} onSubmit={onSearchSubmit}>
|
<form className={styles.search} onSubmit={onSearchSubmit}>
|
||||||
<InputText
|
<InputText
|
||||||
title="Поиск"
|
title="Поиск"
|
||||||
value={search.text}
|
value={searchText}
|
||||||
handler={onSearchChange}
|
handler={onSearchChange}
|
||||||
after={after}
|
after={after}
|
||||||
onKeyUp={onKeyUp}
|
onKeyUp={onKeyUp}
|
||||||
|
@ -69,16 +77,20 @@ const FlowStamp: FC<IProps> = ({
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
{search.text ? (
|
{searchText ? (
|
||||||
<>
|
<>
|
||||||
<div className={styles.label}>
|
<div className={styles.label}>
|
||||||
<span className={styles.label_text}>Результаты поиска</span>
|
<span className={styles.label_text}>Результаты поиска</span>
|
||||||
<span className="line" />
|
<span className="line" />
|
||||||
<span>{!search.is_loading && search.total}</span>
|
<span>{!searchIsLoading && searchTotal}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.items}>
|
<div className={styles.items}>
|
||||||
<FlowSearchResults search={search} onLoadMore={onLoadMore} />
|
<FlowSearchResults
|
||||||
|
isLoading={searchIsLoading}
|
||||||
|
results={searchResults}
|
||||||
|
onLoadMore={onSearchLoadMore}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -98,7 +110,7 @@ const FlowStamp: FC<IProps> = ({
|
||||||
{experimentalFeatures.liquidFlow && (
|
{experimentalFeatures.liquidFlow && (
|
||||||
<Superpower>
|
<Superpower>
|
||||||
<div className={styles.toggles}>
|
<div className={styles.toggles}>
|
||||||
<Group horizontal onClick={toggleLayout} className={styles.fluid_toggle}>
|
<Group horizontal onClick={onToggleLayout} className={styles.fluid_toggle}>
|
||||||
<Toggle value={isFluid} />
|
<Toggle value={isFluid} />
|
||||||
<div className={styles.toggles__label}>Жидкое течение</div>
|
<div className={styles.toggles__label}>Жидкое течение</div>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { URLS } from '~/constants/urls';
|
import { URLS } from '~/constants/urls';
|
||||||
import { FlowLayout } from '~/layouts/FlowLayout';
|
|
||||||
import { NodeLayout } from '~/layouts/NodeLayout';
|
import { NodeLayout } from '~/layouts/NodeLayout';
|
||||||
import { BorisLayout } from '~/layouts/BorisLayout';
|
import { BorisLayout } from '~/layouts/BorisLayout';
|
||||||
import { ErrorNotFound } from '~/containers/pages/ErrorNotFound';
|
import { ErrorNotFound } from '~/containers/pages/ErrorNotFound';
|
||||||
|
@ -9,6 +8,7 @@ import { LabLayout } from '~/layouts/LabLayout';
|
||||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
import { selectAuthUser } from '~/redux/auth/selectors';
|
import { selectAuthUser } from '~/redux/auth/selectors';
|
||||||
import { ProfileLayout } from '~/layouts/ProfileLayout';
|
import { ProfileLayout } from '~/layouts/ProfileLayout';
|
||||||
|
import FlowPage from '~/pages';
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ const MainRouter: FC<IProps> = () => {
|
||||||
|
|
||||||
{is_user && <Route path={URLS.LAB} component={LabLayout} />}
|
{is_user && <Route path={URLS.LAB} component={LabLayout} />}
|
||||||
|
|
||||||
<Route path={URLS.BASE} component={FlowLayout} />
|
<Route path={URLS.BASE} component={FlowPage} />
|
||||||
<Redirect to="/" />
|
<Redirect to="/" />
|
||||||
</Switch>
|
</Switch>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,61 +1,50 @@
|
||||||
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
import React, { FC } from 'react';
|
||||||
import { useDispatch } from 'react-redux';
|
|
||||||
import { FlowGrid } from '~/components/flow/FlowGrid';
|
import { FlowGrid } from '~/components/flow/FlowGrid';
|
||||||
import { selectFlow } from '~/redux/flow/selectors';
|
|
||||||
import {
|
|
||||||
flowChangeSearch,
|
|
||||||
flowGetMore,
|
|
||||||
flowLoadMoreSearch,
|
|
||||||
flowSetCellView,
|
|
||||||
} from '~/redux/flow/actions';
|
|
||||||
import { selectUser } from '~/redux/auth/selectors';
|
|
||||||
import { FlowHero } from '~/components/flow/FlowHero';
|
|
||||||
import styles from './styles.module.scss';
|
import styles from './styles.module.scss';
|
||||||
import { FlowStamp } from '~/components/flow/FlowStamp';
|
import { FlowStamp } from '~/components/flow/FlowStamp';
|
||||||
import { Container } from '~/containers/main/Container';
|
|
||||||
import { SidebarRouter } from '~/containers/main/SidebarRouter';
|
import { SidebarRouter } from '~/containers/main/SidebarRouter';
|
||||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
import { FlowDisplay, IFlowNode, INode } from '~/redux/types';
|
||||||
import { FlowDisplay, INode } from '~/redux/types';
|
|
||||||
import { selectLabUpdatesNodes } from '~/redux/lab/selectors';
|
|
||||||
import { usePersistedState } from '~/utils/hooks/usePersistedState';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useFlowLayout } from '~/utils/hooks/flow/useFlowLayout';
|
|
||||||
import { useFlowPagination } from '~/utils/hooks/flow/useFlowPagination';
|
|
||||||
import { FlowSwiperHero } from '~/components/flow/FlowSwiperHero';
|
import { FlowSwiperHero } from '~/components/flow/FlowSwiperHero';
|
||||||
|
import { IUser } from '~/redux/auth/types';
|
||||||
|
|
||||||
const FlowLayout: FC = () => {
|
interface Props {
|
||||||
const { nodes, heroes, recent, updated, isLoading, search } = useShallowSelect(selectFlow);
|
updates: IFlowNode[];
|
||||||
const { isFluid, toggleLayout } = useFlowLayout();
|
recent: IFlowNode[];
|
||||||
const labUpdates = useShallowSelect(selectLabUpdatesNodes);
|
heroes: IFlowNode[];
|
||||||
const user = useShallowSelect(selectUser);
|
nodes: IFlowNode[];
|
||||||
const dispatch = useDispatch();
|
user: IUser;
|
||||||
|
isFluid: boolean;
|
||||||
|
onToggleLayout: () => void;
|
||||||
|
onChangeCellView: (id: INode['id'], flow: FlowDisplay) => void;
|
||||||
|
|
||||||
useFlowPagination({ isLoading });
|
searchText: string;
|
||||||
|
searchTotal: number;
|
||||||
|
searchIsLoading: boolean;
|
||||||
|
searchResults: INode[];
|
||||||
|
onSearchChange: (text: string) => void;
|
||||||
|
onSearchLoadMore: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
const onLoadMoreSearch = useCallback(() => {
|
const FlowLayout: FC<Props> = ({
|
||||||
if (search.is_loading_more) return;
|
updates,
|
||||||
dispatch(flowLoadMoreSearch());
|
heroes,
|
||||||
}, [search.is_loading_more, dispatch]);
|
recent,
|
||||||
|
nodes,
|
||||||
const onChangeSearch = useCallback(
|
user,
|
||||||
(text: string) => {
|
isFluid,
|
||||||
dispatch(flowChangeSearch({ text }));
|
onToggleLayout,
|
||||||
},
|
onChangeCellView,
|
||||||
[dispatch]
|
|
||||||
);
|
|
||||||
|
|
||||||
const cumulativeUpdates = useMemo(() => [...updated, ...labUpdates].slice(0, 10), [
|
|
||||||
updated,
|
|
||||||
labUpdates,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const onChangeCellView = useCallback(
|
|
||||||
(id: INode['id'], val: FlowDisplay) => dispatch(flowSetCellView(id, val)),
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
searchText,
|
||||||
|
searchTotal,
|
||||||
|
searchIsLoading,
|
||||||
|
searchResults,
|
||||||
|
onSearchChange,
|
||||||
|
onSearchLoadMore,
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(styles.container, { [styles.fluid]: isFluid })}>
|
<div className={classNames(styles.container)}>
|
||||||
<div className={styles.grid}>
|
<div className={styles.grid}>
|
||||||
<div className={styles.hero}>
|
<div className={styles.hero}>
|
||||||
<FlowSwiperHero heroes={heroes} />
|
<FlowSwiperHero heroes={heroes} />
|
||||||
|
@ -63,13 +52,16 @@ const FlowLayout: FC = () => {
|
||||||
|
|
||||||
<div className={styles.stamp}>
|
<div className={styles.stamp}>
|
||||||
<FlowStamp
|
<FlowStamp
|
||||||
recent={recent}
|
|
||||||
updated={cumulativeUpdates}
|
|
||||||
search={search}
|
|
||||||
isFluid={isFluid}
|
isFluid={isFluid}
|
||||||
onSearchChange={onChangeSearch}
|
recent={recent}
|
||||||
onLoadMore={onLoadMoreSearch}
|
updated={updates}
|
||||||
toggleLayout={toggleLayout}
|
searchText={searchText}
|
||||||
|
searchIsLoading={searchIsLoading}
|
||||||
|
searchTotal={searchTotal}
|
||||||
|
searchResults={searchResults}
|
||||||
|
onSearchChange={onSearchChange}
|
||||||
|
onSearchLoadMore={onSearchLoadMore}
|
||||||
|
onToggleLayout={onToggleLayout}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
34
src/pages/index.tsx
Normal file
34
src/pages/index.tsx
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { FlowLayout } from '~/layouts/FlowLayout';
|
||||||
|
import { useFlow } from '~/utils/hooks/flow/useFlow';
|
||||||
|
import { useSearch } from '~/utils/hooks/search/useSearch';
|
||||||
|
import { useUser } from '~/utils/hooks/user/userUser';
|
||||||
|
|
||||||
|
interface Props {}
|
||||||
|
|
||||||
|
const FlowPage: FC<Props> = () => {
|
||||||
|
const { updates, nodes, heroes, recent, isFluid, toggleLayout, onChangeCellView } = useFlow();
|
||||||
|
const user = useUser();
|
||||||
|
const { search, onSearchLoadMore, onSearchChange } = useSearch();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FlowLayout
|
||||||
|
updates={updates}
|
||||||
|
recent={recent}
|
||||||
|
heroes={heroes}
|
||||||
|
nodes={nodes}
|
||||||
|
user={user}
|
||||||
|
isFluid={isFluid}
|
||||||
|
onToggleLayout={toggleLayout}
|
||||||
|
onChangeCellView={onChangeCellView}
|
||||||
|
searchResults={search.results}
|
||||||
|
searchText={search.text}
|
||||||
|
searchTotal={search.total}
|
||||||
|
searchIsLoading={search.is_loading}
|
||||||
|
onSearchLoadMore={onSearchLoadMore}
|
||||||
|
onSearchChange={onSearchChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FlowPage;
|
|
@ -1,13 +1,13 @@
|
||||||
import { createReducer } from '~/utils/reducer';
|
import { createReducer } from '~/utils/reducer';
|
||||||
import { INode, IError } from '../types';
|
import { IError, IFlowNode, INode } from '../types';
|
||||||
import { FLOW_HANDLERS } from './handlers';
|
import { FLOW_HANDLERS } from './handlers';
|
||||||
|
|
||||||
export type IFlowState = Readonly<{
|
export type IFlowState = Readonly<{
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
nodes: INode[];
|
nodes: IFlowNode[];
|
||||||
heroes: Partial<INode>[];
|
heroes: IFlowNode[];
|
||||||
recent: Partial<INode>[];
|
recent: IFlowNode[];
|
||||||
updated: Partial<INode>[];
|
updated: IFlowNode[];
|
||||||
search: {
|
search: {
|
||||||
text: string;
|
text: string;
|
||||||
results: INode[];
|
results: INode[];
|
||||||
|
|
|
@ -147,6 +147,11 @@ export interface INode {
|
||||||
commented_at?: string;
|
commented_at?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type IFlowNode = Pick<
|
||||||
|
INode,
|
||||||
|
'id' | 'flow' | 'description' | 'title' | 'thumbnail' | 'created_at'
|
||||||
|
>;
|
||||||
|
|
||||||
export interface IComment {
|
export interface IComment {
|
||||||
id: number;
|
id: number;
|
||||||
text: string;
|
text: string;
|
||||||
|
|
27
src/utils/hooks/flow/useFlow.ts
Normal file
27
src/utils/hooks/flow/useFlow.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
|
import { selectFlow } from '~/redux/flow/selectors';
|
||||||
|
import { useFlowLayout } from '~/utils/hooks/flow/useFlowLayout';
|
||||||
|
import { selectLabUpdatesNodes } from '~/redux/lab/selectors';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { useFlowPagination } from '~/utils/hooks/flow/useFlowPagination';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { FlowDisplay, INode } from '~/redux/types';
|
||||||
|
import { flowSetCellView } from '~/redux/flow/actions';
|
||||||
|
|
||||||
|
export const useFlow = () => {
|
||||||
|
const { nodes, heroes, recent, updated, isLoading } = useShallowSelect(selectFlow);
|
||||||
|
const { isFluid, toggleLayout } = useFlowLayout();
|
||||||
|
const labUpdates = useShallowSelect(selectLabUpdatesNodes);
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useFlowPagination({ isLoading });
|
||||||
|
|
||||||
|
const updates = useMemo(() => [...updated, ...labUpdates].slice(0, 10), [updated, labUpdates]);
|
||||||
|
|
||||||
|
const onChangeCellView = useCallback(
|
||||||
|
(id: INode['id'], val: FlowDisplay) => dispatch(flowSetCellView(id, val)),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { nodes, heroes, recent, updates, isFluid, toggleLayout, onChangeCellView };
|
||||||
|
};
|
24
src/utils/hooks/search/useSearch.ts
Normal file
24
src/utils/hooks/search/useSearch.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { flowChangeSearch, flowLoadMoreSearch } from '~/redux/flow/actions';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||||
|
import { selectFlow } from '~/redux/flow/selectors';
|
||||||
|
|
||||||
|
export const useSearch = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const { search } = useShallowSelect(selectFlow);
|
||||||
|
|
||||||
|
const onSearchLoadMore = useCallback(() => {
|
||||||
|
if (search.is_loading_more) return;
|
||||||
|
dispatch(flowLoadMoreSearch());
|
||||||
|
}, [search.is_loading_more, dispatch]);
|
||||||
|
|
||||||
|
const onSearchChange = useCallback(
|
||||||
|
(text: string) => {
|
||||||
|
dispatch(flowChangeSearch({ text }));
|
||||||
|
},
|
||||||
|
[dispatch]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { onSearchChange, onSearchLoadMore, search };
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue