From c72ac77acaa4358bfbacfacb002f23ef91782bd9 Mon Sep 17 00:00:00 2001 From: Ed Brett Date: Wed, 12 Dec 2018 19:06:11 +0100 Subject: [PATCH] move widget selectors to parent level --- app/javascript/components/map/selectors.js | 13 +--- .../components/show-analysis/component.jsx | 6 +- .../components/show-analysis/selectors.js | 7 +- .../maps/components/analysis/selectors.js | 6 +- .../components/maps/map/selectors.js | 59 +++++++++++--- .../components/widgets/selectors.js | 76 +------------------ app/javascript/pages/dashboards/component.js | 10 ++- app/javascript/pages/dashboards/selectors.js | 24 ++++++ 8 files changed, 98 insertions(+), 103 deletions(-) diff --git a/app/javascript/components/map/selectors.js b/app/javascript/components/map/selectors.js index bb15903b16..cd42f89344 100644 --- a/app/javascript/components/map/selectors.js +++ b/app/javascript/components/map/selectors.js @@ -1,10 +1,7 @@ import { createSelector, createStructuredSelector } from 'reselect'; import isEmpty from 'lodash/isEmpty'; -import { - filterWidgetsByCategoryAndLayers, - getActiveWidget -} from 'components/widgets/selectors'; +import { getActiveWidget, getWidgets } from 'pages/dashboards/selectors'; // get list data const selectLoading = state => state.mapOld.loading || state.geostore.loading; @@ -19,13 +16,9 @@ const selectBounds = state => (state.geostore.data && state.geostore.data.bounds) || null; export const getMapSettings = createSelector( - [ - selectSettings, - filterWidgetsByCategoryAndLayers, - getActiveWidget, - selectQuery - ], + [selectSettings, getWidgets, getActiveWidget, selectQuery], (settings, widgets, widget, query) => { + if (!widgets) return settings; const widgetUrlState = query && query[widget]; const activeWidget = widgets.find(w => w.widget === widget); const widgetSettings = activeWidget && activeWidget.settings; diff --git a/app/javascript/components/maps/components/analysis/components/show-analysis/component.jsx b/app/javascript/components/maps/components/analysis/components/show-analysis/component.jsx index a813178ad6..22931fe34e 100644 --- a/app/javascript/components/maps/components/analysis/components/show-analysis/component.jsx +++ b/app/javascript/components/maps/components/analysis/components/show-analysis/component.jsx @@ -80,7 +80,8 @@ class ShowAnalysis extends PureComponent { downloadUrls, hasLayers, hasWidgets, - zoomLevel + zoomLevel, + widgets } = this.props; return ( @@ -167,7 +168,7 @@ class ShowAnalysis extends PureComponent { - +
{zoomLevel < 11 && (

@@ -219,6 +220,7 @@ ShowAnalysis.propTypes = { hasLayers: PropTypes.bool, hasWidgets: PropTypes.bool, downloadUrls: PropTypes.array, + widgets: PropTypes.array, zoomLevel: PropTypes.number }; diff --git a/app/javascript/components/maps/components/analysis/components/show-analysis/selectors.js b/app/javascript/components/maps/components/analysis/components/show-analysis/selectors.js index f0143bd63d..aa34de282b 100644 --- a/app/javascript/components/maps/components/analysis/components/show-analysis/selectors.js +++ b/app/javascript/components/maps/components/analysis/components/show-analysis/selectors.js @@ -2,7 +2,11 @@ import { createSelector, createStructuredSelector } from 'reselect'; import { buildLocationName, buildFullLocationName } from 'utils/format'; -import { getActiveLayers, getMapZoom } from 'components/maps/map/selectors'; +import { + getActiveLayers, + getMapZoom, + getWidgetsWithLayerParams +} from 'components/maps/map/selectors'; import { getWidgetLayers } from 'components/maps/components/analysis/selectors'; const selectLocation = state => state.location && state.location.payload; @@ -148,5 +152,6 @@ export const getShowAnalysisProps = createStructuredSelector({ layers: getActiveLayers, downloadUrls: getDownloadLinks, error: selectError, + widgets: getWidgetsWithLayerParams, zoomLevel: getMapZoom }); diff --git a/app/javascript/components/maps/components/analysis/selectors.js b/app/javascript/components/maps/components/analysis/selectors.js index 9be7dc2dbe..6f31d16f42 100644 --- a/app/javascript/components/maps/components/analysis/selectors.js +++ b/app/javascript/components/maps/components/analysis/selectors.js @@ -6,9 +6,9 @@ import flatMap from 'lodash/flatMap'; import { getAllBoundaries, getActiveBoundaryDatasets, - getAllLayers + getAllLayers, + getWidgetsWithLayerParams } from 'components/maps/map/selectors'; -import { filterWidgetsByCategoryAndLayers } from 'components/widgets/selectors'; import layersIcon from 'assets/icons/layers.svg'; import analysisIcon from 'assets/icons/analysis.svg'; @@ -54,7 +54,7 @@ export const getShowDraw = createSelector( ); export const getWidgetLayers = createSelector( - filterWidgetsByCategoryAndLayers, + getWidgetsWithLayerParams, widgets => { const activeWidgets = widgets && diff --git a/app/javascript/components/maps/map/selectors.js b/app/javascript/components/maps/map/selectors.js index d820be9b78..6ceefb3a22 100644 --- a/app/javascript/components/maps/map/selectors.js +++ b/app/javascript/components/maps/map/selectors.js @@ -1,7 +1,10 @@ import { createSelector, createStructuredSelector } from 'reselect'; import flatten from 'lodash/flatten'; import isEmpty from 'lodash/isEmpty'; +import moment from 'moment'; +import intersection from 'lodash/intersection'; +import { parseWidgetsWithOptions } from 'components/widgets/selectors'; import { initialState } from './reducers'; import basemaps, { labels } from './basemaps-schema'; @@ -24,9 +27,6 @@ const selectGeostore = state => state.geostore.data || null; const selectSelectedInteractionId = state => state.popup.selected; const selectInteractions = state => state.popup.interactions; -// get widgets -const selectWidgetActiveSettings = state => state.widgets.settings; - // CONSTS export const getBasemaps = () => basemaps; export const getLabels = () => labels; @@ -319,18 +319,57 @@ export const getActiveLayers = createSelector(getAllLayers, layers => { return layers.filter(l => !l.confirmedOnly); }); +// get widgets related to map layers and use them to build the layers +export const getWidgetsWithLayerParams = createSelector( + [parseWidgetsWithOptions, getAllLayers], + (widgets, layers) => { + if (!widgets || !widgets.length || !layers || !layers.length) return null; + const layerIds = layers && layers.map(l => l.id); + const filteredWidgets = widgets.filter(w => { + const layerIntersection = intersection(w.config.layers, layerIds); + return w.config.analysis && layerIntersection && layerIntersection.length; + }); + return filteredWidgets.map(w => { + const widgetLayer = + layers && layers.find(l => w.config && w.config.layers.includes(l.id)); + const { params, decodeParams } = widgetLayer || {}; + const startDate = + (params && params.startDate) || + (decodeParams && decodeParams.startDate); + const startYear = + startDate && parseInt(moment(startDate).format('YYYY'), 10); + const endDate = + (params && params.endDate) || (decodeParams && decodeParams.endDate); + const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10); + + return { + ...w, + settings: { + ...w.settings, + ...params, + ...decodeParams, + ...(startYear && { + startYear + }), + ...(endYear && { + endYear + }) + } + }; + }); + } +); + // flatten datasets into layers for the layer manager export const getActiveLayersWithWidgetSettings = createSelector( - [getAllLayers, selectWidgetActiveSettings], - (layers, widgetSettings) => { + [getAllLayers, getWidgetsWithLayerParams], + (layers, widgets) => { if (isEmpty(layers)) return []; - if (isEmpty(widgetSettings)) return layers; + if (isEmpty(widgets)) return layers; return layers.map(l => { const layerWidgetState = - widgetSettings && - Object.values(widgetSettings).find( - w => w.layers && w.layers.includes(l.id) - ); + widgets && + Object.values(widgets).find(w => w.layers && w.layers.includes(l.id)); const { updateLayer } = layerWidgetState || {}; return { ...l, diff --git a/app/javascript/components/widgets/selectors.js b/app/javascript/components/widgets/selectors.js index 791e1202a8..b68807d691 100644 --- a/app/javascript/components/widgets/selectors.js +++ b/app/javascript/components/widgets/selectors.js @@ -3,11 +3,6 @@ import sortBy from 'lodash/sortBy'; import uniq from 'lodash/uniq'; import concat from 'lodash/concat'; import lowerCase from 'lodash/lowerCase'; -import camelCase from 'lodash/camelCase'; -import intersection from 'lodash/intersection'; -import moment from 'moment'; - -import { getAllLayers } from 'components/maps/map/selectors'; import tropicalIsos from 'data/tropical-isos.json'; import colors from 'data/colors.json'; @@ -19,7 +14,6 @@ export const selectAnalysis = (state, { analysis }) => analysis; export const selectAllLocation = state => state.location; export const selectLocationType = state => state.location && state.location.payload && state.location.payload.type; -export const selectQuery = state => state.location && state.location.query; export const selectWidgetFromQuery = state => state.location && state.location.query && state.location.query.widget; export const selectEmbed = (state, { embed }) => embed; @@ -139,11 +133,6 @@ export const isTropicalLocation = createSelector([selectLocation], location => tropicalIsos.includes(location && location.adm0) ); -export const getCategory = createSelector( - [selectQuery], - query => (query && query.category) || 'summary' -); - export const getNoWidgetsMessage = createSelector( [getLocationName, selecteNoWidgetsMessage], (locationName, message) => @@ -172,7 +161,7 @@ export const parseWidgets = createSelector( export const filterWidgetsByLocation = createSelector( [parseWidgets, selectLocationType, getAdminLevel], (widgets, type, adminLevel) => { - if (!widgets) return null; + if (!widgets || !type) return null; return widgets.filter(w => { const { types, admins } = w.config || {}; return ( @@ -291,72 +280,10 @@ export const parseWidgetsWithOptions = createSelector( } ); -export const filterWidgetsByCategoryAndLayers = createSelector( - [parseWidgetsWithOptions, getCategory, getAllLayers, selectAnalysis], - (widgets, category, layers, analysis) => { - if (!widgets) return null; - let filteredWidgets = widgets; - if (analysis) { - const layerIds = layers && layers.map(l => l.id); - filteredWidgets = widgets.filter(w => { - const layerIntersection = intersection(w.config.layers, layerIds); - return ( - w.config.analysis && layerIntersection && layerIntersection.length - ); - }); - filteredWidgets = filteredWidgets.map(w => { - const widgetLayer = - layers && - layers.find(l => w.config && w.config.layers.includes(l.id)); - const { params, decodeParams } = widgetLayer || {}; - const startDate = - (params && params.startDate) || - (decodeParams && decodeParams.startDate); - const startYear = - startDate && parseInt(moment(startDate).format('YYYY'), 10); - const endDate = - (params && params.endDate) || (decodeParams && decodeParams.endDate); - const endYear = endDate && parseInt(moment(endDate).format('YYYY'), 10); - - return { - ...w, - settings: { - ...w.settings, - ...params, - ...decodeParams, - ...(startYear && { - startYear - }), - ...(endYear && { - endYear - }) - } - }; - }); - } else { - filteredWidgets = widgets.filter(w => - w.config.categories.includes(category) - ); - } - - return sortBy(filteredWidgets, `config.sortOrder[${camelCase(category)}]`); - } -); - -export const getActiveWidget = createSelector( - [filterWidgetsByCategoryAndLayers, selectQuery], - (widgets, query) => { - if (query && query.widget) return query.widget; - return widgets && widgets.length && widgets[0].widget; - } -); - export const getWidgetsProps = createStructuredSelector({ loading: selectLoading, whitelists: selectWhitelists, whitelist: getActiveWhitelist, - activeWidget: getActiveWidget, - category: getCategory, allLocation: selectAllLocation, location: selectLocation, locationType: selectLocationType, @@ -364,7 +291,6 @@ export const getWidgetsProps = createStructuredSelector({ locationObject: getLocationObject, locationName: getLocationName, childLocationData: getChildLocationData, - widgets: filterWidgetsByCategoryAndLayers, noWidgetsMessage: getNoWidgetsMessage, isTropical: isTropicalLocation }); diff --git a/app/javascript/pages/dashboards/component.js b/app/javascript/pages/dashboards/component.js index 91fd211b0a..14e58a0f24 100644 --- a/app/javascript/pages/dashboards/component.js +++ b/app/javascript/pages/dashboards/component.js @@ -32,7 +32,9 @@ class Page extends PureComponent { widgetAnchor, setMapZoom, handleCategoryChange, - noWidgetsMessage + noWidgetsMessage, + widgets, + activeWidget } = this.props; return ( @@ -60,6 +62,8 @@ class Page extends PureComponent {

@@ -100,7 +104,9 @@ Page.propTypes = { widgetAnchor: PropTypes.oneOfType([PropTypes.string, PropTypes.object]), setMapZoom: PropTypes.func, noWidgetsMessage: PropTypes.string, - handleCategoryChange: PropTypes.func + handleCategoryChange: PropTypes.func, + widgets: PropTypes.array, + activeWidget: PropTypes.string }; export default Page; diff --git a/app/javascript/pages/dashboards/selectors.js b/app/javascript/pages/dashboards/selectors.js index 5428c3736c..fbcf3de123 100644 --- a/app/javascript/pages/dashboards/selectors.js +++ b/app/javascript/pages/dashboards/selectors.js @@ -1,6 +1,10 @@ import { createSelector, createStructuredSelector } from 'reselect'; import replace from 'lodash/replace'; import upperFirst from 'lodash/upperFirst'; +import camelCase from 'lodash/camelCase'; +import sortBy from 'lodash/sortBy'; + +import { parseWidgetsWithOptions } from 'components/widgets/selectors'; import CATEGORIES from 'data/categories.json'; @@ -9,6 +13,7 @@ const selectShowMap = state => !!state.map.showMapMobile; const selectCategory = state => (state.location && state.location.query && state.location.query.category) || 'summary'; +export const selectQuery = state => state.location && state.location.query; export const getLinks = createSelector([selectCategory], activeCategory => CATEGORIES.map(category => ({ @@ -29,10 +34,29 @@ export const getNoWidgetsMessage = createSelector( category => `${upperFirst(category)} data for {location} coming soon` ); +export const getWidgets = createSelector( + [parseWidgetsWithOptions, selectCategory], + (widgets, category) => + sortBy( + widgets.filter(w => w.config.categories.includes(category)), + `config.sortOrder[${camelCase(category)}]` + ) +); + +export const getActiveWidget = createSelector( + [getWidgets, selectQuery], + (widgets, query) => { + if (query && query.widget) return query.widget; + return widgets && widgets.length && widgets[0].widget; + } +); + export const getDashboardsProps = createStructuredSelector({ showMapMobile: selectShowMap, category: selectCategory, links: getLinks, + widgets: getWidgets, + activeWidget: getActiveWidget, widgetAnchor: getWidgetAnchor, noWidgetsMessage: getNoWidgetsMessage });