diff --git a/src/actions/index.js b/src/actions/index.js
index 4f98eaa..1b24f49 100644
--- a/src/actions/index.js
+++ b/src/actions/index.js
@@ -171,26 +171,20 @@ export function updateDistrict (district) {
}
}
-export const CLEAR_TAGFILTERS = 'CLEAR_TAGFILTERS'
-export function clearTagFilters () {
+export const CLEAR_FILTER = 'CLEAR_FILTER'
+export function clearFilter (filter) {
return {
- type: CLEAR_TAGFILTERS
+ type: CLEAR_FILTER,
+ filter
}
}
-export const TOGGLE_TAGFILTER = 'TOGGLE_TAGFILTER'
-export function toggleTagFilter (tag) {
+export const TOGGLE_FILTER = 'TOGGLE_FILTER'
+export function toggleFilter (filter, value) {
return {
- type: TOGGLE_TAGFILTER,
- tag
- }
-}
-
-export const UPDATE_CATEGORYFILTERS = 'UPDATE_CATEGORYFILTERS'
-export function updateCategoryFilters (category) {
- return {
- type: UPDATE_CATEGORYFILTERS,
- category
+ type: TOGGLE_FILTER,
+ filter,
+ value
}
}
diff --git a/src/components/CategoriesListPanel.jsx b/src/components/CategoriesListPanel.js
similarity index 58%
rename from src/components/CategoriesListPanel.jsx
rename to src/components/CategoriesListPanel.js
index a295aa0..3f4a63c 100644
--- a/src/components/CategoriesListPanel.jsx
+++ b/src/components/CategoriesListPanel.js
@@ -2,16 +2,16 @@ import React from 'react'
import Checkbox from './presentational/Checkbox'
import copy from '../js/data/copy.json'
-export default (props) => {
- function onClickCheckbox (obj, type) {
- obj.active = !obj.active
- props.onCategoryFilter(obj)
- }
-
+export default ({
+ categories,
+ activeCategories,
+ onCategoryFilter,
+ language
+}) => {
function renderCategoryTree () {
return (
- {props.categories.map(cat => {
+ {categories.map(cat => {
return (
{
>
onClickCheckbox(cat, 'category')}
+ isActive={activeCategories.includes(cat.category)}
+ onClickCheckbox={() => onCategoryFilter(cat.category)}
/>
)
})}
@@ -30,8 +30,8 @@ export default (props) => {
return (
-
{copy[props.language].toolbar.categories}
-
{copy[props.language].toolbar.explore_by_category__description}
+
{copy[language].toolbar.categories}
+
{copy[language].toolbar.explore_by_category__description}
{renderCategoryTree()}
)
diff --git a/src/components/Layout.js b/src/components/Layout.js
index fcca00c..961d115 100644
--- a/src/components/Layout.js
+++ b/src/components/Layout.js
@@ -120,8 +120,8 @@ class Dashboard extends React.Component {
actions.toggleFilter('tags', tag),
+ onCategoryFilter: category => actions.toggleFilter('categories', category),
onSelectNarrative: this.setNarrative
}}
/>
diff --git a/src/components/Map.jsx b/src/components/Map.jsx
index a288514..23401da 100644
--- a/src/components/Map.jsx
+++ b/src/components/Map.jsx
@@ -203,7 +203,7 @@ class Map extends React.Component {
return (
onTagFilter(node.key)}
/>
diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx
index 2f489ea..f05eeeb 100644
--- a/src/components/Timeline.jsx
+++ b/src/components/Timeline.jsx
@@ -349,7 +349,7 @@ function mapStateToProps (state) {
isNarrative: !!state.app.narrative,
domain: {
datetimes: selectors.selectDatetimes(state),
- categories: selectors.selectCategories(state),
+ categories: selectors.getCategories(state),
narratives: state.domain.narratives
},
app: {
diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx
index 81043ab..39a1fc2 100644
--- a/src/components/Toolbar.jsx
+++ b/src/components/Toolbar.jsx
@@ -7,8 +7,8 @@ import * as selectors from '../selectors'
import { Tabs, TabPanel } from 'react-tabs'
import Search from './Search.jsx'
import TagListPanel from './TagListPanel'
-import CategoriesListPanel from './CategoriesListPanel.jsx'
-import ToolbarBottomActions from './ToolbarBottomActions.jsx'
+import CategoriesListPanel from './CategoriesListPanel'
+import ToolbarBottomActions from './ToolbarBottomActions'
import copy from '../js/data/copy.json'
import { trimAndEllipse } from '../js/utilities.js'
@@ -78,7 +78,7 @@ class Toolbar extends React.Component {
@@ -94,7 +94,7 @@ class Toolbar extends React.Component {
@@ -199,8 +199,8 @@ function mapStateToProps (state) {
categories: selectors.getCategories(state),
narratives: selectors.selectNarratives(state),
language: state.app.language,
- tagFilters: selectors.getTagsFilter(state),
- categoryFilters: selectors.selectCategories(state),
+ activeTags: selectors.getActiveTags(state),
+ activeCategories: selectors.getActiveCategories(state),
viewFilters: state.app.filters.views,
features: state.app.features,
narrative: state.app.narrative,
diff --git a/src/components/ToolbarBottomActions.jsx b/src/components/ToolbarBottomActions.js
similarity index 100%
rename from src/components/ToolbarBottomActions.jsx
rename to src/components/ToolbarBottomActions.js
diff --git a/src/reducers/app.js b/src/reducers/app.js
index 0cab34b..9fc7d0d 100644
--- a/src/reducers/app.js
+++ b/src/reducers/app.js
@@ -5,9 +5,8 @@ import { parseDate, toggleFlagAC } from '../js/utilities'
import {
UPDATE_HIGHLIGHTED,
UPDATE_SELECTED,
- CLEAR_TAGFILTERS,
- TOGGLE_TAGFILTER,
- UPDATE_CATEGORYFILTERS,
+ CLEAR_FILTER,
+ TOGGLE_FILTER,
UPDATE_TIMERANGE,
UPDATE_NARRATIVE,
INCREMENT_NARRATIVE_CURRENT,
@@ -129,39 +128,22 @@ function clearTagFilters (appState) {
}
}
-function toggleTagFilter (appState, action) {
- let newTags = appState.filters.tags.slice(0)
- if (newTags.includes(action.tag)) {
- newTags = newTags.filter(s => s !== action.tag)
+function toggleFilter (appState, action) {
+ let newTags = appState.filters[action.filter].slice(0)
+ if (newTags.includes(action.value)) {
+ newTags = newTags.filter(s => s !== action.value)
} else {
- newTags.push(action.tag)
+ newTags.push(action.value)
}
return {
...appState,
filters: {
...appState.filters,
- tags: newTags
+ [action.filter]: newTags
}
}
}
-function updateCategoryFilters (appState, action) {
- const categoryFilters = appState.filters.categories.slice(0)
-
- const catFilter = categoryFilters.find(cF => cF.category === action.category.category)
-
- if (!catFilter) {
- categoryFilters.push(action.category)
- } else {
- catFilter.active = (!!action.category.active)
- }
-
- return Object.assign({}, appState, {
- filters: Object.assign({}, appState.filters, {
- categories: categoryFilters
- })
- })
-}
function updateTimeRange (appState, action) { // XXX
return {
@@ -232,12 +214,10 @@ function app (appState = initial.app, action) {
return updateHighlighted(appState, action)
case UPDATE_SELECTED:
return updateSelected(appState, action)
- case CLEAR_TAGFILTERS:
- return clearTagFilters(appState)
- case TOGGLE_TAGFILTER:
- return toggleTagFilter(appState, action)
- case UPDATE_CATEGORYFILTERS:
- return updateCategoryFilters(appState, action)
+ case CLEAR_FILTER:
+ return clearFilter(appState, action)
+ case TOGGLE_FILTER:
+ return toggleFilter(appState, action)
case UPDATE_TIMERANGE:
return updateTimeRange(appState, action)
case UPDATE_NARRATIVE:
diff --git a/src/selectors/helpers.js b/src/selectors/helpers.js
new file mode 100644
index 0000000..af6190c
--- /dev/null
+++ b/src/selectors/helpers.js
@@ -0,0 +1,16 @@
+import { parseTimestamp } from '../js/utilities'
+/**
+* Some handy helpers
+*/
+
+/**
+ * Given an event and a time range,
+ * returns true/false if the event falls within timeRange
+ */
+export function isTimeRangedIn (event, timeRange) {
+ const eventTime = parseTimestamp(event.timestamp)
+ return (
+ timeRange[0] < eventTime &&
+ eventTime < timeRange[1]
+ )
+}
diff --git a/src/selectors/index.js b/src/selectors/index.js
index ca5258d..e9a8399 100644
--- a/src/selectors/index.js
+++ b/src/selectors/index.js
@@ -1,5 +1,6 @@
import { createSelector } from 'reselect'
-import { parseTimestamp, compareTimestamp, insetSourceFrom } from '../js/utilities'
+import { compareTimestamp, insetSourceFrom } from '../js/utilities'
+import { isTaggedIn, isNoTags, isTaggedInWithCategory, isNoCategories, isTimeRangedIn } from './helpers'
// Input selectors
export const getEvents = state => state.domain.events
@@ -22,92 +23,28 @@ export const getShapes = state => {
}
export const getNotifications = state => state.domain.notifications
export const getTagTree = state => state.domain.tags
-export const getTagsFilter = state => state.app.filters.tags
-export const getCategoriesFilter = state => state.app.filters.categories
+export const getActiveTags = state => state.app.filters.tags
+export const getActiveCategories = state => state.app.filters.categories
export const getTimeRange = state => state.app.timeline.range
export const selectNarrative = state => state.app.narrative
/**
-* Some handy helpers
-*/
-
-/**
- * Given an event and all tags,
- * returns true/false if event has any tag that is active
- */
-function isTaggedIn (event, tagFilters) {
- if (event.tags) {
- const isTagged = event.tags.some((tag) => {
- return tagFilters.find(tF => (tF.key === tag && tF.active))
- })
- return isTagged
- } else {
- return false
- }
-}
-
-/**
- * Given an event and all categories,
- * returns true/false if event has a category that is active
- */
-function isTaggedInWithCategory (event, categories) {
- if (event.category) {
- if (categories.find(c => (c.category === event.category && c.active))) return true
- return false
- } else {
- return false
- }
-}
-
-/*
-* Returns true if no tags are selected
-*/
-function isNoTags (tagFilters) {
- return (
- tagFilters.length === 0 ||
- !process.env.features.USE_TAGS ||
- tagFilters.every(t => !t.active)
- )
-}
-
-/*
-* Returns true if no categories are selected
-*/
-function isNoCategories (categories) {
- return (
- categories.length === 0 ||
- !process.env.features.CATEGORIES_AS_TAGS ||
- categories.every(c => !c.active)
- )
-}
-
-/**
- * Given an event and a time range,
- * returns true/false if the event falls within timeRange
- */
-function isTimeRangedIn (event, timeRange) {
- const eventTime = parseTimestamp(event.timestamp)
- return (
- timeRange[0] < eventTime &&
- eventTime < timeRange[1]
- )
-}
-
-/**
- * Of all available events, selects those that fall within the time range,
- * and if TAGS are being used, select them if their tags are enabled
+ * Of all available events, selects those that
+ * 1. fall in time range
+ * 2. exist in an active tag
+ * 3. exist in an active category
*/
export const selectEvents = createSelector(
- [getEvents, getTagsFilter, getCategoriesFilter, getTimeRange],
- (events, tagFilters, categories, timeRange) => {
+ [getEvents, getActiveTags, getActiveCategories, getTimeRange],
+ (events, activeTags, activeCategories, timeRange) => {
return events.reduce((acc, event) => {
- const isTagged = isTaggedIn(event, tagFilters) || isNoTags(tagFilters)
- const isTaggedWithCategory = isTaggedInWithCategory(event, categories) || isNoCategories(categories)
- const isTimeRanged = isTimeRangedIn(event, timeRange)
+ const isMatchingTag = event.tags.map(tag => activeTags.includes(tag)).some(s => s)
+ const isActiveTag = isMatchingTag || activeTags.length === 0
+ const isActiveCategory = activeCategories.includes(event.category) || activeCategories.length === 0
+ const isActiveTime = isTimeRangedIn(event, timeRange)
- if (isTimeRanged && isTagged && isTaggedWithCategory) {
- const eventClone = Object.assign({}, event)
- acc[event.id] = eventClone
+ if (isActiveTime && isActiveTag && isActiveCategory) {
+ acc[event.id] = { ...event }
}
return acc
@@ -201,28 +138,6 @@ export const selectLocations = createSelector(
}
)
-export const selectVisibleLocations = createSelector(
- [selectLocations, getTagsFilter, selectNarrative],
- (locations, filters, narrative) => {
- if (filters.length === 0) {
- return locations
- }
-
- return locations.map(loc => {
- loc.events = loc.events.filter(ev => {
- let isShowing = false
- ev.tags.forEach(tag => {
- if (filters.includes(tag)) {
- isShowing = true
- }
- })
- return isShowing
- })
- return loc
- })
- }
-)
-
/**
* Group events by 'datetime'. Each datetime is an object:
{
@@ -266,44 +181,3 @@ export const selectSelected = createSelector(
return selected.map(insetSourceFrom(sources))
}
)
-
-/*
-* Select categories, return them as a list
-*/
-export const selectCategories = createSelector(
- [getCategories],
- (categories) => {
- categories.map(cat => {
- cat.active = (!cat.hasOwnProperty('active')) ? false : cat.active
- })
- return categories
- }
-)
-
-/**
- * Given a tree of tags, return those tags as a list
- * Each node has been aware of its depth, and given an 'active' flag
- */
-// export const selectTagList = createSelector(
-// [getTagTree],
-// (tags) => {
-// const tagList = []
-// let depth = 0
-// function traverseNode (node, 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)
-// })
-// }
-// }
-// if (tags && tags !== undefined) {
-// if (tags.key && tags.children) traverseNode(tags, depth)
-// }
-// return tagList
-// }
-// )