From e6742d0b046f19e8f5967beb64b54903ee97e3a5 Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 3 Jan 2019 16:11:03 +0000 Subject: [PATCH] factor step state of narrative from NarrativeCard comp to redux --- src/components/NarrativeCard.js | 5 +- src/reducers/app.js | 28 ++------ src/selectors/index.js | 117 +++++++++++++++++--------------- src/store/initial.js | 3 + 4 files changed, 73 insertions(+), 80 deletions(-) diff --git a/src/components/NarrativeCard.js b/src/components/NarrativeCard.js index e52ac5f..5d771a5 100644 --- a/src/components/NarrativeCard.js +++ b/src/components/NarrativeCard.js @@ -1,5 +1,6 @@ import React from 'react'; import { connect } from 'react-redux' +import { selectActiveNarrative } from '../selectors' class NarrativeCard extends React.Component { @@ -59,12 +60,10 @@ class NarrativeCard extends React.Component { // no display if no narrative if (!this.props.narrative) return null - console.log(this.props.narrative) const { steps, current } = this.props.narrative if (steps[current]) { const step = steps[current]; - console.log('here') return (
@@ -89,7 +88,7 @@ class NarrativeCard extends React.Component { function mapStateToProps(state) { return { - narrative: state.app.narrative + narrative: selectActiveNarrative(state) } } export default connect(mapStateToProps)(NarrativeCard); diff --git a/src/reducers/app.js b/src/reducers/app.js index 0b37e77..1904703 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -35,31 +35,11 @@ function updateSelected(appState, action) { function updateNarrative(appState, action) { return { ...appState, - narrative: action.narrative + narrative: action.narrative, + narrativeState: { + current: 0 + } } - - // if (action.narrative === null) { - // console.log(action.narrative) - // return Object.assign({}, appState, { - // narrative: action.narrative, - // }); - // } else { - // const dates = action.narrative.steps.map(n => parseDate(n.timestamp).getTime()) - // let minDate = Math.min(...dates); - // let maxDate = Math.max(...dates); - // // Add some margin to the datetime extent - // minDate = minDate - ((maxDate - minDate) / 20); - // maxDate = maxDate + ((maxDate - minDate) / 20); - // - // const output = Object.assign({}, appState, { - // narrative: action.narrative, - // filters: Object.assign({}, appState.filters, { - // timerange: [new Date(minDate), new Date(maxDate)] - // }), - // }); - // console.log(output) - // return output - // } } function updateTagFilters(appState, action) { diff --git a/src/selectors/index.js b/src/selectors/index.js index 5d7ac87..76cb8ad 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -2,23 +2,25 @@ import { createSelector} from 'reselect' import { parseTimestamp, compareTimestamp } from '../js/utilities' // Input selectors -export const getEvents = state => state.domain.events; -export const getLocations = state => state.domain.locations; -export const getCategories = state => state.domain.categories; -export const getNarratives = state => state.domain.narratives; -export const getSelected = state => state.app.selected; +export const getEvents = state => state.domain.events +export const getLocations = state => state.domain.locations +export const getCategories = state => state.domain.categories +export const getNarratives = state => state.domain.narratives +export const getActiveNarrative = state => state.app.narrative +export const getActiveStep = state => state.app.narrativeState.current +export const getSelected = state => state.app.selected export const getSites = (state) => { - if (process.env.features.USE_SITES) return state.domain.sites; - return []; + if (process.env.features.USE_SITES) return state.domain.sites + return [] } export const getSources = state => { - if (process.env.features.USE_SOURCES) return state.domain.sources; - return []; + if (process.env.features.USE_SOURCES) return state.domain.sources + return [] } -export const getNotifications = state => state.domain.notifications; -export const getTagTree = state => state.domain.tags; -export const getTagsFilter = state => state.app.filters.tags; -export const getTimeRange = state => state.app.filters.timerange; +export const getNotifications = state => state.domain.notifications +export const getTagTree = state => state.domain.tags +export const getTagsFilter = state => state.app.filters.tags +export const getTimeRange = state => state.app.filters.timerange /** * Some handy helpers @@ -30,11 +32,11 @@ export const getTimeRange = state => state.app.filters.timerange; */ function isTaggedIn(event, tagFilters) { if (event.tags) { - const tagsInEvent = event.tags.split(","); + const tagsInEvent = event.tags.split(",") const isTagged = tagsInEvent.some((tag) => { - return tagFilters.find(tF => (tF.key === tag && tF.active)); - }); - return isTagged; + return tagFilters.find(tF => (tF.key === tag && tF.active)) + }) + return isTagged } else { return false } @@ -48,7 +50,7 @@ function isNoTags(tagFilters) { tagFilters.length === 0 || !process.env.features.USE_TAGS || tagFilters.every(t => !t.active) - ); + ) } /** @@ -59,7 +61,7 @@ function isTimeRangedIn(event, timeRange) { return ( timeRange[0] < parseTimestamp(event.timestamp) && parseTimestamp(event.timestamp) < timeRange[1] - ); + ) } /** @@ -71,17 +73,17 @@ export const selectEvents = createSelector( (events, tagFilters, timeRange) => { return events.reduce((acc, event) => { - const isTagged = isTaggedIn(event, tagFilters) || isNoTags(tagFilters); - const isTimeRanged = isTimeRangedIn(event, timeRange); + const isTagged = isTaggedIn(event, tagFilters) || isNoTags(tagFilters) + const isTimeRanged = isTimeRangedIn(event, timeRange) if (isTimeRanged && isTagged) { - const eventClone = Object.assign({}, event); - acc[event.id] = eventClone; + const eventClone = Object.assign({}, event) + acc[event.id] = eventClone } - return acc; - }, []); -}); + return acc + }, []) +}) /** * Of all available events, selects those that fall within the time range, @@ -91,14 +93,14 @@ export const selectNarratives = createSelector( [getEvents, getNarratives, getTagsFilter, getTimeRange], (events, narrativesMeta, tagFilters, timeRange) => { - const narratives = {}; + const narratives = {} const narrativeSkeleton = id => ({ id, steps: [] }) /* populate narratives dict with events */ events.forEach(evt => { - const isTagged = isTaggedIn(evt, tagFilters) || isNoTags(tagFilters); - const isTimeRanged = isTimeRangedIn(evt, timeRange); - const isInNarrative = evt.narratives.length > 0; + const isTagged = isTaggedIn(evt, tagFilters) || isNoTags(tagFilters) + const isTimeRanged = isTimeRangedIn(evt, timeRange) + const isInNarrative = evt.narratives.length > 0 evt.narratives.forEach(narrative => { // initialise @@ -109,19 +111,19 @@ export const selectNarratives = createSelector( if (isInNarrative) narratives[narrative].steps.push(evt) }) - }); + }) /* sort steps by time */ Object.keys(narratives).forEach(key => { - const steps = narratives[key].steps; + const steps = narratives[key].steps - steps.sort(compareTimestamp); + steps.sort(compareTimestamp) // steps.forEach((step, i) => { - // narratives[key].byId[step.id].next = (i < steps.length - 2) ? steps[i + 1] : null; - // narratives[key].byId[step.id].prev = (i > 0) ? steps[i - 1] : null; - // }); + // narratives[key].byId[step.id].next = (i < steps.length - 2) ? steps[i + 1] : null + // narratives[key].byId[step.id].prev = (i > 0) ? steps[i - 1] : null + // }) if (narrativesMeta.find(n => n.id === key)) { narratives[key] = { @@ -129,11 +131,20 @@ export const selectNarratives = createSelector( ...narratives[key] } } - }); + }) - return Object.values(narratives); -}); + return Object.values(narratives) +}) +/** Aggregate information about the narrative and the current step into + * a single object. If narrative is null, the whole object is null. + */ +export const selectActiveNarrative = createSelector( + [getActiveNarrative, getActiveStep], + (narrative, current) => !!narrative + ? { ...narrative, current } + : null +) /** * Of all the filtered events, group them by location and return a list of * locations with at least one event in it, based on the time range and tags @@ -142,12 +153,12 @@ export const selectLocations = createSelector( [selectEvents], (events) => { - const selectedLocations = {}; + const selectedLocations = {} events.forEach(event => { - const location = event.location; + const location = event.location if (selectedLocations[location]) { - selectedLocations[location].events.push(event); + selectedLocations[location].events.push(event) } else { selectedLocations[location] = { label: location, @@ -158,9 +169,9 @@ export const selectLocations = createSelector( } }) - return Object.values(selectedLocations); + return Object.values(selectedLocations) } -); +) /** * Of all the sources, select those that are relevant to the selected events. @@ -176,7 +187,7 @@ export const selectSelected = createSelector( const srcs = selected .map(e => e.sources) .map(_sources => { - if (!_sources) return []; + if (!_sources) return [] return _sources.map(id => ( sources.hasOwnProperty(id) ? sources[id] : null )) @@ -196,7 +207,7 @@ export const selectSelected = createSelector( export const selectCategories = createSelector( [getCategories], (categories) => categories -); +) /** @@ -206,23 +217,23 @@ export const selectCategories = createSelector( export const selectTagList = createSelector( [getTagTree], (tags) => { - const tagList = []; - let depth = 0; + const tagList = [] + let depth = 0 function traverseNode(node, depth) { - node.active = (!node.hasOwnProperty('active')) ? false : node.active; - node.depth = depth; + node.active = (!node.hasOwnProperty('active')) ? false : node.active + node.depth = depth if (node.active) tagList.push(node) if (Object.keys(node.children).length > 0) { Object.values(node.children).forEach((childNode) => { - traverseNode(childNode, depth + 1); - }); + traverseNode(childNode, depth + 1) + }) } } if (tags && tags !== undefined) { if (tags.key && tags.children) traverseNode(tags, depth) } - return tagList; + return tagList } ) diff --git a/src/store/initial.js b/src/store/initial.js index 1c0a624..c18baa0 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -33,6 +33,9 @@ const initial = { selected: [], source: null, narrative: null, + narrativeState: { + current: null + }, filters: { timerange: [ d3.timeParse("%Y-%m-%dT%H:%M:%S")("2013-02-23T12:00:00"),