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"),