mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
getting node results for search
This commit is contained in:
parent
41a35f1490
commit
94ac596b92
15 changed files with 308 additions and 164 deletions
|
@ -1,4 +1,4 @@
|
||||||
import React, { FC, useState, useCallback, FormEvent } from 'react';
|
import React, { FC } from 'react';
|
||||||
import * as styles from './styles.scss';
|
import * as styles from './styles.scss';
|
||||||
import { IFlowState } from '~/redux/flow/reducer';
|
import { IFlowState } from '~/redux/flow/reducer';
|
||||||
import { getURL, getPrettyDate } from '~/utils/dom';
|
import { getURL, getPrettyDate } from '~/utils/dom';
|
||||||
|
@ -6,7 +6,6 @@ import { Link } from 'react-router-dom';
|
||||||
import { URLS, PRESETS } from '~/constants/urls';
|
import { URLS, PRESETS } from '~/constants/urls';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { NodeRelatedItem } from '~/components/node/NodeRelatedItem';
|
import { NodeRelatedItem } from '~/components/node/NodeRelatedItem';
|
||||||
import { InputText } from '~/components/input/InputText';
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
recent: IFlowState['recent'];
|
recent: IFlowState['recent'];
|
||||||
|
@ -14,55 +13,39 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const FlowRecent: FC<IProps> = ({ recent, updated }) => {
|
const FlowRecent: FC<IProps> = ({ recent, updated }) => {
|
||||||
const [search, setSearch] = useState('');
|
|
||||||
|
|
||||||
const onSearchSubmit = useCallback((event: FormEvent) => {
|
|
||||||
event.preventDefault();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<>
|
||||||
<form className={styles.search} onSubmit={onSearchSubmit}>
|
{updated &&
|
||||||
<InputText title="Поиск" value={search} handler={setSearch} />
|
updated.slice(0, 20).map(node => (
|
||||||
</form>
|
<Link key={node.id} className={styles.item} to={URLS.NODE_URL(node.id)}>
|
||||||
|
<div
|
||||||
|
className={classNames(styles.thumb, styles.new)}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url("${getURL({ url: node.thumbnail }, PRESETS.avatar)}")`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className={styles.info}>
|
||||||
<div className={styles.grid_label}>
|
<div className={styles.title}>{node.title}</div>
|
||||||
<span>Что нового?</span>
|
<div className={styles.comment}>{getPrettyDate(node.created_at)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
|
||||||
{updated &&
|
{recent &&
|
||||||
updated.slice(0, 20).map(node => (
|
recent.slice(0, 20).map(node => (
|
||||||
<Link key={node.id} className={styles.item} to={URLS.NODE_URL(node.id)}>
|
<Link key={node.id} className={styles.item} to={URLS.NODE_URL(node.id)}>
|
||||||
<div
|
<div className={styles.thumb}>
|
||||||
className={classNames(styles.thumb, styles.new)}
|
<NodeRelatedItem item={node} />
|
||||||
style={{
|
</div>
|
||||||
backgroundImage: `url("${getURL({ url: node.thumbnail }, PRESETS.avatar)}")`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className={styles.info}>
|
<div className={styles.info}>
|
||||||
<div className={styles.title}>{node.title}</div>
|
<div className={styles.title}>{node.title}</div>
|
||||||
<div className={styles.comment}>{getPrettyDate(node.created_at)}</div>
|
<div className={styles.comment}>{getPrettyDate(node.created_at)}</div>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
))}
|
||||||
|
</>
|
||||||
{recent &&
|
|
||||||
recent.slice(0, 20).map(node => (
|
|
||||||
<Link key={node.id} className={styles.item} to={URLS.NODE_URL(node.id)}>
|
|
||||||
<div className={styles.thumb}>
|
|
||||||
<NodeRelatedItem item={node} />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.info}>
|
|
||||||
<div className={styles.title}>{node.title}</div>
|
|
||||||
<div className={styles.comment}>{getPrettyDate(node.created_at)}</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,3 @@
|
||||||
.grid {
|
|
||||||
display: flex;
|
|
||||||
justify-content: stretch;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
background: $content_bg;
|
|
||||||
padding: $gap;
|
|
||||||
border-radius: 0 0 $radius $radius;
|
|
||||||
|
|
||||||
@include outer_shadow();
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid_label {
|
|
||||||
margin-bottom: $gap;
|
|
||||||
@include title_with_line();
|
|
||||||
color: transparentize(white, $amount: 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search {
|
|
||||||
background: lighten($content_bg, 4%);
|
|
||||||
border-radius: $radius $radius 0 0;
|
|
||||||
padding: $gap;
|
|
||||||
|
|
||||||
@include outer_shadow();
|
|
||||||
}
|
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
21
src/components/flow/FlowSearchResults/index.tsx
Normal file
21
src/components/flow/FlowSearchResults/index.tsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import styles from './styles.scss';
|
||||||
|
import { IFlowState } from '~/redux/flow/reducer';
|
||||||
|
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
search: IFlowState['search'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlowSearchResults: FC<IProps> = ({ search }) => {
|
||||||
|
if (search.is_loading) {
|
||||||
|
return (
|
||||||
|
<div className={styles.loading}>
|
||||||
|
<LoaderCircle size={64} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <div className={styles.wrap}>SEARCH</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { FlowSearchResults };
|
12
src/components/flow/FlowSearchResults/styles.scss
Normal file
12
src/components/flow/FlowSearchResults/styles.scss
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
.wrap {
|
||||||
|
flex: 1;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex: 1;
|
||||||
|
opacity: 0.3;
|
||||||
|
}
|
60
src/components/flow/FlowStamp/index.tsx
Normal file
60
src/components/flow/FlowStamp/index.tsx
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
import React, { FC, useCallback, FormEvent } from 'react';
|
||||||
|
import { IFlowState } from '~/redux/flow/reducer';
|
||||||
|
import { InputText } from '~/components/input/InputText';
|
||||||
|
import { FlowRecent } from '../FlowRecent';
|
||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
import * as styles from './styles.scss';
|
||||||
|
import * as FLOW_ACTIONS from '~/redux/flow/actions';
|
||||||
|
import { FlowSearchResults } from '../FlowSearchResults';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
recent: IFlowState['recent'];
|
||||||
|
updated: IFlowState['updated'];
|
||||||
|
search: IFlowState['search'];
|
||||||
|
flowChangeSearch: typeof FLOW_ACTIONS.flowChangeSearch;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FlowStamp: FC<IProps> = ({ recent, updated, search, flowChangeSearch }) => {
|
||||||
|
const onSearchChange = useCallback((text: string) => flowChangeSearch({ text }), [
|
||||||
|
flowChangeSearch,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const onSearchSubmit = useCallback((event: FormEvent) => {
|
||||||
|
event.preventDefault();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrap}>
|
||||||
|
<form className={styles.search} onSubmit={onSearchSubmit}>
|
||||||
|
<InputText title="Поиск" value={search.text} handler={onSearchChange} />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div className={styles.grid}>
|
||||||
|
{search.text ? (
|
||||||
|
<>
|
||||||
|
<div className={styles.label}>
|
||||||
|
<span className={styles.label_text}>Результаты поиска</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.items}>
|
||||||
|
<FlowSearchResults search={search} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<div className={styles.label}>
|
||||||
|
<span className={styles.label_text}>Что нового?</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.items}>
|
||||||
|
<FlowRecent updated={updated} recent={recent} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { FlowStamp };
|
55
src/components/flow/FlowStamp/styles.scss
Normal file
55
src/components/flow/FlowStamp/styles.scss
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
.wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
background: $content_bg;
|
||||||
|
border-radius: $radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
justify-content: stretch;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
border-radius: $radius;
|
||||||
|
|
||||||
|
@include outer_shadow();
|
||||||
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
padding: 0 $gap 0 $gap;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
min-width: 0;
|
||||||
|
padding: $gap;
|
||||||
|
border-radius: $radius;
|
||||||
|
|
||||||
|
@include title_with_line();
|
||||||
|
|
||||||
|
color: transparentize(white, $amount: 0.8);
|
||||||
|
|
||||||
|
&_search {
|
||||||
|
color: white;
|
||||||
|
padding-left: $gap * 1.2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.label_text {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search {
|
||||||
|
background: lighten($content_bg, 4%);
|
||||||
|
border-radius: $radius $radius 0 0;
|
||||||
|
padding: $gap;
|
||||||
|
|
||||||
|
@include outer_shadow();
|
||||||
|
}
|
|
@ -23,7 +23,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
margin: 0 $gap;
|
margin-left: $gap / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.placeholder {
|
.placeholder {
|
||||||
|
|
|
@ -30,4 +30,5 @@ export const API = {
|
||||||
`/node/${id}/comment/${comment_id}/lock`,
|
`/node/${id}/comment/${comment_id}/lock`,
|
||||||
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
SET_CELL_VIEW: (id: INode['id']) => `/node/${id}/cell-view`,
|
||||||
},
|
},
|
||||||
|
SEARCH: '/search',
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,11 +7,12 @@ import * as FLOW_ACTIONS from '~/redux/flow/actions';
|
||||||
import pick from 'ramda/es/pick';
|
import pick from 'ramda/es/pick';
|
||||||
import { selectUser } from '~/redux/auth/selectors';
|
import { selectUser } from '~/redux/auth/selectors';
|
||||||
import { FlowHero } from '~/components/flow/FlowHero';
|
import { FlowHero } from '~/components/flow/FlowHero';
|
||||||
import { FlowRecent } from '~/components/flow/FlowRecent';
|
|
||||||
import styles from './styles.scss';
|
import styles from './styles.scss';
|
||||||
|
import { IState } from '~/redux/store';
|
||||||
|
import { FlowStamp } from '~/components/flow/FlowStamp';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state: IState) => ({
|
||||||
flow: pick(['nodes', 'heroes', 'recent', 'updated', 'is_loading'], selectFlow(state)),
|
flow: pick(['nodes', 'heroes', 'recent', 'updated', 'is_loading', 'search'], selectFlow(state)),
|
||||||
user: pick(['role', 'id'], selectUser(state)),
|
user: pick(['role', 'id'], selectUser(state)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -19,16 +20,18 @@ const mapDispatchToProps = {
|
||||||
nodeGotoNode: NODE_ACTIONS.nodeGotoNode,
|
nodeGotoNode: NODE_ACTIONS.nodeGotoNode,
|
||||||
flowSetCellView: FLOW_ACTIONS.flowSetCellView,
|
flowSetCellView: FLOW_ACTIONS.flowSetCellView,
|
||||||
flowGetMore: FLOW_ACTIONS.flowGetMore,
|
flowGetMore: FLOW_ACTIONS.flowGetMore,
|
||||||
|
flowChangeSearch: FLOW_ACTIONS.flowChangeSearch,
|
||||||
};
|
};
|
||||||
|
|
||||||
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
type IProps = ReturnType<typeof mapStateToProps> & typeof mapDispatchToProps & {};
|
||||||
|
|
||||||
const FlowLayoutUnconnected: FC<IProps> = ({
|
const FlowLayoutUnconnected: FC<IProps> = ({
|
||||||
flow: { nodes, heroes, recent, updated, is_loading },
|
flow: { nodes, heroes, recent, updated, is_loading, search },
|
||||||
user,
|
user,
|
||||||
nodeGotoNode,
|
nodeGotoNode,
|
||||||
flowSetCellView,
|
flowSetCellView,
|
||||||
flowGetMore,
|
flowGetMore,
|
||||||
|
flowChangeSearch,
|
||||||
}) => {
|
}) => {
|
||||||
const loadMore = useCallback(() => {
|
const loadMore = useCallback(() => {
|
||||||
const pos = window.scrollY + window.innerHeight - document.body.scrollHeight;
|
const pos = window.scrollY + window.innerHeight - document.body.scrollHeight;
|
||||||
|
@ -51,7 +54,12 @@ const FlowLayoutUnconnected: FC<IProps> = ({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.stamp}>
|
<div className={styles.stamp}>
|
||||||
<FlowRecent recent={recent} updated={updated} />
|
<FlowStamp
|
||||||
|
recent={recent}
|
||||||
|
updated={updated}
|
||||||
|
flowChangeSearch={flowChangeSearch}
|
||||||
|
search={search}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FlowGrid
|
<FlowGrid
|
||||||
|
|
|
@ -1,43 +1,48 @@
|
||||||
import { FLOW_ACTIONS } from "./constants";
|
import { FLOW_ACTIONS } from './constants';
|
||||||
import { IFlowState } from "./reducer";
|
import { IFlowState } from './reducer';
|
||||||
import { INode } from "../types";
|
import { INode } from '../types';
|
||||||
|
|
||||||
export const flowSetNodes = (nodes: IFlowState["nodes"]) => ({
|
export const flowSetNodes = (nodes: IFlowState['nodes']) => ({
|
||||||
nodes,
|
nodes,
|
||||||
type: FLOW_ACTIONS.SET_NODES
|
type: FLOW_ACTIONS.SET_NODES,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowSetHeroes = (heroes: IFlowState["heroes"]) => ({
|
export const flowSetHeroes = (heroes: IFlowState['heroes']) => ({
|
||||||
heroes,
|
heroes,
|
||||||
type: FLOW_ACTIONS.SET_HEROES
|
type: FLOW_ACTIONS.SET_HEROES,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowSetRecent = (recent: IFlowState["recent"]) => ({
|
export const flowSetRecent = (recent: IFlowState['recent']) => ({
|
||||||
recent,
|
recent,
|
||||||
type: FLOW_ACTIONS.SET_RECENT
|
type: FLOW_ACTIONS.SET_RECENT,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowSetUpdated = (updated: IFlowState["updated"]) => ({
|
export const flowSetUpdated = (updated: IFlowState['updated']) => ({
|
||||||
updated,
|
updated,
|
||||||
type: FLOW_ACTIONS.SET_UPDATED
|
type: FLOW_ACTIONS.SET_UPDATED,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowSetCellView = (id: INode["id"], flow: INode["flow"]) => ({
|
export const flowSetCellView = (id: INode['id'], flow: INode['flow']) => ({
|
||||||
type: FLOW_ACTIONS.SET_CELL_VIEW,
|
type: FLOW_ACTIONS.SET_CELL_VIEW,
|
||||||
id,
|
id,
|
||||||
flow
|
flow,
|
||||||
});
|
|
||||||
|
|
||||||
export const flowSetRange = (range: IFlowState["range"]) => ({
|
|
||||||
range,
|
|
||||||
type: FLOW_ACTIONS.SET_RANGE
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowGetMore = () => ({
|
export const flowGetMore = () => ({
|
||||||
type: FLOW_ACTIONS.GET_MORE
|
type: FLOW_ACTIONS.GET_MORE,
|
||||||
});
|
});
|
||||||
|
|
||||||
export const flowSetFlow = (data: Partial<IFlowState>) => ({
|
export const flowSetFlow = (data: Partial<IFlowState>) => ({
|
||||||
type: FLOW_ACTIONS.SET_FLOW,
|
type: FLOW_ACTIONS.SET_FLOW,
|
||||||
data
|
data,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const flowSetSearch = (search: Partial<IFlowState['search']>) => ({
|
||||||
|
type: FLOW_ACTIONS.SET_SEARCH,
|
||||||
|
search,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const flowChangeSearch = (search: Partial<IFlowState['search']>) => ({
|
||||||
|
type: FLOW_ACTIONS.CHANGE_SEARCH,
|
||||||
|
search,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
import {
|
import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api';
|
||||||
api,
|
import { INode, IResultWithStatus } from '../types';
|
||||||
configWithToken,
|
import { API } from '~/constants/api';
|
||||||
resultMiddleware,
|
import { flowSetCellView } from '~/redux/flow/actions';
|
||||||
errorMiddleware
|
|
||||||
} from "~/utils/api";
|
|
||||||
import { INode, IResultWithStatus } from "../types";
|
|
||||||
import { API } from "~/constants/api";
|
|
||||||
import { flowSetCellView } from "~/redux/flow/actions";
|
|
||||||
|
|
||||||
export const postNode = ({
|
export const postNode = ({
|
||||||
access,
|
access,
|
||||||
node
|
node,
|
||||||
}: {
|
}: {
|
||||||
access: string;
|
access: string;
|
||||||
node: INode;
|
node: INode;
|
||||||
|
@ -20,24 +15,26 @@ export const postNode = ({
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
// export const getNodes = ({
|
|
||||||
// from = null
|
|
||||||
// }: {
|
|
||||||
// from: string;
|
|
||||||
// }): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
|
||||||
// api
|
|
||||||
// .get(API.NODE.GET, { params: { from } })
|
|
||||||
// .then(resultMiddleware)
|
|
||||||
// .catch(errorMiddleware);
|
|
||||||
|
|
||||||
export const postCellView = ({
|
export const postCellView = ({
|
||||||
id,
|
id,
|
||||||
flow,
|
flow,
|
||||||
access
|
access,
|
||||||
}: ReturnType<typeof flowSetCellView> & { access: string }): Promise<
|
}: ReturnType<typeof flowSetCellView> & { access: string }): Promise<IResultWithStatus<{
|
||||||
IResultWithStatus<{ is_liked: INode["is_liked"] }>
|
is_liked: INode['is_liked'];
|
||||||
> =>
|
}>> =>
|
||||||
api
|
api
|
||||||
.post(API.NODE.SET_CELL_VIEW(id), { flow }, configWithToken(access))
|
.post(API.NODE.SET_CELL_VIEW(id), { flow }, configWithToken(access))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
|
export const getSearchResults = ({
|
||||||
|
access,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
access: string;
|
||||||
|
text: string;
|
||||||
|
}): Promise<IResultWithStatus<{ nodes: INode[]; total: number }>> =>
|
||||||
|
api
|
||||||
|
.get(API.SEARCH, configWithToken(access, { params: { text } }))
|
||||||
|
.then(resultMiddleware)
|
||||||
|
.catch(errorMiddleware);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const prefix = "FLOW.";
|
const prefix = 'FLOW.';
|
||||||
|
|
||||||
export const FLOW_ACTIONS = {
|
export const FLOW_ACTIONS = {
|
||||||
GET_FLOW: `${prefix}GET_FLOW`,
|
GET_FLOW: `${prefix}GET_FLOW`,
|
||||||
|
@ -9,5 +9,8 @@ export const FLOW_ACTIONS = {
|
||||||
SET_UPDATED: `${prefix}SET_UPDATED`,
|
SET_UPDATED: `${prefix}SET_UPDATED`,
|
||||||
SET_RANGE: `${prefix}SET_RANGE`,
|
SET_RANGE: `${prefix}SET_RANGE`,
|
||||||
SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`,
|
SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`,
|
||||||
GET_MORE: `${prefix}GET_MORE`
|
GET_MORE: `${prefix}GET_MORE`,
|
||||||
|
|
||||||
|
SET_SEARCH: `${prefix}SET_SEARCH`,
|
||||||
|
CHANGE_SEARCH: `${prefix}CHANGE_SEARCH`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,46 +1,41 @@
|
||||||
import assocPath from "ramda/es/assocPath";
|
import assocPath from 'ramda/es/assocPath';
|
||||||
import { FLOW_ACTIONS } from "./constants";
|
import { FLOW_ACTIONS } from './constants';
|
||||||
import {
|
import {
|
||||||
flowSetNodes,
|
flowSetNodes,
|
||||||
flowSetHeroes,
|
flowSetHeroes,
|
||||||
flowSetRecent,
|
flowSetRecent,
|
||||||
flowSetUpdated,
|
flowSetUpdated,
|
||||||
flowSetRange,
|
flowSetFlow,
|
||||||
flowSetFlow
|
flowSetSearch,
|
||||||
} from "./actions";
|
} from './actions';
|
||||||
import { IFlowState } from "./reducer";
|
import { IFlowState } from './reducer';
|
||||||
|
|
||||||
const setNodes = (
|
const setNodes = (state: IFlowState, { nodes }: ReturnType<typeof flowSetNodes>) =>
|
||||||
state: IFlowState,
|
assocPath(['nodes'], nodes, state);
|
||||||
{ nodes }: ReturnType<typeof flowSetNodes>
|
|
||||||
) => assocPath(["nodes"], nodes, state);
|
|
||||||
|
|
||||||
const setHeroes = (
|
const setHeroes = (state: IFlowState, { heroes }: ReturnType<typeof flowSetHeroes>) =>
|
||||||
state: IFlowState,
|
assocPath(['heroes'], heroes, state);
|
||||||
{ heroes }: ReturnType<typeof flowSetHeroes>
|
|
||||||
) => assocPath(["heroes"], heroes, state);
|
|
||||||
|
|
||||||
const setRecent = (
|
const setRecent = (state: IFlowState, { recent }: ReturnType<typeof flowSetRecent>) =>
|
||||||
state: IFlowState,
|
assocPath(['recent'], recent, state);
|
||||||
{ recent }: ReturnType<typeof flowSetRecent>
|
|
||||||
) => assocPath(["recent"], recent, state);
|
|
||||||
|
|
||||||
const setUpdated = (
|
const setUpdated = (state: IFlowState, { updated }: ReturnType<typeof flowSetUpdated>) =>
|
||||||
state: IFlowState,
|
assocPath(['updated'], updated, state);
|
||||||
{ updated }: ReturnType<typeof flowSetUpdated>
|
|
||||||
) => assocPath(["updated"], updated, state);
|
|
||||||
|
|
||||||
const setRange = (
|
const setFlow = (state: IFlowState, { data }: ReturnType<typeof flowSetFlow>): IFlowState => ({
|
||||||
state: IFlowState,
|
...state,
|
||||||
{ range }: ReturnType<typeof flowSetRange>
|
...data,
|
||||||
) => assocPath(["range"], range, state);
|
});
|
||||||
|
|
||||||
const setFlow = (
|
const setSearch = (
|
||||||
state: IFlowState,
|
state: IFlowState,
|
||||||
{ data }: ReturnType<typeof flowSetFlow>
|
{ search }: ReturnType<typeof flowSetSearch>
|
||||||
): IFlowState => ({
|
): IFlowState => ({
|
||||||
...state,
|
...state,
|
||||||
...data
|
search: {
|
||||||
|
...state.search,
|
||||||
|
...search,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export const FLOW_HANDLERS = {
|
export const FLOW_HANDLERS = {
|
||||||
|
@ -48,6 +43,6 @@ export const FLOW_HANDLERS = {
|
||||||
[FLOW_ACTIONS.SET_HEROES]: setHeroes,
|
[FLOW_ACTIONS.SET_HEROES]: setHeroes,
|
||||||
[FLOW_ACTIONS.SET_RECENT]: setRecent,
|
[FLOW_ACTIONS.SET_RECENT]: setRecent,
|
||||||
[FLOW_ACTIONS.SET_UPDATED]: setUpdated,
|
[FLOW_ACTIONS.SET_UPDATED]: setUpdated,
|
||||||
[FLOW_ACTIONS.SET_RANGE]: setRange,
|
[FLOW_ACTIONS.SET_FLOW]: setFlow,
|
||||||
[FLOW_ACTIONS.SET_FLOW]: setFlow
|
[FLOW_ACTIONS.SET_SEARCH]: setSearch,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { createReducer } from "~/utils/reducer";
|
import { createReducer } from '~/utils/reducer';
|
||||||
import { INode, IError } from "../types";
|
import { INode, IError } from '../types';
|
||||||
import { FLOW_HANDLERS } from "./handlers";
|
import { FLOW_HANDLERS } from './handlers';
|
||||||
|
|
||||||
export type IFlowState = Readonly<{
|
export type IFlowState = Readonly<{
|
||||||
is_loading: boolean;
|
is_loading: boolean;
|
||||||
|
@ -8,7 +8,11 @@ export type IFlowState = Readonly<{
|
||||||
heroes: Partial<INode>[];
|
heroes: Partial<INode>[];
|
||||||
recent: Partial<INode>[];
|
recent: Partial<INode>[];
|
||||||
updated: Partial<INode>[];
|
updated: Partial<INode>[];
|
||||||
range: [string, string];
|
search: {
|
||||||
|
text: string;
|
||||||
|
is_loading: boolean;
|
||||||
|
results: INode[];
|
||||||
|
};
|
||||||
error: IError;
|
error: IError;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
@ -17,9 +21,13 @@ const INITIAL_STATE: IFlowState = {
|
||||||
heroes: [],
|
heroes: [],
|
||||||
recent: [],
|
recent: [],
|
||||||
updated: [],
|
updated: [],
|
||||||
range: [null, null], // drop it, we use realtime range calc
|
search: {
|
||||||
|
text: '',
|
||||||
|
is_loading: false,
|
||||||
|
results: [],
|
||||||
|
},
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
error: null
|
error: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createReducer(INITIAL_STATE, FLOW_HANDLERS);
|
export default createReducer(INITIAL_STATE, FLOW_HANDLERS);
|
||||||
|
|
|
@ -9,11 +9,13 @@ import {
|
||||||
flowSetRecent,
|
flowSetRecent,
|
||||||
flowSetUpdated,
|
flowSetUpdated,
|
||||||
flowSetFlow,
|
flowSetFlow,
|
||||||
|
flowChangeSearch,
|
||||||
|
flowSetSearch,
|
||||||
} from './actions';
|
} from './actions';
|
||||||
import { IResultWithStatus, INode } from '../types';
|
import { IResultWithStatus, INode, Unwrap } from '../types';
|
||||||
import { selectFlowNodes } from './selectors';
|
import { selectFlowNodes } from './selectors';
|
||||||
import { reqWrapper } from '../auth/sagas';
|
import { reqWrapper } from '../auth/sagas';
|
||||||
import { postCellView } from './api';
|
import { postCellView, getSearchResults } from './api';
|
||||||
import { IFlowState } from './reducer';
|
import { IFlowState } from './reducer';
|
||||||
import uniq from 'ramda/es/uniq';
|
import uniq from 'ramda/es/uniq';
|
||||||
|
|
||||||
|
@ -110,8 +112,28 @@ function* getMore() {
|
||||||
yield delay(1000);
|
yield delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* changeSearch({ search }: ReturnType<typeof flowChangeSearch>) {
|
||||||
|
yield put(
|
||||||
|
flowSetSearch({
|
||||||
|
...search,
|
||||||
|
is_loading: !!search.text,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!search.text) return;
|
||||||
|
|
||||||
|
yield delay(500);
|
||||||
|
|
||||||
|
const res: Unwrap<typeof getSearchResults> = yield call(reqWrapper, getSearchResults, {
|
||||||
|
...search,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
|
}
|
||||||
|
|
||||||
export default function* nodeSaga() {
|
export default function* nodeSaga() {
|
||||||
yield takeLatest([FLOW_ACTIONS.GET_FLOW, REHYDRATE], onGetFlow);
|
yield takeLatest([FLOW_ACTIONS.GET_FLOW, REHYDRATE], onGetFlow);
|
||||||
yield takeLatest(FLOW_ACTIONS.SET_CELL_VIEW, onSetCellView);
|
yield takeLatest(FLOW_ACTIONS.SET_CELL_VIEW, onSetCellView);
|
||||||
yield takeLeading(FLOW_ACTIONS.GET_MORE, getMore);
|
yield takeLeading(FLOW_ACTIONS.GET_MORE, getMore);
|
||||||
|
yield takeLatest(FLOW_ACTIONS.CHANGE_SEARCH, changeSearch);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue