diff --git a/src/components/flow/FlowGrid/index.tsx b/src/components/flow/FlowGrid/index.tsx index 538401b7..064a92d3 100644 --- a/src/components/flow/FlowGrid/index.tsx +++ b/src/components/flow/FlowGrid/index.tsx @@ -21,6 +21,7 @@ export const FlowGrid: FC = ({ nodes, heroes, recent, + updated, onSelect, onChangeCellView, }) => ( @@ -30,7 +31,7 @@ export const FlowGrid: FC = ({
- +
{nodes.map(node => ( diff --git a/src/components/flow/FlowGrid/styles.scss b/src/components/flow/FlowGrid/styles.scss index b997ef36..230d21d9 100644 --- a/src/components/flow/FlowGrid/styles.scss +++ b/src/components/flow/FlowGrid/styles.scss @@ -1,4 +1,5 @@ $cols: $content_width / $cell; +$stamp_color: $content_bg; .grid { padding: $gap / 2; @@ -11,8 +12,8 @@ $cols: $content_width / $cell; grid-template-rows: 50vh $cell; grid-auto-rows: $cell; grid-auto-flow: row dense; - grid-column-gap: $grid_line; - grid-row-gap: $grid_line; + grid-column-gap: $gap; + grid-row-gap: $gap; @include tablet { padding: 0 $gap; @@ -72,10 +73,11 @@ $cols: $content_width / $cell; } .stamp { + @include outer_shadow(); // grid-row: -1 / 3; grid-row-end: span 2; grid-column: -2 / -1; - background: darken($content_bg, 8%); + background: $stamp_color; border-radius: $radius; padding: $gap; display: flex; @@ -85,4 +87,16 @@ $cols: $content_width / $cell; display: flex; align-items: stretch; justify-content: stretch; + overflow: hidden; + position: relative; + + &::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + height: 60px; + width: 100%; + background: linear-gradient(transparentize($stamp_color, 1), $stamp_color 90%); + } } diff --git a/src/components/flow/FlowRecent/index.tsx b/src/components/flow/FlowRecent/index.tsx index 6f1bbf37..51440bb9 100644 --- a/src/components/flow/FlowRecent/index.tsx +++ b/src/components/flow/FlowRecent/index.tsx @@ -1,19 +1,45 @@ import React, { FC } from 'react'; import * as styles from './styles.scss'; import { IFlowState } from '~/redux/flow/reducer'; +import { getURL } from '~/utils/dom'; +import { Link } from 'react-router-dom'; +import { URLS } from '~/constants/urls'; +import classNames from 'classnames'; interface IProps { recent: IFlowState['recent']; + updated: IFlowState['updated']; } -const FlowRecent: FC = ({ recent }) => ( +const FlowRecent: FC = ({ recent, updated }) => (
+ {updated && + updated.slice(0, 20).map(node => ( + +
+ +
+
{node.title}
+
{node.created_at}
+
+ + ))} + {recent && - recent.slice(0, 9).map(node => ( -
-
-
{node.title}
-
+ recent.slice(0, 20).map(node => ( + +
+
+
{node.title}
+
{node.created_at}
+
+ ))}
); diff --git a/src/components/flow/FlowRecent/styles.scss b/src/components/flow/FlowRecent/styles.scss index dfa20d6d..ce85ed4c 100644 --- a/src/components/flow/FlowRecent/styles.scss +++ b/src/components/flow/FlowRecent/styles.scss @@ -1,9 +1,7 @@ .grid { - display: grid; - grid-template-rows: repeat(9, 1fr); - grid-template-columns: auto; - row-gap: $grid_line; + display: flex; justify-content: stretch; + flex-direction: column; flex: 1; & > div { @@ -20,17 +18,51 @@ font: $font_12_regular; border-radius: $radius; flex-direction: row; + margin-bottom: $gap; + color: white; + text-decoration: none; } .thumb { - height: 100%; - width: 40px; - margin-right: $grid_line; - // padding-left: 100%; - background: red; - // flex: + height: 48px; + margin-right: $gap; + background: 50% 50% no-repeat/cover; + border-radius: $radius; + flex: 0 0 48px; + display: flex; + align-items: center; + justify-content: center; + position: relative; + + &.new { + &::after { + content: ' '; + width: 12px; + height: 12px; + border-radius: 100%; + background: $red; + box-shadow: $content_bg 0 0 0 5px; + position: absolute; + right: -2px; + bottom: -2px; + } + } } .info { flex: 1; + min-width: 0; +} + +.title { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + font: $font_16_semibold; +} + +.comment { + font: $font_12_regular; + margin-top: 4px; + opacity: 0.5; } diff --git a/src/containers/flow/FlowLayout/index.tsx b/src/containers/flow/FlowLayout/index.tsx index b4d0806a..19885295 100644 --- a/src/containers/flow/FlowLayout/index.tsx +++ b/src/containers/flow/FlowLayout/index.tsx @@ -8,7 +8,7 @@ import pick from 'ramda/es/pick'; import { selectUser } from '~/redux/auth/selectors'; const mapStateToProps = state => ({ - flow: pick(['nodes', 'heroes', 'recent'], selectFlow(state)), + flow: pick(['nodes', 'heroes', 'recent', 'updated'], selectFlow(state)), user: pick(['role', 'id'], selectUser(state)), }); @@ -20,7 +20,7 @@ const mapDispatchToProps = { type IProps = ReturnType & typeof mapDispatchToProps & {}; const FlowLayoutUnconnected: FC = ({ - flow: { nodes, heroes, recent }, + flow: { nodes, heroes, recent, updated }, user, nodeLoadNode, flowSetCellView, @@ -29,6 +29,7 @@ const FlowLayoutUnconnected: FC = ({ nodes={nodes} heroes={heroes} recent={recent} + updated={updated} onSelect={nodeLoadNode} user={user} onChangeCellView={flowSetCellView} diff --git a/src/redux/flow/actions.ts b/src/redux/flow/actions.ts index 51d3737a..5046ad9c 100644 --- a/src/redux/flow/actions.ts +++ b/src/redux/flow/actions.ts @@ -17,6 +17,11 @@ export const flowSetRecent = (recent: IFlowState['recent']) => ({ type: FLOW_ACTIONS.SET_RECENT, }); +export const flowSetUpdated = (updated: IFlowState['updated']) => ({ + updated, + type: FLOW_ACTIONS.SET_UPDATED, +}); + export const flowSetCellView = (id: INode['id'], flow: INode['flow']) => ({ type: FLOW_ACTIONS.SET_CELL_VIEW, id, diff --git a/src/redux/flow/constants.ts b/src/redux/flow/constants.ts index d350f7cb..5f7e02b8 100644 --- a/src/redux/flow/constants.ts +++ b/src/redux/flow/constants.ts @@ -5,5 +5,6 @@ export const FLOW_ACTIONS = { SET_NODES: `${prefix}SET_NODES`, SET_HEROES: `${prefix}SET_HEROES`, SET_RECENT: `${prefix}SET_RECENT`, + SET_UPDATED: `${prefix}SET_UPDATED`, SET_CELL_VIEW: `${prefix}SET_CELL_VIEW`, }; diff --git a/src/redux/flow/handlers.ts b/src/redux/flow/handlers.ts index 2951bc89..7a273178 100644 --- a/src/redux/flow/handlers.ts +++ b/src/redux/flow/handlers.ts @@ -1,6 +1,6 @@ import assocPath from 'ramda/es/assocPath'; import { FLOW_ACTIONS } from './constants'; -import { flowSetNodes, flowSetHeroes, flowSetRecent } from './actions'; +import { flowSetNodes, flowSetHeroes, flowSetRecent, flowSetUpdated } from './actions'; import { IFlowState } from './reducer'; const setNodes = (state: IFlowState, { nodes }: ReturnType) => @@ -12,8 +12,12 @@ const setHeroes = (state: IFlowState, { heroes }: ReturnType) => assocPath(['recent'], recent, state); +const setUpdated = (state: IFlowState, { updated }: ReturnType) => + assocPath(['updated'], updated, state); + export const FLOW_HANDLERS = { [FLOW_ACTIONS.SET_NODES]: setNodes, [FLOW_ACTIONS.SET_HEROES]: setHeroes, [FLOW_ACTIONS.SET_RECENT]: setRecent, + [FLOW_ACTIONS.SET_UPDATED]: setUpdated, }; diff --git a/src/redux/flow/reducer.ts b/src/redux/flow/reducer.ts index a5cc3845..d406bbd5 100644 --- a/src/redux/flow/reducer.ts +++ b/src/redux/flow/reducer.ts @@ -7,6 +7,7 @@ export type IFlowState = Readonly<{ nodes: INode[]; heroes: Partial[]; recent: Partial[]; + updated: Partial[]; error: IError; }>; @@ -14,6 +15,7 @@ const INITIAL_STATE: IFlowState = { nodes: [], heroes: [], recent: [], + updated: [], is_loading: false, error: null, }; diff --git a/src/redux/flow/sagas.ts b/src/redux/flow/sagas.ts index 89c41165..370629be 100644 --- a/src/redux/flow/sagas.ts +++ b/src/redux/flow/sagas.ts @@ -2,7 +2,13 @@ import { takeLatest, call, put, select } from 'redux-saga/effects'; import { REHYDRATE } from 'redux-persist'; import { FLOW_ACTIONS } from './constants'; import { getNodes } from '../node/api'; -import { flowSetNodes, flowSetCellView, flowSetHeroes, flowSetRecent } from './actions'; +import { + flowSetNodes, + flowSetCellView, + flowSetHeroes, + flowSetRecent, + flowSetUpdated, +} from './actions'; import { IResultWithStatus, INode } from '../types'; import { selectFlowNodes } from './selectors'; import { reqWrapper } from '../auth/sagas'; @@ -11,23 +17,26 @@ import { IFlowState } from './reducer'; function* onGetFlow() { const { - data: { nodes = null, heroes = null, recent = null }, + data: { nodes = null, heroes = null, recent = [], updated = [] }, }: IResultWithStatus<{ nodes: IFlowState['nodes']; heroes: IFlowState['heroes']; recent: IFlowState['recent']; - }> = yield call(getNodes, {}); + updated: IFlowState['updated']; + }> = yield call(reqWrapper, getNodes, {}); if (!nodes || !nodes.length) { yield put(flowSetNodes([])); yield put(flowSetHeroes([])); yield put(flowSetRecent([])); + yield put(flowSetUpdated([])); return; } yield put(flowSetNodes(nodes)); yield put(flowSetHeroes(heroes)); yield put(flowSetRecent(recent)); + yield put(flowSetUpdated(updated)); } function* onSetCellView({ id, flow }: ReturnType) { diff --git a/src/redux/node/api.ts b/src/redux/node/api.ts index 5a98044d..044f80c6 100644 --- a/src/redux/node/api.ts +++ b/src/redux/node/api.ts @@ -19,11 +19,13 @@ export const postNode = ({ export const getNodes = ({ skip = 0, + access, }: { skip?: number; + access: string; }): Promise> => api - .get(API.NODE.GET, { params: { skip } }) + .get(API.NODE.GET, configWithToken(access, { params: { skip } })) .then(resultMiddleware) .catch(errorMiddleware); diff --git a/src/redux/types.ts b/src/redux/types.ts index d2e57edb..75069c48 100644 --- a/src/redux/types.ts +++ b/src/redux/types.ts @@ -129,8 +129,9 @@ export interface INode { tags: ITag[]; - createdAt?: string; - updatedAt?: string; + created_at?: string; + updated_at?: string; + commented_at?: string; } export interface IComment { diff --git a/src/styles/variables.scss b/src/styles/variables.scss index a7a75fb5..f7b53f8c 100644 --- a/src/styles/variables.scss +++ b/src/styles/variables.scss @@ -8,7 +8,7 @@ $spc: $gap * 2; $comment_height: 72px; $bar_height: 64px; -$radius: 8px; +$radius: 2px; $cell_radius: $radius; $panel_radius: $radius; $input_radius: $radius;