diff --git a/docs/configuration.md b/docs/configuration.md index a27a012..561f6f0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,7 +2,7 @@ **NOTE: WIP. These settings are currently slightly out of date.** -In order to make timemap interesting, you need to configure it to read events. When loaded in a browser, timemap queries HTTP endpoint, expecting from them well-defined JSON objects. There are certain endpoints, such as `events`, that are required, while others , such as `tags`, are optional; when provided, they enhance a timemap instance with additional features and capabilities related to the additional data. +In order to make timemap interesting, you need to configure it to read events. When loaded in a browser, timemap queries HTTP endpoint, expecting from them well-defined JSON objects. There are certain endpoints, such as `events`, that are required, while others , such as `filters`, are optional; when provided, they enhance a timemap instance with additional features and capabilities related to the additional data. The URLs for these endpoints, as well as other configurable settings in your timemap instance, are read from the `config.js` that you created in step 3 of the setup above. The example contains sensible defaults. This section covers each option in more detail: @@ -14,11 +14,11 @@ The URLs for these endpoints, as well as other configurable settings in your tim | EVENT_DESC_ROOT | Endpoint for additional metadata for each individual event, concatenated to SERVER_ROOT | String | Yes | | CATEGORY_EXT | Endpoint for categories, concatenated with SERVER_ROOT | String | Yes | | NARRATIVE_EXT | Endpoint for narratives, concatenated with SERVER_ROOT | String | No | -| TAG_TREE_EXT | Endpoint for tags, concatenated with SERVER_ROOT | String | Yes | +| FILTER_TREE_EXT | Endpoint for filters, concatenated with SERVER_ROOT | String | Yes | | SITES_EXT | Endpoint for sites, concatenated with SERVER_ROOT | String | Yes | | MAP_ANCHOR | Geographic coordinates for original map anchor | Array of numbers | No | | MAPBOX_TOKEN | Access token for Mapbox satellite imagery | String | No | -| features.USE_TAGS | Enable / Disable tags | boolean | No | +| features.USE_FILTERS | Enable / Disable filters | boolean | No | | features.USE_SEARCH | Enable / Disable search | boolean | No | | features.USE_SITES | Enable / Disable sites | boolean | No | @@ -47,7 +47,7 @@ a `config.js` file in timemap's root folder (explained in the next section). "lat":"17.810358", "long":"-18.2251664", "source":"", - "tags": "", + "filters": "", "category": "" } ] @@ -69,35 +69,35 @@ a `config.js` file in timemap's root folder (explained in the next section). #### Optional endpoints -3. **Tags**: `events` can be tagged by multiple `tags`. These will further characterize the event, and allow to select or deselect based on them. Tags are or can be distributed in a tree-like hierarchy, and each node on the tree can be a tag, including those who are not leafs. +3. **Filters**: `events` can be filterged by multiple `filters`. These will further characterize the event, and allow to select or deselect based on them. Filters are or can be distributed in a tree-like hierarchy, and each node on the tree can be a filter, including those who are not leafs. ```json { - "key":"tags", + "key":"filters", "children": { - "tag0": { - "key": "tag0 ", + "filter0": { + "key": "filter0 ", "children": { - "tag00": { - "key": "tag00", + "filter00": { + "key": "filter00", "children": { - "tag001": { - "key": "tag001", + "filter001": { + "key": "filter001", "children": {} } } }, - "tag01": { - "key": "tag01", + "filter01": { + "key": "filter01", "children": {} } } }, - "tag1": { - "key": "tag1", + "filter1": { + "key": "filter1", "children": { - "tag10": { - "key": "tag10", + "filter10": { + "key": "filter10", "children": {} } } @@ -106,7 +106,7 @@ a `config.js` file in timemap's root folder (explained in the next section). } ``` -4. **Sites**: sites are labels on the map, aiming to highlight particularly relevant locations that should not be a function of time or tags. +4. **Sites**: sites are labels on the map, aiming to highlight particularly relevant locations that should not be a function of time or filters. ```json [ diff --git a/src/actions/index.js b/src/actions/index.js index 3dd6078..5bcdd85 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -4,7 +4,7 @@ import { urlFromEnv } from '../common/utilities' // TODO: relegate these URLs entirely to environment variables const EVENT_DATA_URL = urlFromEnv('EVENT_EXT') const CATEGORY_URL = urlFromEnv('CATEGORY_EXT') -const TAGS_URL = urlFromEnv('TAGS_EXT') +const FILTERS_URL = urlFromEnv('FILTERS_EXT') const SOURCES_URL = urlFromEnv('SOURCES_EXT') const NARRATIVE_URL = urlFromEnv('NARRATIVE_EXT') const SITES_URL = urlFromEnv('SITES_EXT') @@ -49,14 +49,14 @@ export function fetchDomain () { .catch(() => handleError(domainMsg('sites'))) } - let tagsPromise = Promise.resolve([]) + let filtersPromise = Promise.resolve([]) if (features.USE_FILTERS) { - if (!TAGS_URL) { - tagsPromise = Promise.resolve(handleError('USE_TAGS is true, but you have not provided a TAGS_EXT')) + if (!FILTERS_URL) { + filtersPromise = Promise.resolve(handleError('USE_FILTERS is true, but you have not provided a FILTERS_EXT')) } else { - tagsPromise = fetch(TAGS_URL) + filtersPromise = fetch(FILTERS_URL) .then(response => response.json()) - .catch(() => handleError(domainMsg('tags'))) + .catch(() => handleError(domainMsg('filters'))) } } @@ -83,7 +83,7 @@ export function fetchDomain () { catPromise, narPromise, sitesPromise, - tagsPromise, + filtersPromise, sourcesPromise, shapesPromise ]) @@ -93,7 +93,7 @@ export function fetchDomain () { categories: response[1], narratives: response[2], sites: response[3], - tags: response[4], + filters: response[4], sources: response[5], shapes: response[6], notifications diff --git a/src/common/data/copy.json b/src/common/data/copy.json index 44999f9..f965823 100644 --- a/src/common/data/copy.json +++ b/src/common/data/copy.json @@ -19,9 +19,9 @@ "toolbar": { "title": "TITLE", "categories": "Categories", - "tags": "Tags", - "explore_by_tag__title": "Explore by tag or category", - "explore_by_tag__description": "Selecting tags or categories, you'll see only those events that are tagged accordingly. If you select nothing, as well as everything, all data will be displayed.", + "filters": "Filters", + "explore_by_filter__title": "Explore by filter or category", + "explore_by_filter__description": "Selecting filters or categories, you'll see only those events that are tagged accordingly. If you select nothing, as well as everything, all data will be displayed.", "panels": { "mentions": { "title": "Personas", @@ -80,7 +80,7 @@ "legend": { "view2d": { "paragraphs": [ - "Selecting a series of tags, you will be able to explore events on the map of Iguala and on the timeline.", + "Selecting a series of filters, you will be able to explore events on the map of Iguala and on the timeline.", "Each event is colored according the person that gave category of the event." ], "colors": [ @@ -114,17 +114,17 @@ "overview": "Selecting the name of a person will show the events only according to a person’s category or category. The number in the parentheses show how many events are contained in each category, e.g. (34)." }, "search": { - "title": "Directory of tags", + "title": "Directory of filters", "placeholder": "Search" } }, "narratives_label": "Narratives", "narrative_summary": "Follow a path through the data, from one key event to the next.", "categories": "Categories", - "tags": "Filters", - "tags_label": "Filters", - "explore_by_tag__title": "Explore by filter", - "explore_by_tag__description": "Selecting a filter will show you only those events that are annotated with the filter. If you select nothing, as well as everything, all data will be displayed.", + "filters": "Filters", + "filters_label": "Filters", + "explore_by_filter__title": "Explore by filter", + "explore_by_filter__description": "Selecting a filter will show you only those events that are annotated with the filter. If you select nothing, as well as everything, all data will be displayed.", "explore_by_category__title": "Explore events by category", "explore_by_category__description": "" @@ -158,8 +158,8 @@ "location": "Localization", "incident_type": "Type of action", "description": "Summary", - "tags": "Tags", - "notags": "No known tags for this event.", + "filters": "Filters", + "nofilters": "No known filters for this event.", "sources": "Sources", "unknown_source": "The information for this source could not be retrieved.", "category": "Category", diff --git a/src/components/Card.jsx b/src/components/Card.jsx index 735540c..f56ad2d 100644 --- a/src/components/Card.jsx +++ b/src/components/Card.jsx @@ -4,7 +4,7 @@ import React from 'react' import CardTime from './presentational/Card/Time' import CardLocation from './presentational/Card/Location' import CardCaret from './presentational/Card/Caret' -import CardTags from './presentational/Card/Tags' +import CardFilters from './presentational/Card/Filters' import CardSummary from './presentational/Card/Summary' import CardSource from './presentational/Card/Source' import CardNarrative from './presentational/Card/Narrative' @@ -38,13 +38,13 @@ class Card extends React.Component { ) } - renderTags () { - if (!this.props.tags || (this.props.tags && this.props.tags.length === 0)) { + renderFilters () { + if (!this.props.filters || (this.props.filters && this.props.filters.length === 0)) { return null } return ( - ) @@ -137,7 +137,7 @@ class Card extends React.Component { renderExtra () { return (
- {this.renderTags()} + {this.renderFilters()} {this.renderSources()} {this.renderNarrative()}
diff --git a/src/components/Layout.js b/src/components/Layout.js index 863b36e..fcc20bf 100644 --- a/src/components/Layout.js +++ b/src/components/Layout.js @@ -104,7 +104,7 @@ class Dashboard extends React.Component { setNarrative (narrative) { // only handleSelect if narrative is not null if (narrative) { - this.props.actions.clearFilter('tags') + this.props.actions.clearFilter('filters') this.props.actions.clearFilter('categories') this.handleSelect([ narrative.steps[0] ]) } @@ -152,7 +152,7 @@ class Dashboard extends React.Component { isNarrative={!!app.narrative} methods={{ onTitle: actions.toggleCover, - onTagFilter: tag => actions.toggleFilter('tags', tag), + onSelectFilter: filter => actions.toggleFilter('filters', filter), onCategoryFilter: category => actions.toggleFilter('categories', category), onSelectNarrative: this.setNarrative }} diff --git a/src/components/Map.jsx b/src/components/Map.jsx index 59fcd1c..843693f 100644 --- a/src/components/Map.jsx +++ b/src/components/Map.jsx @@ -205,6 +205,7 @@ class Map extends React.Component { return ( { return (
  • {/* */} - {/* */} + {/* */} {/* */} {/* */} {/* */} onTagFilter(matchingKeys)} + isActive={activeFilters.includes(node.key)} + onClickCheckbox={() => onSelectFilter(matchingKeys)} /> {children.length > 0 - ? children.map(tag => createNodeComponent(tag, depth + 1)) + ? children.map(filter => createNodeComponent(filter, depth + 1)) : null}
  • ) @@ -45,18 +45,18 @@ function TagListPanel ({ function renderTree (children) { return (
    - {Object.values(children).map(tag => createNodeComponent(tag, 1))} + {Object.values(children).map(filter => createNodeComponent(filter, 1))}
    ) } return (
    -

    {copy[language].toolbar.tags}

    -

    {copy[language].toolbar.explore_by_tag__description}

    - {renderTree(tags.children)} +

    {copy[language].toolbar.filters}

    +

    {copy[language].toolbar.explore_by_filter__description}

    + {renderTree(filters.children)}
    ) } -export default TagListPanel +export default FilterListPanel diff --git a/src/components/Toolbar/Layout.js b/src/components/Toolbar/Layout.js index 13ee5ee..430f30d 100644 --- a/src/components/Toolbar/Layout.js +++ b/src/components/Toolbar/Layout.js @@ -6,7 +6,7 @@ import * as selectors from '../../selectors' import { Tabs, TabPanel } from 'react-tabs' import Search from './Search' -import TagListPanel from './TagListPanel' +import FilterListPanel from './FilterListPanel' import CategoriesListPanel from './CategoriesListPanel' import BottomActions from './BottomActions' import copy from '../../common/data/copy.json' @@ -37,9 +37,9 @@ class Toolbar extends React.Component { @@ -73,7 +73,7 @@ class Toolbar extends React.Component { } renderToolbarCategoriesPanel () { - if (this.props.features.CATEGORIES_AS_TAGS) { + if (this.props.features.CATEGORIES_AS_FILTERS) { return ( - @@ -120,7 +120,7 @@ class Toolbar extends React.Component { {this.renderClosePanel()} {features.USE_NARRATIVES ? this.renderToolbarNarrativePanel() : null} - {features.CATEGORIES_AS_TAGS ? this.renderToolbarCategoriesPanel() : null} + {features.CATEGORIES_AS_FILTERS ? this.renderToolbarCategoriesPanel() : null} {features.USE_FILTERS ? this.renderToolbarFilterPanel() : null} @@ -149,7 +149,7 @@ class Toolbar extends React.Component { let title = copy[this.props.language].toolbar.title if (process.env.display_title) title = process.env.display_title const narrativesLabel = copy[this.props.language].toolbar.narratives_label - const tagsLabel = copy[this.props.language].toolbar.tags_label + const filtersLabel = copy[this.props.language].toolbar.filters_label const categoriesLabel = 'Categories' // TODO: return ( @@ -157,8 +157,8 @@ class Toolbar extends React.Component {

    {title}

    {features.USE_NARRATIVES ? this.renderToolbarTab(0, narrativesLabel, 'timeline') : null} - {features.CATEGORIES_AS_TAGS ? this.renderToolbarTab(1, categoriesLabel, 'widgets') : null} - {features.USE_FILTERS ? this.renderToolbarTab(2, tagsLabel, 'filter_list') : null} + {features.CATEGORIES_AS_FILTERS ? this.renderToolbarTab(1, categoriesLabel, 'widgets') : null} + {features.USE_FILTERS ? this.renderToolbarTab(features.CATEGORIES_AS_FILTERS ? 2 : 1, filtersLabel, 'filter_list') : null}
    response.json()) .then(json => { this.setState({ - searchResults: json.tags + searchResults: json.filters }) }) } @@ -32,16 +32,16 @@ export default class Search extends React.Component { renderSearchResults () { return ( - this.state.searchResults.map(tag => { + this.state.searchResults.map(filter => { return ( - ) diff --git a/src/components/Toolbar/TagFilter.js b/src/components/Toolbar/SelectFilter.js similarity index 51% rename from src/components/Toolbar/TagFilter.js rename to src/components/Toolbar/SelectFilter.js index a27f0b0..9c18906 100644 --- a/src/components/Toolbar/TagFilter.js +++ b/src/components/Toolbar/SelectFilter.js @@ -1,22 +1,22 @@ import React from 'react' import Checkbox from '../presentational/Checkbox' -function TagFilter (props) { +function SelectFilter (props) { function isActive () { if (props.isCategory) { - return props.categoryFilters.includes(props.tag.id) + return props.categoryFilters.includes(props.filter.id) } - return props.tagFilters.includes(props.tag.id) + return props.filterFilters.includes(props.filter.id) } - function onClickTag () { + function onClickFilter () { if (isActive()) { props.filter({ - tags: props.tagFilters.filter(element => element !== props.tag.id) + filters: props.filterFilters.filter(element => element !== props.filter.id) }) } else { props.filter({ - tags: props.tagFilters.concat(props.tag.id) + filters: props.filterFilters.concat(props.filter.id) }) } } @@ -24,44 +24,44 @@ function TagFilter (props) { function onClickCategory () { if (isActive()) { props.filter({ - categories: props.categoryFilters.filter(element => element !== props.tag.id) + categories: props.categoryFilters.filter(element => element !== props.filter.id) }) } else { props.filter({ - categories: props.categoryFilters.concat(props.tag.id) + categories: props.categoryFilters.concat(props.filter.id) }) } } - function renderTag () { - const tag = props.tag - let classes = (isActive()) ? 'tag-filter active' : 'tag-filter' - let label = `${tag.name} ( ${tag.mentions} )` + function renderFilter () { + const filter = props.filter + let classes = (isActive()) ? 'filter-filter active' : 'filter-filter' + let label = `${filter.name} ( ${filter.mentions} )` if (props.isShowTree) { - label = `${tag.group} > ${tag.subgroup} > ${tag.name} ( ${tag.mentions} )` + label = `${filter.group} > ${filter.subgroup} > ${filter.name} ( ${filter.mentions} )` } return (
  • onClickTag()} + onClickCheckbox={() => onClickFilter()} />
  • ) } function renderCategory () { - const category = props.categories[props.tag.id] - let classes = (isActive()) ? 'tag-filter active' : 'tag-filter' + const category = props.categories[props.filter.id] + let classes = (isActive()) ? 'filter-filter active' : 'filter-filter' if (category) { return (
  • { + const filtersLang = copy[language].cardstack.filters + const noFiltersLang = copy[language].cardstack.nofilters + + if (filters.length > 0) { + return ( +
    +

    {filtersLang}:

    +

    + {filters.map((filter, idx) => { + return ( + + {filter.name} + {(idx < filters.length - 1) + ? ',' + : ''} + + ) + })} +

    +
    + ) + } + return ( +
    +

    {filtersLang}

    +

    {noFiltersLang}

    +
    + ) +} + +export default CardFilters diff --git a/src/components/presentational/Card/Tags.js b/src/components/presentational/Card/Tags.js deleted file mode 100644 index ae9b711..0000000 --- a/src/components/presentational/Card/Tags.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' - -import copy from '../../../common/data/copy.json' - -const CardTags = ({ tags, language }) => { - const tagsLang = copy[language].cardstack.tags - const noTagsLang = copy[language].cardstack.notags - - if (tags.length > 0) { - return ( -
    -

    {tagsLang}:

    -

    - {tags.map((tag, idx) => { - return ( - - {tag.name} - {(idx < tags.length - 1) - ? ',' - : ''} - - ) - })} -

    -
    - ) - } - return ( -
    -

    {tagsLang}

    -

    {noTagsLang}

    -
    - ) -} - -export default CardTags diff --git a/src/components/presentational/Map/Events.jsx b/src/components/presentational/Map/Events.jsx index 7f4c12a..33e949d 100644 --- a/src/components/presentational/Map/Events.jsx +++ b/src/components/presentational/Map/Events.jsx @@ -3,7 +3,17 @@ import { Portal } from 'react-portal' import colors from '../../../common/global.js' import { calcOpacity } from '../../../common/utilities' -function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, selected, narrative, onSelect, svg, locations }) { +function MapEvents ({ + getCategoryColor, + categories, + projectPoint, + styleLocation, + selected, + narrative, + onSelect, + svg, + locations +}) { function getCoordinatesForPercent (radius, percent) { const x = radius * Math.cos(2 * Math.PI * percent) const y = radius * Math.sin(2 * Math.PI * percent) @@ -38,7 +48,7 @@ function MapEvents ({ getCategoryColor, categories, projectPoint, styleLocation, ...extraStyles }) - const colorSlices = location.events.map(e => getCategoryColor(e.category)) + const colorSlices = location.events.map(e => e.colour ? e.colour : getCategoryColor(e.category)) let cumulativeAngleSweep = 0 diff --git a/src/components/presentational/Timeline/Events.js b/src/components/presentational/Timeline/Events.js index 1159fec..deb3145 100644 --- a/src/components/presentational/Timeline/Events.js +++ b/src/components/presentational/Timeline/Events.js @@ -109,7 +109,7 @@ const TimelineEvents = ({ y: eventY, onSelect: () => onSelect(event), dims, - highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.tags[features.HIGHLIGHT_GROUPS.tagIndexIndicatingGroup]) : [], + highlights: features.HIGHLIGHT_GROUPS ? getHighlights(event.filters[features.HIGHLIGHT_GROUPS.filterIndexIndicatingGroup]) : [], features }) } diff --git a/src/reducers/app.js b/src/reducers/app.js index 0de7e37..3ece12f 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -124,12 +124,12 @@ function toggleFilter (appState, action) { action.value = [action.value] } - let newTags = appState.filters[action.filter].slice(0) + let newFilters = appState.filters[action.filter].slice(0) action.value.forEach(vl => { - if (newTags.includes(vl)) { - newTags = newTags.filter(s => s !== vl) + if (newFilters.includes(vl)) { + newFilters = newFilters.filter(s => s !== vl) } else { - newTags.push(vl) + newFilters.push(vl) } }) @@ -137,7 +137,7 @@ function toggleFilter (appState, action) { ...appState, filters: { ...appState.filters, - [action.filter]: newTags + [action.filter]: newFilters } } } diff --git a/src/reducers/validate/eventSchema.js b/src/reducers/validate/eventSchema.js index b96f8cd..b0532ef 100644 --- a/src/reducers/validate/eventSchema.js +++ b/src/reducers/validate/eventSchema.js @@ -14,6 +14,7 @@ const eventSchema = Joi.object().keys({ category_full: Joi.string().allow(''), narratives: Joi.array(), sources: Joi.array(), + filters: Joi.array().allow(''), tags: Joi.array().allow(''), comments: Joi.string().allow(''), time_display: Joi.string().allow(''), diff --git a/src/reducers/validate/validators.js b/src/reducers/validate/validators.js index a70860f..a89d0be 100644 --- a/src/reducers/validate/validators.js +++ b/src/reducers/validate/validators.js @@ -25,7 +25,7 @@ const isLeaf = node => (Object.keys(node.children).length === 0) const isDuplicate = (node, set) => { return (set.has(node.key)) } /* -* Traverse a tag tree and check its duplicates +* Traverse a filter tree and check its duplicates */ function validateTree (node, parent, set, duplicates) { if (!Array.isArray(node) || !node.length) { @@ -36,7 +36,7 @@ function validateTree (node, parent, set, duplicates) { if (isDuplicate(node, set)) { duplicates.push({ id: node.key, - error: makeError('Tags', node.key, 'tag was found more than once in hierarchy. Ignoring duplicate.') + error: makeError('Filters', node.key, 'filter was found more than once in hierarchy. Ignoring duplicate.') }) delete parent.children[node.key] } else { @@ -60,9 +60,13 @@ export function validateDomain (domain) { sites: [], narratives: [], sources: {}, - tags: {}, + filters: {}, shapes: [], - notifications: domain.notifications + notifications: domain ? domain.notifications : null + } + + if (domain === undefined) { + return sanitizedDomain } const discardedDomain = { @@ -89,6 +93,12 @@ export function validateDomain (domain) { function validateArray (items, domainKey, schema) { items.forEach(item => { + // NB: backwards compatibility with 'tags' for 'filters' + if (domainKey === 'events') { + if (!item.filters && !!item.tags) { + item.filters = item.tags + } + } validateArrayItem(item, domainKey, schema) }) } @@ -138,20 +148,20 @@ export function validateDomain (domain) { } }) - // Validate uniqueness of tags - const tagSet = new Set([]) - const duplicateTags = [] - validateTree(domain.tags, {}, tagSet, duplicateTags) + // Validate uniqueness of filters + const filterSet = new Set([]) + const duplicateFilters = [] + validateTree(domain.filters, {}, filterSet, duplicateFilters) - // Duplicated tags - if (duplicateTags.length > 0) { + // Duplicated filters + if (duplicateFilters.length > 0) { sanitizedDomain.notifications.push({ - message: `Tags are required to be unique. Ignoring duplicates for now.`, - items: duplicateTags, + message: `Filters are required to be unique. Ignoring duplicates for now.`, + items: duplicateFilters, type: 'error' }) } - sanitizedDomain.tags = domain.tags + sanitizedDomain.filters = domain.filters // append events with datetime and sort sanitizedDomain.events.forEach(event => { diff --git a/src/scss/card.scss b/src/scss/card.scss index 3c899c3..4f88028 100644 --- a/src/scss/card.scss +++ b/src/scss/card.scss @@ -154,7 +154,7 @@ } } - .tags { + .filters { width: 100%; margin: 5px 0; text-align: left; @@ -206,7 +206,7 @@ white-space: pre-line; } - .tag { + .filter { display: inline-block; margin: 0; margin-right: 5px; diff --git a/src/scss/popup.scss b/src/scss/popup.scss index b28686d..9ac27ec 100644 --- a/src/scss/popup.scss +++ b/src/scss/popup.scss @@ -34,7 +34,7 @@ } } - .tag, + .filter, p.see-more { cursor: pointer; diff --git a/src/selectors/index.js b/src/selectors/index.js index d06a528..4a66352 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -14,8 +14,8 @@ export const getSites = state => state.domain.sites export const getSources = state => state.domain.sources export const getShapes = state => state.domain.shapes export const getNotifications = state => state.domain.notifications -export const getTagTree = state => state.domain.tags -export const getActiveTags = state => state.app.filters.tags +export const getFilterTree = state => state.domain.filters +export const getActiveFilters = state => state.app.filters.filters export const getActiveCategories = state => state.app.filters.categories export const getTimeRange = state => state.app.timeline.range export const getTimelineDimensions = state => state.app.timeline.dimensions @@ -42,20 +42,24 @@ export const selectShapes = createSelector([getShapes, getFeatures], (shapes, fe /** * Of all available events, selects those that * 1. fall in time range - * 2. exist in an active tag + * 2. exist in an active filter * 3. exist in an active category */ export const selectEvents = createSelector( - [getEvents, getActiveTags, getActiveCategories, getTimeRange, getFeatures], - (events, activeTags, activeCategories, timeRange, features) => { + [getEvents, getActiveFilters, getActiveCategories, getTimeRange, getFeatures], + (events, activeFilters, activeCategories, timeRange, features) => { return events.reduce((acc, event) => { - const isMatchingTag = (event.tags && event.tags.map(tag => activeTags.includes(tag)).some(s => s)) || activeTags.length === 0 - const isActiveTag = isMatchingTag || activeTags.length === 0 + const isMatchingFilter = (event.filters && + event.filters.map(filter => + activeFilters.includes(filter)) + .some(s => s) + ) || activeFilters.length === 0 + const isActiveFilter = isMatchingFilter || activeFilters.length === 0 const isActiveCategory = activeCategories.includes(event.category) || activeCategories.length === 0 let isActiveTime = isTimeRangedIn(event, timeRange) isActiveTime = features.GRAPH_NONLOCATED ? ((!event.latitude && !event.longitude) || isActiveTime) : isActiveTime - if (isActiveTime && isActiveTag && isActiveCategory) { + if (isActiveTime && isActiveFilter && isActiveCategory) { acc[event.id] = { ...event } } @@ -65,7 +69,7 @@ export const selectEvents = createSelector( /** * 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 + * and if filters are being used, select them if their filters are enabled */ export const selectNarratives = createSelector( [getEvents, getNarratives, getSources, getFeatures], @@ -161,12 +165,12 @@ export const selectEventsWithProjects = createSelector( } const projSize = 2 * sizes.eventDotR const projectIdx = features.GRAPH_NONLOCATED.projectIdx || 0 - const getProject = ev => ev.tags[projectIdx] + const getProject = ev => ev.filters[projectIdx] const projects = {} // get all projects events = events.reduce((acc, event) => { - const project = event.tags.length >= 1 && !event.latitude && !event.longitude ? getProject(event) : null + const project = event.filters.length >= 1 && !event.latitude && !event.longitude ? getProject(event) : null // add project if it doesn't exist if (project !== null) { diff --git a/src/store/initial.js b/src/store/initial.js index 30c822b..7a11c5a 100644 --- a/src/store/initial.js +++ b/src/store/initial.js @@ -16,7 +16,7 @@ const initial = { categories: [], sources: {}, sites: [], - tags: {}, + filters: {}, notifications: [] }, @@ -40,7 +40,7 @@ const initial = { current: null }, filters: { - tags: [], + filters: [], categories: [], views: { events: true, @@ -131,9 +131,8 @@ const initial = { }, features: { - CATEGORIES_AS_TAGS: true, USE_COVER: false, - USE_TAGS: false, + USE_FILTERS: false, USE_SEARCH: false, USE_SITES: false, USE_SOURCES: false,