mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
flow get more
This commit is contained in:
parent
f440a5f4c3
commit
971578bb21
11 changed files with 281 additions and 159 deletions
|
@ -9,6 +9,7 @@ import { flowSetCellView } from "~/redux/flow/actions";
|
||||||
import { PRESETS } from "~/constants/urls";
|
import { PRESETS } from "~/constants/urls";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
import { NODE_TYPES } from "~/redux/node/constants";
|
import { NODE_TYPES } from "~/redux/node/constants";
|
||||||
|
import { Group } from "~/components/containers/Group";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
node: INode;
|
node: INode;
|
||||||
|
@ -135,7 +136,7 @@ const Cell: FC<IProps> = ({
|
||||||
<div className={styles.text}>
|
<div className={styles.text}>
|
||||||
{title && <div className={styles.text_title}>{title}</div>}
|
{title && <div className={styles.text_title}>{title}</div>}
|
||||||
|
|
||||||
<div
|
<Group
|
||||||
dangerouslySetInnerHTML={{ __html: formatCellText(text) }}
|
dangerouslySetInnerHTML={{ __html: formatCellText(text) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -145,7 +146,7 @@ const Cell: FC<IProps> = ({
|
||||||
<div className={styles.text_only}>
|
<div className={styles.text_only}>
|
||||||
{title && <div className={styles.text_title}>{title}</div>}
|
{title && <div className={styles.text_title}>{title}</div>}
|
||||||
|
|
||||||
<div
|
<Group
|
||||||
dangerouslySetInnerHTML={{ __html: formatCellText(text) }}
|
dangerouslySetInnerHTML={{ __html: formatCellText(text) }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,30 +1,52 @@
|
||||||
import React, { FC } from 'react';
|
import React, { FC, useEffect, useCallback } from "react";
|
||||||
import { connect } from 'react-redux';
|
import { connect } from "react-redux";
|
||||||
import { FlowGrid } from '~/components/flow/FlowGrid';
|
import { FlowGrid } from "~/components/flow/FlowGrid";
|
||||||
import { selectFlow } from '~/redux/flow/selectors';
|
import { selectFlow } from "~/redux/flow/selectors";
|
||||||
import * as NODE_ACTIONS from '~/redux/node/actions';
|
import * as NODE_ACTIONS from "~/redux/node/actions";
|
||||||
import * as FLOW_ACTIONS from '~/redux/flow/actions';
|
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";
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
flow: pick(['nodes', 'heroes', 'recent', 'updated'], selectFlow(state)),
|
flow: pick(
|
||||||
user: pick(['role', 'id'], selectUser(state)),
|
["nodes", "heroes", "recent", "updated", "is_loading"],
|
||||||
|
selectFlow(state)
|
||||||
|
),
|
||||||
|
user: pick(["role", "id"], selectUser(state))
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
nodeGotoNode: NODE_ACTIONS.nodeGotoNode,
|
nodeGotoNode: NODE_ACTIONS.nodeGotoNode,
|
||||||
flowSetCellView: FLOW_ACTIONS.flowSetCellView,
|
flowSetCellView: FLOW_ACTIONS.flowSetCellView,
|
||||||
|
flowGetMore: FLOW_ACTIONS.flowGetMore
|
||||||
};
|
};
|
||||||
|
|
||||||
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 },
|
flow: { nodes, heroes, recent, updated, is_loading },
|
||||||
user,
|
user,
|
||||||
nodeGotoNode,
|
nodeGotoNode,
|
||||||
flowSetCellView,
|
flowSetCellView,
|
||||||
}) => (
|
flowGetMore
|
||||||
|
}) => {
|
||||||
|
const loadMore = useCallback(() => {
|
||||||
|
const pos =
|
||||||
|
window.scrollY + window.innerHeight - document.body.scrollHeight;
|
||||||
|
|
||||||
|
if (is_loading || pos < -600) return;
|
||||||
|
|
||||||
|
flowGetMore();
|
||||||
|
}, [flowGetMore, is_loading]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
window.addEventListener("scroll", loadMore);
|
||||||
|
|
||||||
|
return () => window.removeEventListener("scroll", loadMore);
|
||||||
|
}, [loadMore]);
|
||||||
|
|
||||||
|
return (
|
||||||
<FlowGrid
|
<FlowGrid
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
heroes={heroes}
|
heroes={heroes}
|
||||||
|
@ -35,6 +57,7 @@ const FlowLayoutUnconnected: FC<IProps> = ({
|
||||||
onChangeCellView={flowSetCellView}
|
onChangeCellView={flowSetCellView}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const FlowLayout = connect(
|
const FlowLayout = connect(
|
||||||
mapStateToProps,
|
mapStateToProps,
|
||||||
|
|
|
@ -21,11 +21,10 @@ render(
|
||||||
/*
|
/*
|
||||||
|
|
||||||
[Stage 0]:
|
[Stage 0]:
|
||||||
- fix: text nodes cell has no preview (actually, that's a problem of brief)
|
- check if email is registered at social login
|
||||||
|
- friendship
|
||||||
- password restore
|
- password restore
|
||||||
- signup?
|
- signup?
|
||||||
- better node brief update
|
|
||||||
- flow updates
|
- flow updates
|
||||||
- flow infinite scroll
|
- flow infinite scroll
|
||||||
- avatar upload
|
- avatar upload
|
||||||
|
@ -43,6 +42,8 @@ render(
|
||||||
- comment editing
|
- comment editing
|
||||||
|
|
||||||
Done:
|
Done:
|
||||||
|
- better node brief update
|
||||||
|
- fix: text nodes cell has no preview (actually, that's a problem of brief)
|
||||||
- relocate files
|
- relocate files
|
||||||
- backend: exclude node covers on import
|
- backend: exclude node covers on import
|
||||||
- profile editing
|
- profile editing
|
||||||
|
@ -59,5 +60,4 @@ Done:
|
||||||
- fix: select node and edit it. All images will be not loaded
|
- fix: select node and edit it. All images will be not loaded
|
||||||
- fix: text nodes cell not clickable
|
- fix: text nodes cell not clickable
|
||||||
- fix: text nodes should not have 'no comments yet badge
|
- fix: text nodes should not have 'no comments yet badge
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,29 +1,43 @@
|
||||||
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 = () => ({
|
||||||
|
type: FLOW_ACTIONS.GET_MORE
|
||||||
|
});
|
||||||
|
|
||||||
|
export const flowSetFlow = (data: Partial<IFlowState>) => ({
|
||||||
|
type: FLOW_ACTIONS.SET_FLOW,
|
||||||
|
data
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api';
|
import {
|
||||||
import { INode, IResultWithStatus } from '../types';
|
api,
|
||||||
import { API } from '~/constants/api';
|
configWithToken,
|
||||||
import { flowSetCellView } from '~/redux/flow/actions';
|
resultMiddleware,
|
||||||
|
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;
|
||||||
|
@ -15,22 +20,22 @@ export const postNode = ({
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
export const getNodes = ({
|
// export const getNodes = ({
|
||||||
skip = 0,
|
// from = null
|
||||||
}: {
|
// }: {
|
||||||
skip: number;
|
// from: string;
|
||||||
}): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
// }): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
||||||
api
|
// api
|
||||||
.get(API.NODE.GET, { params: { skip } })
|
// .get(API.NODE.GET, { params: { from } })
|
||||||
.then(resultMiddleware)
|
// .then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
// .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<{ is_liked: INode['is_liked'] }>
|
IResultWithStatus<{ 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))
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
const prefix = 'FLOW.';
|
const prefix = "FLOW.";
|
||||||
|
|
||||||
export const FLOW_ACTIONS = {
|
export const FLOW_ACTIONS = {
|
||||||
GET_FLOW: `${prefix}GET_FLOW`,
|
GET_FLOW: `${prefix}GET_FLOW`,
|
||||||
|
SET_FLOW: `${prefix}SET_FLOW`,
|
||||||
SET_NODES: `${prefix}SET_NODES`,
|
SET_NODES: `${prefix}SET_NODES`,
|
||||||
SET_HEROES: `${prefix}SET_HEROES`,
|
SET_HEROES: `${prefix}SET_HEROES`,
|
||||||
SET_RECENT: `${prefix}SET_RECENT`,
|
SET_RECENT: `${prefix}SET_RECENT`,
|
||||||
SET_UPDATED: `${prefix}SET_UPDATED`,
|
SET_UPDATED: `${prefix}SET_UPDATED`,
|
||||||
|
SET_RANGE: `${prefix}SET_RANGE`,
|
||||||
SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`,
|
SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`,
|
||||||
|
GET_MORE: `${prefix}GET_MORE`
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,23 +1,53 @@
|
||||||
import assocPath from 'ramda/es/assocPath';
|
import assocPath from "ramda/es/assocPath";
|
||||||
import { FLOW_ACTIONS } from './constants';
|
import { FLOW_ACTIONS } from "./constants";
|
||||||
import { flowSetNodes, flowSetHeroes, flowSetRecent, flowSetUpdated } from './actions';
|
import {
|
||||||
import { IFlowState } from './reducer';
|
flowSetNodes,
|
||||||
|
flowSetHeroes,
|
||||||
|
flowSetRecent,
|
||||||
|
flowSetUpdated,
|
||||||
|
flowSetRange,
|
||||||
|
flowSetFlow
|
||||||
|
} from "./actions";
|
||||||
|
import { IFlowState } from "./reducer";
|
||||||
|
|
||||||
const setNodes = (state: IFlowState, { nodes }: ReturnType<typeof flowSetNodes>) =>
|
const setNodes = (
|
||||||
assocPath(['nodes'], nodes, state);
|
state: IFlowState,
|
||||||
|
{ nodes }: ReturnType<typeof flowSetNodes>
|
||||||
|
) => assocPath(["nodes"], nodes, state);
|
||||||
|
|
||||||
const setHeroes = (state: IFlowState, { heroes }: ReturnType<typeof flowSetHeroes>) =>
|
const setHeroes = (
|
||||||
assocPath(['heroes'], heroes, state);
|
state: IFlowState,
|
||||||
|
{ heroes }: ReturnType<typeof flowSetHeroes>
|
||||||
|
) => assocPath(["heroes"], heroes, state);
|
||||||
|
|
||||||
const setRecent = (state: IFlowState, { recent }: ReturnType<typeof flowSetRecent>) =>
|
const setRecent = (
|
||||||
assocPath(['recent'], recent, state);
|
state: IFlowState,
|
||||||
|
{ recent }: ReturnType<typeof flowSetRecent>
|
||||||
|
) => assocPath(["recent"], recent, state);
|
||||||
|
|
||||||
const setUpdated = (state: IFlowState, { updated }: ReturnType<typeof flowSetUpdated>) =>
|
const setUpdated = (
|
||||||
assocPath(['updated'], updated, state);
|
state: IFlowState,
|
||||||
|
{ updated }: ReturnType<typeof flowSetUpdated>
|
||||||
|
) => assocPath(["updated"], updated, state);
|
||||||
|
|
||||||
|
const setRange = (
|
||||||
|
state: IFlowState,
|
||||||
|
{ range }: ReturnType<typeof flowSetRange>
|
||||||
|
) => assocPath(["range"], range, state);
|
||||||
|
|
||||||
|
const setFlow = (
|
||||||
|
state: IFlowState,
|
||||||
|
{ data }: ReturnType<typeof flowSetFlow>
|
||||||
|
): IFlowState => ({
|
||||||
|
...state,
|
||||||
|
...data
|
||||||
|
});
|
||||||
|
|
||||||
export const FLOW_HANDLERS = {
|
export const FLOW_HANDLERS = {
|
||||||
[FLOW_ACTIONS.SET_NODES]: setNodes,
|
[FLOW_ACTIONS.SET_NODES]: setNodes,
|
||||||
[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
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,6 +8,7 @@ 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];
|
||||||
error: IError;
|
error: IError;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
@ -16,8 +17,9 @@ const INITIAL_STATE: IFlowState = {
|
||||||
heroes: [],
|
heroes: [],
|
||||||
recent: [],
|
recent: [],
|
||||||
updated: [],
|
updated: [],
|
||||||
|
range: [null, null], // drop it, we use realtime range calc
|
||||||
is_loading: false,
|
is_loading: false,
|
||||||
error: null,
|
error: null
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createReducer(INITIAL_STATE, FLOW_HANDLERS);
|
export default createReducer(INITIAL_STATE, FLOW_HANDLERS);
|
||||||
|
|
|
@ -1,56 +1,78 @@
|
||||||
import { takeLatest, call, put, select } from 'redux-saga/effects';
|
import {
|
||||||
import { REHYDRATE } from 'redux-persist';
|
takeLatest,
|
||||||
import { FLOW_ACTIONS } from './constants';
|
call,
|
||||||
import { getNodes } from '../node/api';
|
put,
|
||||||
|
select,
|
||||||
|
takeLeading,
|
||||||
|
delay
|
||||||
|
} from "redux-saga/effects";
|
||||||
|
import { REHYDRATE } from "redux-persist";
|
||||||
|
import { FLOW_ACTIONS } from "./constants";
|
||||||
|
import { getNodes } from "../node/api";
|
||||||
import {
|
import {
|
||||||
flowSetNodes,
|
flowSetNodes,
|
||||||
flowSetCellView,
|
flowSetCellView,
|
||||||
flowSetHeroes,
|
flowSetHeroes,
|
||||||
flowSetRecent,
|
flowSetRecent,
|
||||||
flowSetUpdated,
|
flowSetUpdated,
|
||||||
} from './actions';
|
flowSetFlow
|
||||||
import { IResultWithStatus, INode } from '../types';
|
} from "./actions";
|
||||||
import { selectFlowNodes } from './selectors';
|
import { IResultWithStatus } from "../types";
|
||||||
import { reqWrapper } from '../auth/sagas';
|
import { selectFlowNodes } from "./selectors";
|
||||||
import { postCellView } from './api';
|
import { reqWrapper } from "../auth/sagas";
|
||||||
import { IFlowState } from './reducer';
|
import { postCellView } from "./api";
|
||||||
|
import { IFlowState } from "./reducer";
|
||||||
|
|
||||||
function* onGetFlow() {
|
function* onGetFlow() {
|
||||||
|
yield put(flowSetFlow({ is_loading: true }));
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: { nodes = [], heroes = [], recent = [], updated = [] },
|
data: { nodes = [], heroes = [], recent = [], updated = [], mode }
|
||||||
}: IResultWithStatus<{
|
}: IResultWithStatus<{
|
||||||
nodes: IFlowState['nodes'];
|
nodes: IFlowState["nodes"];
|
||||||
heroes: IFlowState['heroes'];
|
heroes: IFlowState["heroes"];
|
||||||
recent: IFlowState['recent'];
|
recent: IFlowState["recent"];
|
||||||
updated: IFlowState['updated'];
|
updated: IFlowState["updated"];
|
||||||
|
mode: string;
|
||||||
}> = yield call(reqWrapper, getNodes, {});
|
}> = yield call(reqWrapper, getNodes, {});
|
||||||
|
|
||||||
// if (!nodes || !nodes.length) {
|
yield put(flowSetFlow({ is_loading: false, nodes }));
|
||||||
// yield put(flowSetNodes([]));
|
|
||||||
// yield put(flowSetHeroes([]));
|
|
||||||
// yield put(flowSetRecent([]));
|
|
||||||
// yield put(flowSetUpdated([]));
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
yield put(flowSetNodes(nodes));
|
if (heroes.length) yield put(flowSetHeroes(heroes));
|
||||||
yield put(flowSetHeroes(heroes));
|
if (recent.length) yield put(flowSetRecent(recent));
|
||||||
yield put(flowSetRecent(recent));
|
if (updated.length) yield put(flowSetUpdated(updated));
|
||||||
yield put(flowSetUpdated(updated));
|
|
||||||
|
|
||||||
document.getElementById('main_loader').style.display = 'none';
|
document.getElementById("main_loader").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
function* onSetCellView({ id, flow }: ReturnType<typeof flowSetCellView>) {
|
function* onSetCellView({ id, flow }: ReturnType<typeof flowSetCellView>) {
|
||||||
const nodes = yield select(selectFlowNodes);
|
const nodes = yield select(selectFlowNodes);
|
||||||
yield put(flowSetNodes(nodes.map(node => (node.id === id ? { ...node, flow } : node))));
|
yield put(
|
||||||
|
flowSetNodes(nodes.map(node => (node.id === id ? { ...node, flow } : node)))
|
||||||
|
);
|
||||||
|
|
||||||
const { data, error } = yield call(reqWrapper, postCellView, { id, flow });
|
const { data, error } = yield call(reqWrapper, postCellView, { id, flow });
|
||||||
|
}
|
||||||
|
|
||||||
console.log({ data, error });
|
function* getMore() {
|
||||||
|
yield put(flowSetFlow({ is_loading: true }));
|
||||||
|
const nodes: IFlowState["nodes"] = yield select(selectFlowNodes);
|
||||||
|
const from =
|
||||||
|
nodes && nodes[nodes.length - 1] && nodes[nodes.length - 1].created_at;
|
||||||
|
|
||||||
|
const { error, data } = yield call(reqWrapper, getNodes, { from });
|
||||||
|
|
||||||
|
if (error || !data || !data.nodes) return;
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
flowSetFlow({ is_loading: false, nodes: [...nodes, ...data.nodes] })
|
||||||
|
);
|
||||||
|
|
||||||
|
yield delay(data.nodes.length > 0 ? 2000 : 30000);
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
import { api, configWithToken, resultMiddleware, errorMiddleware } from '~/utils/api';
|
import {
|
||||||
import { INode, IResultWithStatus, IComment } from '../types';
|
api,
|
||||||
import { API } from '~/constants/api';
|
configWithToken,
|
||||||
import { nodeUpdateTags, nodeLike, nodeStar } from './actions';
|
resultMiddleware,
|
||||||
import { INodeState } from './reducer';
|
errorMiddleware
|
||||||
|
} from "~/utils/api";
|
||||||
|
import { INode, IResultWithStatus, IComment } from "../types";
|
||||||
|
import { API } from "~/constants/api";
|
||||||
|
import { nodeUpdateTags, nodeLike, nodeStar } from "./actions";
|
||||||
|
import { INodeState } from "./reducer";
|
||||||
|
|
||||||
export const postNode = ({
|
export const postNode = ({
|
||||||
access,
|
access,
|
||||||
node,
|
node
|
||||||
}: {
|
}: {
|
||||||
access: string;
|
access: string;
|
||||||
node: INode;
|
node: INode;
|
||||||
|
@ -18,20 +23,20 @@ export const postNode = ({
|
||||||
// .then(console.log);
|
// .then(console.log);
|
||||||
|
|
||||||
export const getNodes = ({
|
export const getNodes = ({
|
||||||
skip = 0,
|
from = null,
|
||||||
access,
|
access
|
||||||
}: {
|
}: {
|
||||||
skip?: number;
|
from?: string;
|
||||||
access: string;
|
access: string;
|
||||||
}): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
}): Promise<IResultWithStatus<{ nodes: INode[] }>> =>
|
||||||
api
|
api
|
||||||
.get(API.NODE.GET, configWithToken(access, { params: { skip } }))
|
.get(API.NODE.GET, configWithToken(access, { params: { from } }))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
.catch(errorMiddleware);
|
.catch(errorMiddleware);
|
||||||
|
|
||||||
export const getNode = ({
|
export const getNode = ({
|
||||||
id,
|
id,
|
||||||
access,
|
access
|
||||||
}: {
|
}: {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
access: string;
|
access: string;
|
||||||
|
@ -44,7 +49,7 @@ export const getNode = ({
|
||||||
export const postNodeComment = ({
|
export const postNodeComment = ({
|
||||||
id,
|
id,
|
||||||
data,
|
data,
|
||||||
access,
|
access
|
||||||
}: {
|
}: {
|
||||||
access: string;
|
access: string;
|
||||||
id: number;
|
id: number;
|
||||||
|
@ -58,11 +63,11 @@ export const postNodeComment = ({
|
||||||
export const getNodeComments = ({
|
export const getNodeComments = ({
|
||||||
id,
|
id,
|
||||||
access,
|
access,
|
||||||
order = 'ASC',
|
order = "ASC"
|
||||||
}: {
|
}: {
|
||||||
id: number;
|
id: number;
|
||||||
access: string;
|
access: string;
|
||||||
order: 'ASC' | 'DESC';
|
order: "ASC" | "DESC";
|
||||||
}): Promise<IResultWithStatus<{ comments: Comment[] }>> =>
|
}): Promise<IResultWithStatus<{ comments: Comment[] }>> =>
|
||||||
api
|
api
|
||||||
.get(API.NODE.COMMENT(id), configWithToken(access, { params: { order } }))
|
.get(API.NODE.COMMENT(id), configWithToken(access, { params: { order } }))
|
||||||
|
@ -71,11 +76,11 @@ export const getNodeComments = ({
|
||||||
|
|
||||||
export const getNodeRelated = ({
|
export const getNodeRelated = ({
|
||||||
id,
|
id,
|
||||||
access,
|
access
|
||||||
}: {
|
}: {
|
||||||
id: number;
|
id: number;
|
||||||
access: string;
|
access: string;
|
||||||
}): Promise<IResultWithStatus<{ related: INodeState['related'] }>> =>
|
}): Promise<IResultWithStatus<{ related: INodeState["related"] }>> =>
|
||||||
api
|
api
|
||||||
.get(API.NODE.RELATED(id), configWithToken(access))
|
.get(API.NODE.RELATED(id), configWithToken(access))
|
||||||
.then(resultMiddleware)
|
.then(resultMiddleware)
|
||||||
|
@ -84,7 +89,7 @@ export const getNodeRelated = ({
|
||||||
export const updateNodeTags = ({
|
export const updateNodeTags = ({
|
||||||
id,
|
id,
|
||||||
tags,
|
tags,
|
||||||
access,
|
access
|
||||||
}: ReturnType<typeof nodeUpdateTags> & { access: string }): Promise<
|
}: ReturnType<typeof nodeUpdateTags> & { access: string }): Promise<
|
||||||
IResultWithStatus<{ node: INode }>
|
IResultWithStatus<{ node: INode }>
|
||||||
> =>
|
> =>
|
||||||
|
@ -95,9 +100,9 @@ export const updateNodeTags = ({
|
||||||
|
|
||||||
export const postNodeLike = ({
|
export const postNodeLike = ({
|
||||||
id,
|
id,
|
||||||
access,
|
access
|
||||||
}: ReturnType<typeof nodeLike> & { access: string }): Promise<
|
}: ReturnType<typeof nodeLike> & { access: string }): Promise<
|
||||||
IResultWithStatus<{ is_liked: INode['is_liked'] }>
|
IResultWithStatus<{ is_liked: INode["is_liked"] }>
|
||||||
> =>
|
> =>
|
||||||
api
|
api
|
||||||
.post(API.NODE.POST_LIKE(id), {}, configWithToken(access))
|
.post(API.NODE.POST_LIKE(id), {}, configWithToken(access))
|
||||||
|
@ -106,9 +111,9 @@ export const postNodeLike = ({
|
||||||
|
|
||||||
export const postNodeStar = ({
|
export const postNodeStar = ({
|
||||||
id,
|
id,
|
||||||
access,
|
access
|
||||||
}: ReturnType<typeof nodeStar> & { access: string }): Promise<
|
}: ReturnType<typeof nodeStar> & { access: string }): Promise<
|
||||||
IResultWithStatus<{ is_liked: INode['is_liked'] }>
|
IResultWithStatus<{ is_liked: INode["is_liked"] }>
|
||||||
> =>
|
> =>
|
||||||
api
|
api
|
||||||
.post(API.NODE.POST_STAR(id), {}, configWithToken(access))
|
.post(API.NODE.POST_STAR(id), {}, configWithToken(access))
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
import { IFile } from '~/redux/types';
|
import { IFile } from "~/redux/types";
|
||||||
import formatDistanceToNow from 'date-fns/formatDistanceToNow';
|
import formatDistanceToNow from "date-fns/formatDistanceToNow";
|
||||||
import { ru } from 'date-fns/locale';
|
import { ru } from "date-fns/locale";
|
||||||
import Axios from 'axios';
|
import Axios from "axios";
|
||||||
import { PRESETS } from '~/constants/urls';
|
import { PRESETS } from "~/constants/urls";
|
||||||
|
|
||||||
export const getStyle = (oElm: any, strCssRule: string) => {
|
export const getStyle = (oElm: any, strCssRule: string) => {
|
||||||
if (document.defaultView && document.defaultView.getComputedStyle) {
|
if (document.defaultView && document.defaultView.getComputedStyle) {
|
||||||
return document.defaultView.getComputedStyle(oElm, '').getPropertyValue(strCssRule);
|
return document.defaultView
|
||||||
|
.getComputedStyle(oElm, "")
|
||||||
|
.getPropertyValue(strCssRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oElm.currentStyle) {
|
if (oElm.currentStyle) {
|
||||||
return oElm.currentStyle[strCssRule.replace(/-(\w)/g, (strMatch, p1) => p1.toUpperCase())];
|
return oElm.currentStyle[
|
||||||
|
strCssRule.replace(/-(\w)/g, (strMatch, p1) => p1.toUpperCase())
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return "";
|
||||||
};
|
};
|
||||||
|
|
||||||
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
||||||
|
@ -21,7 +25,7 @@ function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: centerX + radius * Math.cos(angleInRadians),
|
x: centerX + radius * Math.cos(angleInRadians),
|
||||||
y: centerY + radius * Math.sin(angleInRadians),
|
y: centerY + radius * Math.sin(angleInRadians)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +42,10 @@ export const describeArc = (
|
||||||
const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;
|
const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'M',
|
"M",
|
||||||
start.x,
|
start.x,
|
||||||
start.y,
|
start.y,
|
||||||
'A',
|
"A",
|
||||||
radius,
|
radius,
|
||||||
radius,
|
radius,
|
||||||
0,
|
0,
|
||||||
|
@ -49,63 +53,76 @@ export const describeArc = (
|
||||||
0,
|
0,
|
||||||
end.x,
|
end.x,
|
||||||
end.y,
|
end.y,
|
||||||
'L',
|
"L",
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
'L',
|
"L",
|
||||||
start.x,
|
start.x,
|
||||||
start.y,
|
start.y
|
||||||
].join(' ');
|
].join(" ");
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getURL = (file: Partial<IFile>, size?: typeof PRESETS[keyof typeof PRESETS]) => {
|
export const getURL = (
|
||||||
|
file: Partial<IFile>,
|
||||||
|
size?: typeof PRESETS[keyof typeof PRESETS]
|
||||||
|
) => {
|
||||||
if (!file || !file.url) return null;
|
if (!file || !file.url) return null;
|
||||||
|
|
||||||
if (size) {
|
if (size) {
|
||||||
return file.url
|
return file.url
|
||||||
.replace('REMOTE_CURRENT://', `${process.env.REMOTE_CURRENT}cache/${size}/`)
|
.replace(
|
||||||
.replace('REMOTE_OLD://', process.env.REMOTE_OLD);
|
"REMOTE_CURRENT://",
|
||||||
|
`${process.env.REMOTE_CURRENT}cache/${size}/`
|
||||||
|
)
|
||||||
|
.replace("REMOTE_OLD://", process.env.REMOTE_OLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
return file.url
|
return file.url
|
||||||
.replace('REMOTE_CURRENT://', process.env.REMOTE_CURRENT)
|
.replace("REMOTE_CURRENT://", process.env.REMOTE_CURRENT)
|
||||||
.replace('REMOTE_OLD://', process.env.REMOTE_OLD);
|
.replace("REMOTE_OLD://", process.env.REMOTE_OLD);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const formatText = (text: string): string =>
|
export const formatText = (text: string): string =>
|
||||||
!text
|
!text
|
||||||
? ''
|
? ""
|
||||||
: text
|
: text
|
||||||
.replace(/(\n{2,})/gi, '\n')
|
.replace(/\n{1,}/gim, "\n")
|
||||||
.replace(/</g, '<')
|
.replace(/</g, "<")
|
||||||
.replace(/>/g, '>')
|
.replace(/>/g, ">")
|
||||||
.replace(
|
.replace(
|
||||||
/~([\wа-яА-Я\-]+)/giu,
|
/~([\wа-яА-Я\-]+)/giu,
|
||||||
'<span class="username" onClick="window.postMessage({ type: \'username\', username: \'$1\'});">~$1</span>'
|
"<span class=\"username\" onClick=\"window.postMessage({ type: 'username', username: '$1'});\">~$1</span>"
|
||||||
)
|
)
|
||||||
.replace(/:\/\//gim, ':|--|')
|
.replace(/:\/\//gim, ":|--|")
|
||||||
.replace(/(\/\/[^\n]+)/gim, '<span class="grey">$1</span>')
|
.replace(/(\/\/[^\n]+)/gim, '<span class="grey">$1</span>')
|
||||||
.replace(/(\/\*[\s\S]*?\*\/)/gim, '<span class="grey">$1</span>')
|
.replace(/(\/\*[\s\S]*?\*\/)/gim, '<span class="grey">$1</span>')
|
||||||
.replace(/:\|--\|/gim, '://')
|
.replace(/:\|--\|/gim, "://")
|
||||||
.split('\n')
|
.split("\n")
|
||||||
|
.filter(el => el.trim().length)
|
||||||
.map(el => `<p>${el}</p>`)
|
.map(el => `<p>${el}</p>`)
|
||||||
.join('');
|
.join("");
|
||||||
|
|
||||||
export const formatCommentText = (author: string, text: string): string =>
|
export const formatCommentText = (author: string, text: string): string =>
|
||||||
text
|
text
|
||||||
? formatText(text).replace(
|
? formatText(text).replace(
|
||||||
/^<p>/,
|
/^<p>/,
|
||||||
author ? `<p><b class="comment-author">${author}: </b>` : '<p>'
|
author ? `<p><b class="comment-author">${author}: </b>` : "<p>"
|
||||||
)
|
)
|
||||||
: '';
|
: "";
|
||||||
|
|
||||||
export const formatCellText = (text: string): string => formatText(text);
|
export const formatCellText = (text: string): string => formatText(text);
|
||||||
|
|
||||||
export const getPrettyDate = (date: string): string =>
|
export const getPrettyDate = (date: string): string =>
|
||||||
formatDistanceToNow(new Date(date), { locale: ru, includeSeconds: true, addSuffix: true });
|
formatDistanceToNow(new Date(date), {
|
||||||
|
locale: ru,
|
||||||
|
includeSeconds: true,
|
||||||
|
addSuffix: true
|
||||||
|
});
|
||||||
|
|
||||||
export const getYoutubeTitle = async (id: string) => {
|
export const getYoutubeTitle = async (id: string) => {
|
||||||
Axios.get(`http://youtube.com/get_video_info?video_id=${id}`).then(console.log);
|
Axios.get(`http://youtube.com/get_video_info?video_id=${id}`).then(
|
||||||
|
console.log
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
(<any>window).getYoutubeTitle = getYoutubeTitle;
|
(<any>window).getYoutubeTitle = getYoutubeTitle;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue