From 470daf27e7f68e06f83508cd06501854109b3cbc Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Wed, 16 Jan 2019 09:57:51 -0500 Subject: [PATCH 1/9] Use tag arrays in events, no longer strings --- src/components/SourceOverlay.jsx | 7 +++-- src/components/TagListPanel.jsx | 2 +- src/components/Toolbar.jsx | 16 +++++------ src/reducers/schema/eventSchema.js | 2 +- src/scss/mediaoverlay.scss | 44 ++++++++++++++++++++++++++---- src/scss/timeline.scss | 5 ++++ src/selectors/index.js | 3 +- 7 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/components/SourceOverlay.jsx b/src/components/SourceOverlay.jsx index d18a865..ea94e2d 100644 --- a/src/components/SourceOverlay.jsx +++ b/src/components/SourceOverlay.jsx @@ -19,7 +19,7 @@ function SourceOverlay ({ source, onCancel }) { } + loader={
} unloader={} /> @@ -107,8 +107,8 @@ function SourceOverlay ({ source, onCancel }) { return (
{img ? img : ''} - {vid ? `, ${vid}`: ''} - {txt ? `, ${txt}`: ''} + {(img && vid) ? `, ${vid}`: (vid || '')} + {((img || vid) && txt) ? `, ${txt}`: (txt || '')}
) } @@ -145,6 +145,7 @@ function SourceOverlay ({ source, onCancel }) {
{title?

{title}

: null}
{_renderCounts(counts)}
+
{type ?

Media type

: null} {type ?

perm_media{type}

: null} {date ?

Date

: null} diff --git a/src/components/TagListPanel.jsx b/src/components/TagListPanel.jsx index fa6667b..1ca46e7 100644 --- a/src/components/TagListPanel.jsx +++ b/src/components/TagListPanel.jsx @@ -22,7 +22,7 @@ class TagListPanel extends React.Component { onClickCheckbox(tag) { tag.active = !tag.active - this.props.filter(tag); + this.props.onFilter(tag); } createNodeComponent (node, depth) { diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index ea526f9..53b0808 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -81,7 +81,7 @@ class Toolbar extends React.Component { categories={this.props.categories} tagFilters={this.props.tagFilters} categoryFilters={this.props.categoryFilters} - filter={this.props.filter} + onFilter={this.props.methods.onFilter} language={this.props.language} /> @@ -90,13 +90,13 @@ class Toolbar extends React.Component { return ''; } - renderToolbarTab(_selected, label) { + renderToolbarTab(_selected, label, icon_key) { const isActive = (this.state._selected === _selected); let classes = (isActive) ? 'toolbar-tab active' : 'toolbar-tab'; return (
{ this.selectTab(_selected); }}> - timeline + {icon_key}
{label}
); @@ -104,7 +104,7 @@ class Toolbar extends React.Component { renderToolbarTabs() { const title = copy[this.props.language].toolbar.title; - const isTags = this.props.tags && (this.props.tags.children > 0); + const isTags = this.props.tags && (Object.keys(this.props.tags.children) > 0); return (
@@ -125,7 +125,7 @@ class Toolbar extends React.Component { {this.renderClosePanel()} {this.renderToolbarNarrativePanel()} - {/* {this.renderToolbarTagPanel()} */} + {this.renderToolbarTagPanel()}}
) @@ -150,15 +150,15 @@ class Toolbar extends React.Component { renderToolbarTabs() { const title = copy[this.props.language].toolbar.title; - const isTags = this.props.tags && (this.props.tags.children > 0); + const isTags = this.props.tags && this.props.tags.children; return (

{title}

{/*this.renderToolbarTab(0, 'search')*/} - {this.renderToolbarTab(0, 'Narratives')} - {(isTags) ? this.renderToolbarTab(1, 'Explore by tag') : ''} + {this.renderToolbarTab(0, 'Narratives', 'timeline')} + {(isTags) ? this.renderToolbarTab(1, 'Explore by tag', 'style') : ''}
state.app.filters.timerange */ function isTaggedIn(event, tagFilters) { if (event.tags) { - const tagsInEvent = event.tags.split(",") - const isTagged = tagsInEvent.some((tag) => { + const isTagged = event.tags.some((tag) => { return tagFilters.find(tF => (tF.key === tag && tF.active)) }) return isTagged From 4251ed0e94ceffb76bbc0b8b0c6f3b6d1d6a8619 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Wed, 16 Jan 2019 11:16:05 -0500 Subject: [PATCH 2/9] Allow filtering by category, intersecting with tags --- src/actions/index.js | 10 ++++++- src/components/Dashboard.jsx | 3 ++- src/components/TagListPanel.jsx | 45 ++++++++++++++++++++++++++------ src/components/Toolbar.jsx | 7 ++--- src/js/data/copy.json | 10 ++++++- src/reducers/app.js | 24 +++++++++++++++-- src/selectors/index.js | 46 +++++++++++++++++++++++++++------ 7 files changed, 121 insertions(+), 24 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index da6fe64..c8bdf0e 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -189,7 +189,7 @@ export function updateDistrict(district) { } } -export const UPDATE_TAGFILTERS = 'UPDATE_TIMEFILTERS' +export const UPDATE_TAGFILTERS = 'UPDATE_TAGFILTERS' export function updateTagFilters(tag) { return { type: UPDATE_TAGFILTERS, @@ -197,6 +197,14 @@ export function updateTagFilters(tag) { } } +export const UPDATE_CATEGORYFILTERS = 'UPDATE_CATEGORYFILTERS' +export function updateCategoryFilters(category) { + return { + type: UPDATE_CATEGORYFILTERS, + category + } +} + export const UPDATE_TIMERANGE = 'UPDATE_TIMERANGE'; export function updateTimeRange(timerange) { return { diff --git a/src/components/Dashboard.jsx b/src/components/Dashboard.jsx index 077e0f2..e0a59aa 100644 --- a/src/components/Dashboard.jsx +++ b/src/components/Dashboard.jsx @@ -98,7 +98,8 @@ class Dashboard extends React.Component { diff --git a/src/components/TagListPanel.jsx b/src/components/TagListPanel.jsx index 1ca46e7..3406140 100644 --- a/src/components/TagListPanel.jsx +++ b/src/components/TagListPanel.jsx @@ -1,5 +1,6 @@ import React from 'react'; import Checkbox from './presentational/Checkbox'; +import copy from '../js/data/copy.json'; class TagListPanel extends React.Component { @@ -20,9 +21,10 @@ class TagListPanel extends React.Component { this.computeTree(nextProps.tags);//.children[nextProps.tagType]); } - onClickCheckbox(tag) { - tag.active = !tag.active - this.props.onFilter(tag); + onClickCheckbox(obj, type) { + obj.active = !obj.active + if (type === 'category') this.props.onCategoryFilter(obj); + if (type === 'tag') this.props.onTagFilter(obj); } createNodeComponent (node, depth) { @@ -35,7 +37,7 @@ class TagListPanel extends React.Component { this.onClickCheckbox(node)} + onClickCheckbox={() => this.onClickCheckbox(node, 'tag')} /> ); @@ -61,15 +63,42 @@ class TagListPanel extends React.Component { } renderTree() { - return this.state.treeComponents.map(c => c); + return ( +
+

{copy[this.props.language].toolbar.tags}

+ {this.state.treeComponents.map(c => c)} +
+ ) + } + + renderCategoryTree() { + return ( +
+

{copy[this.props.language].toolbar.categories}

+ {this.props.categories.map(cat => { + return (
  • + this.onClickCheckbox(cat, 'category')} + /> +
  • ) + }) + } +
    + ) } render() { - return (
    -

    Explore data by tag

    -

    Explore freely all the data by selecting tags.

    +

    {copy[this.props.language].toolbar.explore_by_tag__title}

    +

    {copy[this.props.language].toolbar.explore_by_tag__description}

    + {this.renderCategoryTree()} {this.renderTree()}
    ); diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index 53b0808..4826445 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -81,7 +81,8 @@ class Toolbar extends React.Component { categories={this.props.categories} tagFilters={this.props.tagFilters} categoryFilters={this.props.categoryFilters} - onFilter={this.props.methods.onFilter} + onTagFilter={this.props.methods.onTagFilter} + onCategoryFilter={this.props.methods.onCategoryFilter} language={this.props.language} /> @@ -185,11 +186,11 @@ class Toolbar extends React.Component { function mapStateToProps(state) { return { tags: selectors.getTagTree(state), - categories: selectors.selectCategories(state), + categories: selectors.getCategories(state), narratives: selectors.selectNarratives(state), language: state.app.language, tagFilters: selectors.selectTagList(state), - categoryFilter: state.app.filters.categories, + categoryFilters: selectors.selectCategories(state), viewFilters: state.app.filters.views, features: state.app.features, narrative: state.app.narrative, diff --git a/src/js/data/copy.json b/src/js/data/copy.json index 039a50d..6453a2a 100644 --- a/src/js/data/copy.json +++ b/src/js/data/copy.json @@ -18,6 +18,10 @@ }, "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.", "panels": { "mentions": { "title": "Personas", @@ -105,7 +109,11 @@ } }, "narrative_panel_title": "Focus narratives", - "narrative_summary": "Here you can follow some curated stories we have found in the data." + "narrative_summary": "Here you can follow some curated stories we have found in the data.", + "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." }, "timeline": { "zooms": [ diff --git a/src/reducers/app.js b/src/reducers/app.js index a17ec9a..b7164ea 100644 --- a/src/reducers/app.js +++ b/src/reducers/app.js @@ -1,11 +1,10 @@ import initial from '../store/initial.js' -import { parseDate } from '../js/utilities.js' - import { UPDATE_HIGHLIGHTED, UPDATE_SELECTED, UPDATE_TAGFILTERS, + UPDATE_CATEGORYFILTERS, UPDATE_TIMERANGE, UPDATE_NARRATIVE, INCREMENT_NARRATIVE_CURRENT, @@ -136,6 +135,25 @@ function updateTagFilters(appState, action) { }) } +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 Object.assign({}, appState, { filters: Object.assign({}, appState.filters, { @@ -254,6 +272,8 @@ function app(appState = initial.app, action) { return updateSelected(appState, action) case UPDATE_TAGFILTERS: return updateTagFilters(appState, action) + case UPDATE_CATEGORYFILTERS: + return updateCategoryFilters(appState, action) case UPDATE_TIMERANGE: return updateTimeRange(appState, action) case UPDATE_NARRATIVE: diff --git a/src/selectors/index.js b/src/selectors/index.js index aec7e5c..2367b35 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -20,6 +20,7 @@ export const getSources = 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 getTimeRange = state => state.app.filters.timerange @@ -42,6 +43,19 @@ function isTaggedIn(event, tagFilters) { } } +/** + * 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 */ @@ -53,6 +67,17 @@ function isNoTags(tagFilters) { ) } +/* +* 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 @@ -69,14 +94,15 @@ function isTimeRangedIn(event, timeRange) { * and if TAGS are being used, select them if their tags are enabled */ export const selectEvents = createSelector( - [getEvents, getTagsFilter, getTimeRange], - (events, tagFilters, timeRange) => { + [getEvents, getTagsFilter, getCategoriesFilter, getTimeRange], + (events, tagFilters, categories, 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) - if (isTimeRanged && isTagged) { + if (isTimeRanged && isTagged && isTaggedWithCategory) { const eventClone = Object.assign({}, event) acc[event.id] = eventClone } @@ -90,16 +116,14 @@ export const selectEvents = createSelector( * and if TAGS are being used, select them if their tags are enabled */ export const selectNarratives = createSelector( - [getEvents, getNarratives, getTagsFilter, getTimeRange, getSources], - (events, narrativesMeta, tagFilters, timeRange, sources) => { + [getEvents, getNarratives, getSources], + (events, narrativesMeta, sources) => { 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 evt.narratives.forEach(narrative => { @@ -229,7 +253,13 @@ export const selectSelected = createSelector( */ export const selectCategories = createSelector( [getCategories], - (categories) => categories + (categories) => { + categories.map(cat => { + cat.active = (!cat.hasOwnProperty('active')) ? false : cat.active + }); + console.log(categories) + return categories; + } ) From 08d44c79c7db7de83b33fdc9d2fc785fb1b69458 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Wed, 16 Jan 2019 11:32:02 -0500 Subject: [PATCH 3/9] Add categories-as-tag flag to config example --- example.config.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/example.config.js b/example.config.js index 9a83349..ee5b1c6 100644 --- a/example.config.js +++ b/example.config.js @@ -14,7 +14,8 @@ module.exports = { USE_TAGS: false, USE_SEARCH: false, USE_SITES: true, - USE_SOURCES: true + USE_SOURCES: true, + CATEGORIES_AS_TAGS: true } } From 79af252b33f943e9c404e1f5339c99de549486cd Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 17 Jan 2019 09:40:04 +0000 Subject: [PATCH 4/9] add more descriptive error handling for optional exts --- src/actions/index.js | 48 +++++++++++++++++++++++++----------------- src/selectors/index.js | 1 - 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index c8bdf0e..9642de9 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -10,7 +10,7 @@ function urlFromEnv(ext) { // TODO: relegate these URLs entirely to environment variables const EVENT_DATA_URL = urlFromEnv('EVENT_EXT'); const CATEGORY_URL = urlFromEnv('CATEGORY_EXT'); -const TAG_URL = urlFromEnv('TAGS_EXT'); +const TAGS_URL = urlFromEnv('TAGS_EXT'); const SOURCES_URL = urlFromEnv('SOURCES_EXT'); const NARRATIVE_URL = urlFromEnv('NARRATIVE_EXT'); const SITES_URL = urlFromEnv('SITES_EXT'); @@ -26,18 +26,6 @@ function _debugger(value) { } } -/* -* Create an error notification object -* Types: ['error', 'warning', 'good', 'neural'] -*/ -function makeError (type, id, message) { - return { - type: 'error', - id, - message: `${type} ${id}: ${message}` - } -} - export function fetchDomain () { let notifications = [] @@ -76,16 +64,38 @@ export function fetchDomain () { let tagsPromise = Promise.resolve([]) if (process.env.features.USE_TAGS) { - tagsPromise = fetch(TAG_URL) - .then(response => response.json()) - .catch(handleError('tags')) + if (!TAGS_URL) { + notifications.push({ + message: 'USE_TAGS is true, but you have not provided a TAGS_URL', + type: 'error' + }) + tagsPromise = Promise.resolve({}) + } else { + tagsPromise = fetch(TAGS_URL) + .then(response => { + console.log(response) + return response.json() + }) + .catch(err => { + console.log(err) + return handleError('tags') + }) + } } let sourcesPromise = Promise.resolve([]) if (process.env.features.USE_SOURCES) { - sourcesPromise = fetch(SOURCES_URL) - .then(response => response.json()) - .catch(handleError('sources')) + if (!SOURCES_URL) { + notifications.push({ + message: 'USE_SOURCES is true, but you have not provided a SOURCES_URL', + type: 'error' + }) + tagsPromise = Prommise.resolve([]) + } else { + sourcesPromise = fetch(SOURCES_URL) + .then(response => response.json()) + .catch(handleError('sources')) + } } return Promise.all([ diff --git a/src/selectors/index.js b/src/selectors/index.js index 2367b35..e6562ad 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -257,7 +257,6 @@ export const selectCategories = createSelector( categories.map(cat => { cat.active = (!cat.hasOwnProperty('active')) ? false : cat.active }); - console.log(categories) return categories; } ) From a6e04b23eea28bab3fd4a9fee17f2e61be2a92cc Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 17 Jan 2019 09:50:07 +0000 Subject: [PATCH 5/9] more generic error handling actions --- src/actions/index.js | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index 9642de9..ab96975 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -26,17 +26,18 @@ function _debugger(value) { } } +const domainMsg = (domainType) => `Something went wrong fetching ${domainType}. Check the URL or try disabling them in the config file.` + + export function fetchDomain () { let notifications = [] - function handleError (domainType) { - return () => { - notifications.push({ - message: `Something went wrong fetching ${domainType}. Check the URL or try disabling them in the config file.`, - type: 'error' - }) - return [] - } + function handleError (message) { + notifications.push({ + message, + type: 'error' + }) + return [] } return dispatch => { @@ -45,56 +46,45 @@ export function fetchDomain () { const eventPromise = fetch(EVENT_DATA_URL) .then(response => response.json()) - .catch(handleError('events')) + .catch(() => handleError('events')) const catPromise = fetch(CATEGORY_URL) .then(response => response.json()) - .catch(handleError('categories')) + .catch(() => handleError(domainMsg('categories'))) const narPromise = fetch(NARRATIVE_URL) .then(response => response.json()) - .catch(handleError('narratives')) + .catch(() => handleError(domainMsg('narratives'))) let sitesPromise = Promise.resolve([]) if (process.env.features.USE_SITES) { sitesPromise = fetch(SITES_URL) .then(response => response.json()) - .catch(handleError('sites')) + .catch(() => handleError(domainMsg('sites'))) } let tagsPromise = Promise.resolve([]) if (process.env.features.USE_TAGS) { if (!TAGS_URL) { - notifications.push({ - message: 'USE_TAGS is true, but you have not provided a TAGS_URL', - type: 'error' - }) - tagsPromise = Promise.resolve({}) + tagsPromise = Promise.resolve(handleError('USE_TAGS is true, but you have not provided a TAGS_URL')) } else { tagsPromise = fetch(TAGS_URL) .then(response => { console.log(response) return response.json() }) - .catch(err => { - console.log(err) - return handleError('tags') - }) + .catch(() => handleError(domainMsg('tags'))) } } let sourcesPromise = Promise.resolve([]) if (process.env.features.USE_SOURCES) { if (!SOURCES_URL) { - notifications.push({ - message: 'USE_SOURCES is true, but you have not provided a SOURCES_URL', - type: 'error' - }) - tagsPromise = Prommise.resolve([]) + sourcesPromise = Promise.resolve(makeError('USE_SOURCES is true, but you have not provided a SOURCES_URL')) } else { sourcesPromise = fetch(SOURCES_URL) .then(response => response.json()) - .catch(handleError('sources')) + .catch(() => handleError(domainMsg('sources'))) } } From 68d43ad9a29e78f19baa898adc78c8687a5601d3 Mon Sep 17 00:00:00 2001 From: Lachlan Kermode Date: Thu, 17 Jan 2019 10:00:55 +0000 Subject: [PATCH 6/9] :lipstick: --- src/actions/index.js | 32 ++++---------------------------- src/js/utilities.js | 8 ++++++++ 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/src/actions/index.js b/src/actions/index.js index ab96975..cad3fa1 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -1,13 +1,5 @@ -// TODO: move to util lib -function urlFromEnv(ext) { - if (process.env[ext]) { - return `${process.env.SERVER_ROOT}${process.env[ext]}` - } else { - return null - } -} +import { urlFromEnv } from '../js/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'); @@ -16,16 +8,6 @@ const NARRATIVE_URL = urlFromEnv('NARRATIVE_EXT'); const SITES_URL = urlFromEnv('SITES_EXT'); const eventUrlMap = (event) => `${process.env.SERVER_ROOT}${process.env.EVENT_DESC_ROOT}/${(event.id) ? event.id : event}`; - -const DEBUG_GER = 'DEBUG_GER' -function _debugger(value) { - console.log(value) - return { - type: DEBUG_GER, - value - } -} - const domainMsg = (domainType) => `Something went wrong fetching ${domainType}. Check the URL or try disabling them in the config file.` @@ -66,13 +48,10 @@ export function fetchDomain () { let tagsPromise = Promise.resolve([]) if (process.env.features.USE_TAGS) { if (!TAGS_URL) { - tagsPromise = Promise.resolve(handleError('USE_TAGS is true, but you have not provided a TAGS_URL')) + tagsPromise = Promise.resolve(handleError('USE_TAGS is true, but you have not provided a TAGS_EXT')) } else { tagsPromise = fetch(TAGS_URL) - .then(response => { - console.log(response) - return response.json() - }) + .then(response => response.json()) .catch(() => handleError(domainMsg('tags'))) } } @@ -80,7 +59,7 @@ export function fetchDomain () { let sourcesPromise = Promise.resolve([]) if (process.env.features.USE_SOURCES) { if (!SOURCES_URL) { - sourcesPromise = Promise.resolve(makeError('USE_SOURCES is true, but you have not provided a SOURCES_URL')) + sourcesPromise = Promise.resolve(makeError('USE_SOURCES is true, but you have not provided a SOURCES_EXT')) } else { sourcesPromise = fetch(SOURCES_URL) .then(response => response.json()) @@ -152,9 +131,6 @@ export function fetchSource(source) { return response.json() } }) - .then(sources => { - dispatch(_debugger(sources)) - }) .catch(err => { dispatch(fetchSourceError(err.message)) dispatch(toggleFetchingSources()) diff --git a/src/js/utilities.js b/src/js/utilities.js index 924e1f5..5568b17 100644 --- a/src/js/utilities.js +++ b/src/js/utilities.js @@ -116,3 +116,11 @@ export function injectSource(id) { } }) } + +export function urlFromEnv(ext) { + if (process.env[ext]) { + return `${process.env.SERVER_ROOT}${process.env[ext]}` + } else { + return null + } +} From 20ef17e77582ccdb01a809ebbf425af175cf9b36 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 17 Jan 2019 11:13:10 -0500 Subject: [PATCH 7/9] Move some strings to copy --- src/components/Toolbar.jsx | 22 ++++------------------ src/js/data/copy.json | 4 +++- 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/components/Toolbar.jsx b/src/components/Toolbar.jsx index 4826445..1596f29 100644 --- a/src/components/Toolbar.jsx +++ b/src/components/Toolbar.jsx @@ -103,22 +103,6 @@ class Toolbar extends React.Component { ); } - renderToolbarTabs() { - const title = copy[this.props.language].toolbar.title; - const isTags = this.props.tags && (Object.keys(this.props.tags.children) > 0); - - return ( -
    -

    {title}

    -
    - {/*this.renderToolbarTab(0, 'search')*/} - {this.renderToolbarTab(0, 'Focus stories')} - {this.renderToolbarTab(1, 'Explore freely')} -
    -
    - ) - } - renderToolbarPanels() { let classes = (this.state._selected >= 0) ? 'toolbar-panels' : 'toolbar-panels folded'; return ( @@ -151,6 +135,8 @@ class Toolbar extends React.Component { renderToolbarTabs() { const title = copy[this.props.language].toolbar.title; + const narratives_label = copy[this.props.language].toolbar.narratives_label; + const tags_label = copy[this.props.language].toolbar.tags_label; const isTags = this.props.tags && this.props.tags.children; return ( @@ -158,8 +144,8 @@ class Toolbar extends React.Component {

    {title}

    {/*this.renderToolbarTab(0, 'search')*/} - {this.renderToolbarTab(0, 'Narratives', 'timeline')} - {(isTags) ? this.renderToolbarTab(1, 'Explore by tag', 'style') : ''} + {this.renderToolbarTab(0, narratives_label, 'timeline')} + {(isTags) ? this.renderToolbarTab(1, tags_label, 'style') : ''}
    Date: Thu, 17 Jan 2019 11:18:22 -0500 Subject: [PATCH 8/9] Return narratives in original order --- src/selectors/index.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/selectors/index.js b/src/selectors/index.js index e6562ad..53cae3f 100644 --- a/src/selectors/index.js +++ b/src/selectors/index.js @@ -145,11 +145,6 @@ export const selectNarratives = createSelector( 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 - // }) - if (narrativesMeta.find(n => n.id === key)) { narratives[key] = { ...narrativesMeta.find(n => n.id === key), @@ -158,7 +153,9 @@ export const selectNarratives = createSelector( } }) - return Object.values(narratives) + // Return narratives in original order + // + filter those that are undefined + return narrativesMeta.map(n => narratives[n.id]).filter(d => d); }) /** Aggregate information about the narrative and the current step into From ec3ea5a7c7a4cbbb88a209a61d3406c539c8fd19 Mon Sep 17 00:00:00 2001 From: Franc Camps-Febrer Date: Thu, 17 Jan 2019 11:24:31 -0500 Subject: [PATCH 9/9] Adjust y ticks height --- src/components/Timeline.jsx | 3 ++- src/components/TimelineCategories.jsx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/Timeline.jsx b/src/components/Timeline.jsx index a05c5e3..5c42e17 100644 --- a/src/components/Timeline.jsx +++ b/src/components/Timeline.jsx @@ -82,7 +82,8 @@ class Timeline extends React.Component { } makeScaleY(categories) { - const catsYpos = categories.map((g, i) => (i + 1) * this.state.dims.trackHeight / categories.length); + const tickHeight = 15; + const catsYpos = categories.map((g, i) => (i + 1) * this.state.dims.trackHeight / categories.length + tickHeight / 2); return d3.scaleOrdinal() .domain(categories) .range(catsYpos); diff --git a/src/components/TimelineCategories.jsx b/src/components/TimelineCategories.jsx index d1cd15f..c90697c 100644 --- a/src/components/TimelineCategories.jsx +++ b/src/components/TimelineCategories.jsx @@ -25,7 +25,7 @@ class TimelineCategories extends React.Component { } getY(idx) { - return (idx + 1) * this.props.dims.trackHeight / this.props.categories.length + return (idx + 1) * this.props.dims.trackHeight / this.props.categories.length + 7.5; } renderCategory(category, idx) {