From 3aa2d4f609e909556978bb2173f558fd88a70e10 Mon Sep 17 00:00:00 2001 From: Fedor Katurov Date: Fri, 12 Mar 2021 13:56:23 +0700 Subject: [PATCH] #23 added lab node layout (sample) --- .../node/NodePanelInner/styles.module.scss | 4 +- src/components/node/NodePanelLab/index.tsx | 19 +++++++++ .../node/NodePanelLab/styles.module.scss | 24 +++++++++++ src/constants/api.ts | 3 ++ src/containers/lab/LabGrid/index.tsx | 4 +- src/containers/lab/LabLayout/index.tsx | 42 ++++++++++++------- src/containers/lab/LabNode/index.tsx | 29 ++++++------- src/containers/lab/LabNode/styles.module.scss | 10 +++++ src/containers/main/MainRouter/index.tsx | 10 ++++- src/redux/lab/actions.ts | 12 ++++++ src/redux/lab/api.ts | 8 ++++ src/redux/lab/constants.ts | 6 +++ src/redux/lab/handlers.ts | 20 +++++++++ src/redux/lab/index.ts | 14 +++++++ src/redux/lab/sagas.ts | 21 ++++++++++ src/redux/lab/selectors.ts | 4 ++ src/redux/lab/types.ts | 19 +++++++++ src/redux/store.ts | 7 ++++ 18 files changed, 218 insertions(+), 38 deletions(-) create mode 100644 src/components/node/NodePanelLab/index.tsx create mode 100644 src/components/node/NodePanelLab/styles.module.scss create mode 100644 src/containers/lab/LabNode/styles.module.scss create mode 100644 src/redux/lab/actions.ts create mode 100644 src/redux/lab/api.ts create mode 100644 src/redux/lab/constants.ts create mode 100644 src/redux/lab/handlers.ts create mode 100644 src/redux/lab/index.ts create mode 100644 src/redux/lab/sagas.ts create mode 100644 src/redux/lab/selectors.ts create mode 100644 src/redux/lab/types.ts diff --git a/src/components/node/NodePanelInner/styles.module.scss b/src/components/node/NodePanelInner/styles.module.scss index 58d925fa..c9b9a94b 100644 --- a/src/components/node/NodePanelInner/styles.module.scss +++ b/src/components/node/NodePanelInner/styles.module.scss @@ -31,8 +31,6 @@ .wrap { display: flex; - align-items: center; - justify-content: stretch; position: relative; width: 100%; flex-direction: row; @@ -88,7 +86,7 @@ @include tablet { white-space: nowrap; padding-bottom: 0; - font: $font_20_semibold; + font: $font_16_semibold; } } diff --git a/src/components/node/NodePanelLab/index.tsx b/src/components/node/NodePanelLab/index.tsx new file mode 100644 index 00000000..c33714d4 --- /dev/null +++ b/src/components/node/NodePanelLab/index.tsx @@ -0,0 +1,19 @@ +import React, { FC } from 'react'; +import { INode } from '~/redux/types'; +import styles from './styles.module.scss'; +import { URLS } from '~/constants/urls'; +import { Link } from 'react-router-dom'; + +interface IProps { + node: INode; +} + +const NodePanelLab: FC = ({ node }) => ( +
+
+ {node.title || '...'} +
+
+); + +export { NodePanelLab }; diff --git a/src/components/node/NodePanelLab/styles.module.scss b/src/components/node/NodePanelLab/styles.module.scss new file mode 100644 index 00000000..095dafe5 --- /dev/null +++ b/src/components/node/NodePanelLab/styles.module.scss @@ -0,0 +1,24 @@ +@import "~/styles/variables.scss"; + +.wrap { + padding: $gap; +} + +.title { + text-transform: uppercase; + font: $font_24_semibold; + overflow: hidden; + flex: 1; + text-overflow: ellipsis; + + a { + text-decoration: none; + color: inherit; + } + + @include tablet { + white-space: nowrap; + padding-bottom: 0; + font: $font_16_semibold; + } +} diff --git a/src/constants/api.ts b/src/constants/api.ts index 6f57d689..c9c4c287 100644 --- a/src/constants/api.ts +++ b/src/constants/api.ts @@ -50,4 +50,7 @@ export const API = { NODES: `/tag/nodes`, AUTOCOMPLETE: `/tag/autocomplete`, }, + LAB: { + NODES: `/lab/`, + }, }; diff --git a/src/containers/lab/LabGrid/index.tsx b/src/containers/lab/LabGrid/index.tsx index 1caa8381..6dc04ef3 100644 --- a/src/containers/lab/LabGrid/index.tsx +++ b/src/containers/lab/LabGrid/index.tsx @@ -1,13 +1,13 @@ import React, { FC } from 'react'; import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; -import { selectFlowNodes } from '~/redux/flow/selectors'; import styles from './styles.module.scss'; import { LabNode } from '~/containers/lab/LabNode'; +import { selectLabListNodes } from '~/redux/lab/selectors'; interface IProps {} const LabGrid: FC = () => { - const nodes = useShallowSelect(selectFlowNodes); + const nodes = useShallowSelect(selectLabListNodes); return (
diff --git a/src/containers/lab/LabLayout/index.tsx b/src/containers/lab/LabLayout/index.tsx index 47a77bcd..087a8546 100644 --- a/src/containers/lab/LabLayout/index.tsx +++ b/src/containers/lab/LabLayout/index.tsx @@ -1,27 +1,37 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import styles from './styles.module.scss'; import { Card } from '~/components/containers/Card'; import { Sticky } from '~/components/containers/Sticky'; import { Container } from '~/containers/main/Container'; import { LabGrid } from '~/containers/lab/LabGrid'; +import { useDispatch } from 'react-redux'; +import { labGetList } from '~/redux/lab/actions'; interface IProps {} -const LabLayout: FC = () => ( -
- -
-
- +const LabLayout: FC = () => { + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(labGetList()); + }, [dispatch]); + + return ( +
+ +
+
+ +
+
+ + Test + +
-
- - Test - -
-
- -
-); + +
+ ); +}; export { LabLayout }; diff --git a/src/containers/lab/LabNode/index.tsx b/src/containers/lab/LabNode/index.tsx index 246a119b..5f64c55f 100644 --- a/src/containers/lab/LabNode/index.tsx +++ b/src/containers/lab/LabNode/index.tsx @@ -2,6 +2,9 @@ import React, { FC } from 'react'; import { INode } from '~/redux/types'; import { NodePanelInner } from '~/components/node/NodePanelInner'; import { useNodeBlocks } from '~/utils/hooks/node/useNodeBlocks'; +import styles from './styles.module.scss'; +import { Card } from '~/components/containers/Card'; +import { NodePanelLab } from '~/components/node/NodePanelLab'; interface IProps { node: INode; @@ -10,24 +13,18 @@ interface IProps { const LabNode: FC = ({ node }) => { const { inline, block, head } = useNodeBlocks(node, false); - return ( -
- + console.log(node.id, { inline, block, head }); + + return ( + +
+ +
- {inline} - {block} {head} -
+ {block} + {inline} + ); }; diff --git a/src/containers/lab/LabNode/styles.module.scss b/src/containers/lab/LabNode/styles.module.scss new file mode 100644 index 00000000..8e002f2b --- /dev/null +++ b/src/containers/lab/LabNode/styles.module.scss @@ -0,0 +1,10 @@ +@import "~/styles/variables.scss"; + +.wrap { + min-width: 0; +} + +.head { + background-color: transparentize(black, 0.9); + border-radius: $radius $radius 0 0; +} diff --git a/src/containers/main/MainRouter/index.tsx b/src/containers/main/MainRouter/index.tsx index b5a17270..ac8988dc 100644 --- a/src/containers/main/MainRouter/index.tsx +++ b/src/containers/main/MainRouter/index.tsx @@ -7,21 +7,29 @@ import { ErrorNotFound } from '~/containers/pages/ErrorNotFound'; import { ProfilePage } from '~/containers/profile/ProfilePage'; import { Redirect, Route, Switch, useLocation } from 'react-router'; import { LabLayout } from '~/containers/lab/LabLayout'; +import { useShallowSelect } from '~/utils/hooks/useShallowSelect'; +import { selectAuthUser } from '~/redux/auth/selectors'; interface IProps {} const MainRouter: FC = () => { + const { is_user } = useShallowSelect(selectAuthUser); const location = useLocation(); return ( - + {is_user && ( + <> + + + )} + ); diff --git a/src/redux/lab/actions.ts b/src/redux/lab/actions.ts new file mode 100644 index 00000000..1e1ff97a --- /dev/null +++ b/src/redux/lab/actions.ts @@ -0,0 +1,12 @@ +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { ILabState } from '~/redux/lab/types'; + +export const labGetList = (after?: string) => ({ + type: LAB_ACTIONS.GET_LIST, + after, +}); + +export const labSetList = (list: Partial) => ({ + type: LAB_ACTIONS.SET_LIST, + list, +}); diff --git a/src/redux/lab/api.ts b/src/redux/lab/api.ts new file mode 100644 index 00000000..5fa97bc0 --- /dev/null +++ b/src/redux/lab/api.ts @@ -0,0 +1,8 @@ +import { api, cleanResult } from '~/utils/api'; +import { API } from '~/constants/api'; +import { GetLabNodesRequest, GetLabNodesResult } from '~/redux/lab/types'; + +export const getLabNodes = ({ after }: GetLabNodesRequest) => + api + .get(API.LAB.NODES, { params: { after } }) + .then(cleanResult); diff --git a/src/redux/lab/constants.ts b/src/redux/lab/constants.ts new file mode 100644 index 00000000..d2e670da --- /dev/null +++ b/src/redux/lab/constants.ts @@ -0,0 +1,6 @@ +const prefix = 'LAB.'; + +export const LAB_ACTIONS = { + GET_LIST: `${prefix}GET_LIST`, + SET_LIST: `${prefix}SET_LIST`, +}; diff --git a/src/redux/lab/handlers.ts b/src/redux/lab/handlers.ts new file mode 100644 index 00000000..b09812e2 --- /dev/null +++ b/src/redux/lab/handlers.ts @@ -0,0 +1,20 @@ +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { labSetList } from '~/redux/lab/actions'; +import { ILabState } from '~/redux/lab/types'; + +type LabHandler any> = ( + state: Readonly, + payload: ReturnType +) => Readonly; + +const setList: LabHandler = (state, { list }) => ({ + ...state, + list: { + ...state.list, + ...list, + }, +}); + +export const LAB_HANDLERS = { + [LAB_ACTIONS.SET_LIST]: setList, +}; diff --git a/src/redux/lab/index.ts b/src/redux/lab/index.ts new file mode 100644 index 00000000..56879a52 --- /dev/null +++ b/src/redux/lab/index.ts @@ -0,0 +1,14 @@ +import { createReducer } from '~/utils/reducer'; +import { LAB_HANDLERS } from '~/redux/lab/handlers'; +import { ILabState } from '~/redux/lab/types'; + +const INITIAL_STATE: ILabState = { + list: { + is_loading: false, + nodes: [], + count: 0, + error: '', + }, +}; + +export default createReducer(INITIAL_STATE, LAB_HANDLERS); diff --git a/src/redux/lab/sagas.ts b/src/redux/lab/sagas.ts new file mode 100644 index 00000000..5fc48b8b --- /dev/null +++ b/src/redux/lab/sagas.ts @@ -0,0 +1,21 @@ +import { takeLeading, call, put } from 'redux-saga/effects'; +import { labGetList, labSetList } from '~/redux/lab/actions'; +import { LAB_ACTIONS } from '~/redux/lab/constants'; +import { Unwrap } from '~/redux/types'; +import { getLabNodes } from '~/redux/lab/api'; + +function* getList({ after = '' }: ReturnType) { + try { + yield put(labSetList({ is_loading: true })); + const { nodes, count }: Unwrap = yield call(getLabNodes, { after }); + yield put(labSetList({ nodes, count })); + } catch (error) { + yield put(labSetList({ error: error.message })); + } finally { + yield put(labSetList({ is_loading: false })); + } +} + +export default function* labSaga() { + yield takeLeading(LAB_ACTIONS.GET_LIST, getList); +} diff --git a/src/redux/lab/selectors.ts b/src/redux/lab/selectors.ts new file mode 100644 index 00000000..0854ac25 --- /dev/null +++ b/src/redux/lab/selectors.ts @@ -0,0 +1,4 @@ +import { IState } from '~/redux/store'; + +export const selectLab = (state: IState) => state.lab; +export const selectLabListNodes = (state: IState) => state.lab.list.nodes; diff --git a/src/redux/lab/types.ts b/src/redux/lab/types.ts new file mode 100644 index 00000000..7614807b --- /dev/null +++ b/src/redux/lab/types.ts @@ -0,0 +1,19 @@ +import { IError, INode } from '~/redux/types'; + +export type ILabState = Readonly<{ + list: { + is_loading: boolean; + nodes: INode[]; + count: number; + error: IError; + }; +}>; + +export type GetLabNodesRequest = { + after?: string; +}; + +export type GetLabNodesResult = { + nodes: INode[]; + count: number; +}; diff --git a/src/redux/store.ts b/src/redux/store.ts index ea16f709..73e31db9 100644 --- a/src/redux/store.ts +++ b/src/redux/store.ts @@ -17,6 +17,10 @@ import nodeSaga from '~/redux/node/sagas'; import flow, { IFlowState } from '~/redux/flow/reducer'; import flowSaga from '~/redux/flow/sagas'; +import lab from '~/redux/lab'; +import labSaga from '~/redux/lab/sagas'; +import { ILabState } from '~/redux/lab/types'; + import uploads, { IUploadState } from '~/redux/uploads/reducer'; import uploadSaga from '~/redux/uploads/sagas'; @@ -69,6 +73,7 @@ export interface IState { boris: IBorisState; messages: IMessagesState; tag: ITagState; + lab: ILabState; } export const sagaMiddleware = createSagaMiddleware(); @@ -93,6 +98,7 @@ export const store = createStore( player: persistReducer(playerPersistConfig, player), messages, tag: tag, + lab: lab, }), composeEnhancers(applyMiddleware(routerMiddleware(history), sagaMiddleware)) ); @@ -110,6 +116,7 @@ export function configureStore(): { sagaMiddleware.run(borisSaga); sagaMiddleware.run(messagesSaga); sagaMiddleware.run(tagSaga); + sagaMiddleware.run(labSaga); window.addEventListener('message', message => { if (message && message.data && message.data.type === 'oauth_login' && message.data.token)