From ed97111f4940a39652044ef39d4bf7ee7ad9ee65 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Sun, 7 Mar 2021 22:45:29 -0500 Subject: [PATCH 01/27] Start moving visFtrs to id-based lookup instead of duplicating objects. --- src/lib/components/App.js | 25 +++++++++++++++++-------- src/lib/components/FeatureList.js | 12 ++++++------ src/lib/components/FeatureListItem.js | 2 +- src/lib/components/InteractiveMap.js | 11 +++++++---- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 63adb0f..b1b6ac7 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -28,6 +28,7 @@ export default class App extends React.Component { /** Array of visible feature points in maps and lists. (visibleFeatures) */ allFeatures: [], visFtrs: [], + visFtrsNew: [], /** The type of view. * Options: list, detail, map, filter * Last two only display differently on mobile. */ @@ -78,7 +79,7 @@ export default class App extends React.Component { if (!isArtwork(f)) return null return f }).filter(Boolean) - this.setState({ allFeatures: visFtrs, visFtrs }, + this.setState({ allFeatures: visFtrs.map((f, i) => Object.assign({index: i}, f)), visFtrs, visFtrsNew: [...visFtrs.keys()] }, // Sort after first load. () => { this.sortList() } ); @@ -105,7 +106,7 @@ export default class App extends React.Component { setVisibleFeatures = (visFtrs) => { this.setState( - {visFtrs: visFtrs}, + {visFtrsNew: visFtrs}, () => { this.sortList() } ); } @@ -152,7 +153,7 @@ export default class App extends React.Component { return keepForYear && keepForWard && keepForProgram; }) - this.setVisibleFeatures(visibleFeatures); + this.setVisibleFeatures(visibleFeatures.map(f => f.index)); } handleSelectYears = (selectedOptions) => { @@ -212,19 +213,19 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visFtrs).asc(u => u.properties.title ? u.properties.title.toLowerCase() : u.properties.title) + sortedList = sort(this.state.visFtrsNew).asc(i => this.state.allFeatures[i].properties?.title.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visFtrs).desc(u => u.properties.title ? u.properties.title.toLowerCase() : u.properties.title) + sortedList = sort(this.state.visFtrsNew).desc(i => this.state.allFeatures[i].properties?.title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visFtrs).asc(u => u.properties.year) + sortedList = sort(this.state.visFtrsNew).asc(i => this.state.allFeatures[i].properties?.year) break case 'year-desc': - sortedList = sort(this.state.visFtrs).desc(u => u.properties.year) + sortedList = sort(this.state.visFtrsNew).desc(i => this.state.allFeatures[i].properties?.year) break } - this.setState({visFtrs: sortedList}) + this.setState({visFtrsNew: sortedList}) } handleMapClick = (feature) => { @@ -275,7 +276,9 @@ export default class App extends React.Component { render() { const { showSplash, + allFeatures, visFtrs, + visFtrsNew, activeFeature, isMobileView, isFiltered, @@ -299,7 +302,9 @@ export default class App extends React.Component { }> @@ -319,7 +324,9 @@ export default class App extends React.Component { /> @@ -331,7 +338,9 @@ export default class App extends React.Component { { +const FeatureList = ({ allFeatures, features = [], featuresNew = [], onItemClick, isMobile, activeFeature }) => { useEffect(() => { forceCheck() }); return (
-

{features.length} Results

+

{featuresNew.length} Results

    - {features.map(feature => + {featuresNew.map(i => { - features.map((feature, i) => { + featuresNew.map((i) => { + const feature = allFeatures[i]; const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid From 7008d2a81cefb23abe7db8ec4404a4b9d31129ae Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Sun, 7 Mar 2021 22:50:59 -0500 Subject: [PATCH 02/27] Removed references to object-based visFtrs. --- src/lib/components/App.js | 5 ----- src/lib/components/FeatureList.js | 4 ++-- src/lib/components/InteractiveMap.js | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index b1b6ac7..9409396 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -27,7 +27,6 @@ export default class App extends React.Component { state = { /** Array of visible feature points in maps and lists. (visibleFeatures) */ allFeatures: [], - visFtrs: [], visFtrsNew: [], /** The type of view. * Options: list, detail, map, filter @@ -277,7 +276,6 @@ export default class App extends React.Component { const { showSplash, allFeatures, - visFtrs, visFtrsNew, activeFeature, isMobileView, @@ -303,7 +301,6 @@ export default class App extends React.Component { { +const FeatureList = ({ allFeatures = [], featuresNew = [], onItemClick, isMobile, activeFeature }) => { useEffect(() => { forceCheck() }); @@ -29,7 +29,7 @@ const FeatureList = ({ allFeatures, features = [], featuresNew = [], onItemClick FeatureListItem.propTypes = { - features: PropTypes.arrayOf(PropTypes.object), + featuresNew: PropTypes.arrayOf(PropTypes.number), onItemClick: PropTypes.func, } diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 3dc0876..8f967e7 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -19,7 +19,6 @@ class InteractiveMap extends React.Component { this.state = { prevActiveFeature: {}, allFeatures: this.props.allFeatures, - features: this.props.features, featuresNew: this.props.featuresNew, wards: {}, } From 86e9d5935988323ed060d474a40bb544cb2ffa4f Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Sun, 7 Mar 2021 23:41:35 -0500 Subject: [PATCH 03/27] Store index of activeFeature instead of full object. --- src/lib/components/App.js | 20 +++++++++++--------- src/lib/components/FeatureListItem.js | 2 +- src/lib/components/InteractiveMap.js | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 9409396..53ecd6a 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -32,8 +32,8 @@ export default class App extends React.Component { * Options: list, detail, map, filter * Last two only display differently on mobile. */ viewType: "map", - /** Full object representing active artwork. */ - activeFeature: null, + /** Integer representing active artwork. */ + activeFeatureIndex: null, /** Keep track of whether any filters are applied. */ isFiltered: false, /** Array of year OptionTypes to filter features by. */ @@ -227,32 +227,32 @@ export default class App extends React.Component { this.setState({visFtrsNew: sortedList}) } - handleMapClick = (feature) => { + handleMapClick = (featureIndex) => { ReactGA.event({ category: 'Map', action: 'Clicked feature', label: 'ward or artwork', }) - this.setActiveFeature(feature) + this.setActiveFeature(featureIndex) } - setActiveFeature = (feature) => { + setActiveFeature = (featureIndex) => { this.setState({ - activeFeature: feature, + activeFeatureIndex: featureIndex, }); } handleCloseFeature = () => { - const uid = this.state.activeFeature.properties.uid + const uid = this.state.allFeatures[this.state.activeFeatureIndex].properties.uid if (typeof(document) !== 'undefined') { const featureBtn = document.getElementById(uid) featureBtn.scrollIntoView() featureBtn.focus() } this.setState({ - activeFeature: null + activeFeatureIndex: null }) } @@ -277,13 +277,15 @@ export default class App extends React.Component { showSplash, allFeatures, visFtrsNew, - activeFeature, + activeFeatureIndex, isMobileView, isFiltered, viewType, showWardLayer, } = this.state; + const activeFeature = allFeatures[activeFeatureIndex] + return (
    diff --git a/src/lib/components/FeatureListItem.js b/src/lib/components/FeatureListItem.js index 68308b4..906acab 100644 --- a/src/lib/components/FeatureListItem.js +++ b/src/lib/components/FeatureListItem.js @@ -16,7 +16,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { value: uid, }) - onClick(feature) + onClick(feature.index) } const handleKeyPress = (event) => { diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 8f967e7..a457451 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -164,7 +164,7 @@ class InteractiveMap extends React.Component { key={feature.properties.uid} icon={icon} position={{ lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] }} - onClick={ () => onFeatureMapClick(feature) } + onClick={ () => onFeatureMapClick(feature.index) } zIndex={isSelected ? 2 : 1} /> ) From db605a34ac13f5781225b047c7753cd5558cb576 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Sun, 7 Mar 2021 23:59:12 -0500 Subject: [PATCH 04/27] Sort list artworks by key. --- package-lock.json | 5 +++++ package.json | 1 + src/lib/components/App.js | 9 +++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 71fe6e8..0f2f6ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9275,6 +9275,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", diff --git a/package.json b/package.json index 2e3e8fd..47e8816 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "fast-sort": "^1.5.6", + "lodash-es": "^4.17.21", "node-sass": "^4.13.1", "react-app-polyfill": "^1.0.6", "react-ga": "^2.7.0", diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 53ecd6a..ba37b25 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -3,6 +3,7 @@ import ReactGA from 'react-ga'; import sort from 'fast-sort'; import runtimeEnv from '@mars/heroku-js-runtime-env'; import { forceCheck } from 'react-lazyload'; +import * as _ from 'lodash'; import BetaBanner from "./BetaBanner"; import Splash from "./Splash"; @@ -212,16 +213,16 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visFtrsNew).asc(i => this.state.allFeatures[i].properties?.title.toLowerCase()) + sortedList = sort(this.state.visFtrsNew).asc(i => _.find(this.state.allFeatures, { index: i }).properties?.title.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visFtrsNew).desc(i => this.state.allFeatures[i].properties?.title.toLowerCase()) + sortedList = sort(this.state.visFtrsNew).desc(i => _.find(this.state.allFeatures, { index: i }).properties?.title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visFtrsNew).asc(i => this.state.allFeatures[i].properties?.year) + sortedList = sort(this.state.visFtrsNew).asc(i => _.find(this.state.allFeatures, { index: i }).properties?.year) break case 'year-desc': - sortedList = sort(this.state.visFtrsNew).desc(i => this.state.allFeatures[i].properties?.year) + sortedList = sort(this.state.visFtrsNew).desc(i => _.find(this.state.allFeatures, { index: i }).properties?.year) break } this.setState({visFtrsNew: sortedList}) From dfe1364df46cc327f5d06ea3dd907cee0eecfd7a Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 00:20:29 -0500 Subject: [PATCH 05/27] Select visible artworks by ID. --- src/lib/components/App.js | 32 +++++++++++++-------------- src/lib/components/FeatureList.js | 12 +++++----- src/lib/components/FeatureListItem.js | 2 +- src/lib/components/InteractiveMap.js | 7 +++--- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index ba37b25..3f20c43 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -33,8 +33,8 @@ export default class App extends React.Component { * Options: list, detail, map, filter * Last two only display differently on mobile. */ viewType: "map", - /** Integer representing active artwork. */ - activeFeatureIndex: null, + /** Integer representing active artwork ID. */ + activeFeatureId: null, /** Keep track of whether any filters are applied. */ isFiltered: false, /** Array of year OptionTypes to filter features by. */ @@ -79,7 +79,7 @@ export default class App extends React.Component { if (!isArtwork(f)) return null return f }).filter(Boolean) - this.setState({ allFeatures: visFtrs.map((f, i) => Object.assign({index: i}, f)), visFtrs, visFtrsNew: [...visFtrs.keys()] }, + this.setState({ allFeatures: visFtrs, visFtrsNew: visFtrs.map(f => f.id) }, // Sort after first load. () => { this.sortList() } ); @@ -153,7 +153,7 @@ export default class App extends React.Component { return keepForYear && keepForWard && keepForProgram; }) - this.setVisibleFeatures(visibleFeatures.map(f => f.index)); + this.setVisibleFeatures(visibleFeatures.map(f => f.id)); } handleSelectYears = (selectedOptions) => { @@ -213,47 +213,47 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visFtrsNew).asc(i => _.find(this.state.allFeatures, { index: i }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visFtrsNew).asc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visFtrsNew).desc(i => _.find(this.state.allFeatures, { index: i }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visFtrsNew).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visFtrsNew).asc(i => _.find(this.state.allFeatures, { index: i }).properties?.year) + sortedList = sort(this.state.visFtrsNew).asc(id => _.find(this.state.allFeatures, { id }).properties?.year) break case 'year-desc': - sortedList = sort(this.state.visFtrsNew).desc(i => _.find(this.state.allFeatures, { index: i }).properties?.year) + sortedList = sort(this.state.visFtrsNew).desc(id => _.find(this.state.allFeatures, { id }).properties?.year) break } this.setState({visFtrsNew: sortedList}) } - handleMapClick = (featureIndex) => { + handleMapClick = (featureId) => { ReactGA.event({ category: 'Map', action: 'Clicked feature', label: 'ward or artwork', }) - this.setActiveFeature(featureIndex) + this.setActiveFeature(featureId) } - setActiveFeature = (featureIndex) => { + setActiveFeature = (featureId) => { this.setState({ - activeFeatureIndex: featureIndex, + activeFeatureId: featureId, }); } handleCloseFeature = () => { - const uid = this.state.allFeatures[this.state.activeFeatureIndex].properties.uid + const uid = this.state.allFeatures[this.state.activeFeatureId].properties.uid if (typeof(document) !== 'undefined') { const featureBtn = document.getElementById(uid) featureBtn.scrollIntoView() featureBtn.focus() } this.setState({ - activeFeatureIndex: null + activeFeatureId: null }) } @@ -278,14 +278,14 @@ export default class App extends React.Component { showSplash, allFeatures, visFtrsNew, - activeFeatureIndex, + activeFeatureId, isMobileView, isFiltered, viewType, showWardLayer, } = this.state; - const activeFeature = allFeatures[activeFeatureIndex] + const activeFeature = allFeatures[activeFeatureId] return (
    diff --git a/src/lib/components/FeatureList.js b/src/lib/components/FeatureList.js index 2626018..ac67fe1 100644 --- a/src/lib/components/FeatureList.js +++ b/src/lib/components/FeatureList.js @@ -1,6 +1,7 @@ import React, { useEffect } from 'react'; import PropTypes from "prop-types"; import { forceCheck } from 'react-lazyload'; +import * as _ from 'lodash'; import FeatureListItem from './FeatureListItem'; @@ -13,15 +14,16 @@ const FeatureList = ({ allFeatures = [], featuresNew = [], onItemClick, isMobile

    {featuresNew.length} Results

      - {featuresNew.map(i => - { + const feature = _.find(allFeatures, { id }) + return - )} + })}
    ); diff --git a/src/lib/components/FeatureListItem.js b/src/lib/components/FeatureListItem.js index 906acab..83dc99a 100644 --- a/src/lib/components/FeatureListItem.js +++ b/src/lib/components/FeatureListItem.js @@ -16,7 +16,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { value: uid, }) - onClick(feature.index) + onClick(feature.id) } const handleKeyPress = (event) => { diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index a457451..7d7c45a 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -1,6 +1,7 @@ import React, { createRef, lazy, Suspense } from 'react'; import PropTypes from "prop-types"; import { Map, Marker, GoogleApiWrapper } from '@nomadiclabs/google-maps-react'; +import * as _ from 'lodash'; import * as constants from "../constants"; @@ -147,8 +148,8 @@ class InteractiveMap extends React.Component { > { - featuresNew.map((i) => { - const feature = allFeatures[i]; + featuresNew.map((id) => { + const feature = _.find(allFeatures, { id }); const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid @@ -164,7 +165,7 @@ class InteractiveMap extends React.Component { key={feature.properties.uid} icon={icon} position={{ lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] }} - onClick={ () => onFeatureMapClick(feature.index) } + onClick={ () => onFeatureMapClick(feature.id) } zIndex={isSelected ? 2 : 1} /> ) From 6c63650302edbe90386d62e168b502e6ea091fd0 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 14:37:34 -0500 Subject: [PATCH 06/27] Bugfix: Clicking map now worked well. --- src/lib/components/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 3f20c43..ba01e0d 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -285,7 +285,7 @@ export default class App extends React.Component { showWardLayer, } = this.state; - const activeFeature = allFeatures[activeFeatureId] + const activeFeature = _.find(allFeatures, { id: activeFeatureId }) return (
    From cfc6e20a8c423e70a9cf103e81825a0653e4b7b0 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 14:38:00 -0500 Subject: [PATCH 07/27] Removed unneeded state in InteractiveMap component. --- src/lib/components/InteractiveMap.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 7d7c45a..6f9f841 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -18,7 +18,6 @@ class InteractiveMap extends React.Component { constructor(props) { super(props) this.state = { - prevActiveFeature: {}, allFeatures: this.props.allFeatures, featuresNew: this.props.featuresNew, wards: {}, From 642fbd9dd9278943f7fd8653de9942ae1d13a94f Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 14:38:37 -0500 Subject: [PATCH 08/27] Bugfix with feature ID. --- src/lib/components/MapMarkers.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/MapMarkers.js b/src/lib/components/MapMarkers.js index a1a4559..48c4bed 100644 --- a/src/lib/components/MapMarkers.js +++ b/src/lib/components/MapMarkers.js @@ -67,7 +67,7 @@ const MapMarkers = ({ features, activeFeature, onFeatureMapClick }) => { key={feature.properties.uid} position={[feature.geometry.coordinates[1], feature.geometry.coordinates[0]]} icon={icon} - onClick={() => onFeatureMapClick(feature) } + onClick={() => onFeatureMapClick(feature.id) } zIndexOffset={isSelected ? 9999 : 0} /> ) From 6e3f7ba03af0062e4f260106469cc33c8ef860aa Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 17:13:48 -0500 Subject: [PATCH 09/27] Renamed vars and funcs for consistency. --- src/lib/components/App.js | 41 ++++++++++++++-------------- src/lib/components/FeatureList.js | 8 +++--- src/lib/components/InteractiveMap.js | 10 +++---- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index ba01e0d..b792006 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -26,9 +26,10 @@ const Footer = lazy(() => import('./Footer')); export default class App extends React.Component { state = { - /** Array of visible feature points in maps and lists. (visibleFeatures) */ + /** Array of all artwork objects that may show in maps and lists. */ allFeatures: [], - visFtrsNew: [], + /** Array of visible artwork IDs in maps and lists. */ + visibleFeatureIds: [], /** The type of view. * Options: list, detail, map, filter * Last two only display differently on mobile. */ @@ -75,11 +76,11 @@ export default class App extends React.Component { fetch(this.props.featuresDataSource) .then(response => response.json()) .then(json => { - const visFtrs = json.features.map(f => { + const allFeatures = json.features.map(f => { if (!isArtwork(f)) return null return f }).filter(Boolean) - this.setState({ allFeatures: visFtrs, visFtrsNew: visFtrs.map(f => f.id) }, + this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.id) }, // Sort after first load. () => { this.sortList() } ); @@ -104,9 +105,9 @@ export default class App extends React.Component { }) } - setVisibleFeatures = (visFtrs) => { + setVisibleFeatureIds = (visibleFeatureIds) => { this.setState( - {visFtrsNew: visFtrs}, + { visibleFeatureIds }, () => { this.sortList() } ); } @@ -153,7 +154,7 @@ export default class App extends React.Component { return keepForYear && keepForWard && keepForProgram; }) - this.setVisibleFeatures(visibleFeatures.map(f => f.id)); + this.setVisibleFeatureIds(visibleFeatures.map(f => f.id)); } handleSelectYears = (selectedOptions) => { @@ -213,19 +214,19 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visFtrsNew).asc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visFtrsNew).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visFtrsNew).asc(id => _.find(this.state.allFeatures, { id }).properties?.year) + sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.year) break case 'year-desc': - sortedList = sort(this.state.visFtrsNew).desc(id => _.find(this.state.allFeatures, { id }).properties?.year) + sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.year) break } - this.setState({visFtrsNew: sortedList}) + this.setState({ visibleFeatureIds: sortedList }) } handleMapClick = (featureId) => { @@ -234,11 +235,11 @@ export default class App extends React.Component { action: 'Clicked feature', label: 'ward or artwork', }) - this.setActiveFeature(featureId) + this.setActiveFeatureId(featureId) } - setActiveFeature = (featureId) => { + setActiveFeatureId = (featureId) => { this.setState({ activeFeatureId: featureId, }); @@ -277,7 +278,7 @@ export default class App extends React.Component { const { showSplash, allFeatures, - visFtrsNew, + visibleFeatureIds, activeFeatureId, isMobileView, isFiltered, @@ -304,8 +305,8 @@ export default class App extends React.Component { @@ -325,8 +326,8 @@ export default class App extends React.Component { @@ -338,7 +339,7 @@ export default class App extends React.Component { isMobile={isMobileView} onFeatureMapClick={this.handleMapClick} allFeatures={allFeatures} - featuresNew={visFtrsNew} + visibleFeatureIds={visibleFeatureIds} activeFeature={activeFeature} showWardLayer={showWardLayer} googleApiKey={this.props.googleApiKey} diff --git a/src/lib/components/FeatureList.js b/src/lib/components/FeatureList.js index ac67fe1..974ba79 100644 --- a/src/lib/components/FeatureList.js +++ b/src/lib/components/FeatureList.js @@ -5,16 +5,16 @@ import * as _ from 'lodash'; import FeatureListItem from './FeatureListItem'; -const FeatureList = ({ allFeatures = [], featuresNew = [], onItemClick, isMobile, activeFeature }) => { +const FeatureList = ({ allFeatures = [], featureIds = [], onItemClick, isMobile, activeFeature }) => { useEffect(() => { forceCheck() }); return (
    -

    {featuresNew.length} Results

    +

    {featureIds.length} Results

      - {featuresNew.map(id => { + {featureIds.map(id => { const feature = _.find(allFeatures, { id }) return { - featuresNew.map((id) => { + visibleFeatureIds.map((id) => { const feature = _.find(allFeatures, { id }); const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" From b94b8c65ad6dede5bf675cf2dd52ae524102547e Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Mon, 8 Mar 2021 20:47:11 -0500 Subject: [PATCH 10/27] Added debug code to test issue on heroku. --- src/lib/components/InteractiveMap.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index edbd0ce..d635226 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -149,6 +149,7 @@ class InteractiveMap extends React.Component { { visibleFeatureIds.map((id) => { const feature = _.find(allFeatures, { id }); + if (!feature) { console.log(feature) } const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid From c01a20eb4e8ca3cfd849bcbc267fd2f95be74258 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Tue, 9 Mar 2021 08:23:53 -0500 Subject: [PATCH 11/27] More debug. --- src/lib/components/InteractiveMap.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index d635226..8762105 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -148,6 +148,7 @@ class InteractiveMap extends React.Component { { visibleFeatureIds.map((id) => { + console.log(id) const feature = _.find(allFeatures, { id }); if (!feature) { console.log(feature) } const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] From 1448cc44d1aad22b2947f8e9e0754c910b23c9e3 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Tue, 9 Mar 2021 14:00:56 -0500 Subject: [PATCH 12/27] Minimal proof-of-concept for live data :tada: --- .env | 4 + package-lock.json | 117 +++++++++++++++++++++++++- package.json | 3 + src/lib/components/App.js | 64 +++++++++++--- src/lib/components/FeatureList.js | 6 +- src/lib/components/FeatureListItem.js | 6 +- src/lib/components/InteractiveMap.js | 18 ++-- src/lib/components/MapMarkers.js | 2 +- src/lib/constants.js | 2 + 9 files changed, 191 insertions(+), 31 deletions(-) create mode 100644 .env diff --git a/.env b/.env new file mode 100644 index 0000000..57a45af --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +#REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyBZxy09BXS_K-RtyIpqv7Spu4y6c5qLrfo + +REACT_APP_GOOGLE_MAPS_API_KEY=AIzaSyCqhXDnXAnPgXk_4n9EC1u2jeEP5E10k-E +REACT_APP_GRAPHQL_HOST=start-v3.hasura.app diff --git a/package-lock.json b/package-lock.json index 0f2f6ad..b643a11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,6 +4,26 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@apollo/client": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.3.11.tgz", + "integrity": "sha512-54+D5FB6RJlQ+g37f432gaexnyvDsG5X6L9VO5kqN54HJlbF8hCf/8CXtAQEHCWodAwZhy6kOLp2RM96829q3A==", + "requires": { + "@graphql-typed-document-node/core": "^3.0.0", + "@types/zen-observable": "^0.8.0", + "@wry/context": "^0.5.2", + "@wry/equality": "^0.3.0", + "fast-json-stable-stringify": "^2.0.0", + "graphql-tag": "^2.12.0", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.14.0", + "prop-types": "^15.7.2", + "symbol-observable": "^2.0.0", + "ts-invariant": "^0.6.0", + "tslib": "^1.10.0", + "zen-observable": "^0.8.14" + } + }, "@babel/cli": { "version": "7.12.1", "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.12.1.tgz", @@ -1391,6 +1411,11 @@ "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz", "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" }, + "@graphql-typed-document-node/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz", + "integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg==" + }, "@hapi/address": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.4.tgz", @@ -2246,6 +2271,11 @@ } } }, + "@types/ungap__global-this": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@types/ungap__global-this/-/ungap__global-this-0.3.1.tgz", + "integrity": "sha512-+/DsiV4CxXl6ZWefwHZDXSe1Slitz21tom38qPCaG0DYCS1NnDPIQDTKcmQ/tvK/edJUKkmuIDBJbmKDiB0r/g==" + }, "@types/yargs": { "version": "13.0.11", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.11.tgz", @@ -2259,6 +2289,11 @@ "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" }, + "@types/zen-observable": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz", + "integrity": "sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg==" + }, "@typescript-eslint/eslint-plugin": { "version": "2.34.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz", @@ -2333,6 +2368,11 @@ } } }, + "@ungap/global-this": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@ungap/global-this/-/global-this-0.4.4.tgz", + "integrity": "sha512-mHkm6FvepJECMNthFuIgpAEFmPOk71UyXuIxYfjytvFTnSDBIz7jmViO+LfHI/AjrazWije0PnSP3+/NlwzqtA==" + }, "@webassemblyjs/ast": { "version": "1.8.5", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz", @@ -2509,6 +2549,30 @@ "@xtuc/long": "4.2.2" } }, + "@wry/context": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.4.tgz", + "integrity": "sha512-/pktJKHUXDr4D6TJqWgudOPJW2Z+Nb+bqk40jufA3uTkLbnCRKdJPiYDIa/c7mfcPH8Hr6O8zjCERpg5Sq04Zg==", + "requires": { + "tslib": "^1.14.1" + } + }, + "@wry/equality": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.3.3.tgz", + "integrity": "sha512-pMrKHIgDAWxLDTGsbaVag+USmwZ2+gGrSBrtyGUxp2pxRg1Cad70lI/hd0NTPtJ4zJxN16EQ679U1Rts83AF5g==", + "requires": { + "tslib": "^1.14.1" + } + }, + "@wry/trie": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.2.2.tgz", + "integrity": "sha512-OxqBB39x6MfHaa2HpMiRMfhuUnQTddD32Ko020eBeJXq87ivX6xnSSnzKHVbA21p7iqBASz8n/07b6W5wW1BVQ==", + "requires": { + "tslib": "^1.14.1" + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -7192,6 +7256,19 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, + "graphql": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz", + "integrity": "sha512-OmaM7y0kaK31NKG31q4YbD2beNYa6jBBKtMFT6gLYJljHLJr42IqJ8KX08u3Li/0ifzTU5HjmoOOrwa5BRLeDA==" + }, + "graphql-tag": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.1.tgz", + "integrity": "sha512-LPewEE1vzGkHnCO8zdOGogKsHHBdtpGyihow1UuMwp6RnZa0lAS7NcbvltLOuo4pi5diQCPASAXZkQq44ffixA==", + "requires": { + "tslib": "^1.14.1" + } + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -7376,6 +7453,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "hosted-git-info": { "version": "2.8.8", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", @@ -10429,6 +10514,15 @@ "is-wsl": "^1.1.0" } }, + "optimism": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.14.0.tgz", + "integrity": "sha512-ygbNt8n4DOCVpkwiLF+IrKKeNHOjtr9aXLWGP9HNJGoblSGsnVbJLstcH6/nE9Xy5ZQtlkSioFQNnthmENW6FQ==", + "requires": { + "@wry/context": "^0.5.2", + "@wry/trie": "^0.2.1" + } + }, "optimize-css-assets-webpack-plugin": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.3.tgz", @@ -14611,6 +14705,11 @@ } } }, + "symbol-observable": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", + "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==" + }, "symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -15067,6 +15166,16 @@ "glob": "^7.1.2" } }, + "ts-invariant": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.6.1.tgz", + "integrity": "sha512-QQgN33g8E8yrdDuH29HASveLtbzMnRRgWh0i/JNTW4+zcLsdIOnfsgEDi/NKx4UckQyuMFt9Ujm6TWLWQ58Kvg==", + "requires": { + "@types/ungap__global-this": "^0.3.1", + "@ungap/global-this": "^0.4.2", + "tslib": "^1.9.3" + } + }, "ts-pnp": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz", @@ -15076,8 +15185,7 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsutils": { "version": "3.17.1", @@ -16270,6 +16378,11 @@ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" } } + }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" } } } diff --git a/package.json b/package.json index 47e8816..1591b01 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ ], "homepage": "https://streetartoronto.ca", "dependencies": { + "@apollo/client": "^3.3.11", "@mars/heroku-js-runtime-env": "^3.0.2", "@nomadiclabs/google-maps-react": "^2.0.6", "@testing-library/jest-dom": "^4.2.4", @@ -19,6 +20,7 @@ "@testing-library/user-event": "^7.2.1", "fast-sort": "^1.5.6", "lodash-es": "^4.17.21", + "graphql": "^15.5.0", "node-sass": "^4.13.1", "react-app-polyfill": "^1.0.6", "react-ga": "^2.7.0", @@ -46,6 +48,7 @@ "react-dom": "^16.13.1" }, "scripts": { + "clean": "rm -rf node_modules/", "analyze": "npm run build && source-map-explorer 'build/static/js/*.js'", "start": "react-scripts start", "build": "react-scripts build", diff --git a/src/lib/components/App.js b/src/lib/components/App.js index b792006..8a0684e 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -4,6 +4,7 @@ import sort from 'fast-sort'; import runtimeEnv from '@mars/heroku-js-runtime-env'; import { forceCheck } from 'react-lazyload'; import * as _ from 'lodash'; +import { gql, ApolloClient, InMemoryCache } from '@apollo/client'; import BetaBanner from "./BetaBanner"; import Splash from "./Splash"; @@ -23,6 +24,28 @@ const Filters = lazy(() => import('./Filters')); const Header = lazy(() => import('./Header')); const Footer = lazy(() => import('./Footer')); +const client = new ApolloClient({ + uri: constants.GRAPHQL_ENDPOINT, + cache: new InMemoryCache() +}); + +const GET_ARTWORKS = gql` + query GetArtworks { + artworks(limit: 100, order_by: { uid: asc }) { + uid + title + year + program_details { + program_name + } + location_details { + latitude + longitude + } + } + } +`; + export default class App extends React.Component { state = { @@ -30,6 +53,8 @@ export default class App extends React.Component { allFeatures: [], /** Array of visible artwork IDs in maps and lists. */ visibleFeatureIds: [], + allFeaturesNew: [], + visibleFeatureIdsNew: [], /** The type of view. * Options: list, detail, map, filter * Last two only display differently on mobile. */ @@ -73,6 +98,15 @@ export default class App extends React.Component { feature.geometry.type === 'Point' ) + client.query({ query: GET_ARTWORKS }) + .then(result => { + console.log(result.data.artworks) + this.setState({ + allFeaturesNew: result.data.artworks, + visibleFeatureIdsNew: result.data.artworks.map(f => f.uid), + }) + }); + fetch(this.props.featuresDataSource) .then(response => response.json()) .then(json => { @@ -80,7 +114,7 @@ export default class App extends React.Component { if (!isArtwork(f)) return null return f }).filter(Boolean) - this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.id) }, + this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.uid) }, // Sort after first load. () => { this.sortList() } ); @@ -107,7 +141,7 @@ export default class App extends React.Component { setVisibleFeatureIds = (visibleFeatureIds) => { this.setState( - { visibleFeatureIds }, + { visibleFeatureIdsNew: visibleFeatureIds }, () => { this.sortList() } ); } @@ -154,7 +188,7 @@ export default class App extends React.Component { return keepForYear && keepForWard && keepForProgram; }) - this.setVisibleFeatureIds(visibleFeatures.map(f => f.id)); + this.setVisibleFeatureIds(visibleFeatures.map(f => f.uid)); } handleSelectYears = (selectedOptions) => { @@ -214,19 +248,19 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).title.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.year) + sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).year) break case 'year-desc': - sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.year) + sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).year) break } - this.setState({ visibleFeatureIds: sortedList }) + this.setState({ visibleFeatureIdsNew: sortedList }) } handleMapClick = (featureId) => { @@ -279,6 +313,8 @@ export default class App extends React.Component { showSplash, allFeatures, visibleFeatureIds, + allFeaturesNew, + visibleFeatureIdsNew, activeFeatureId, isMobileView, isFiltered, @@ -304,8 +340,8 @@ export default class App extends React.Component { }> @@ -325,8 +361,8 @@ export default class App extends React.Component { /> @@ -338,8 +374,8 @@ export default class App extends React.Component { {featureIds.length} Results

        {featureIds.map(id => { - const feature = _.find(allFeatures, { id }) + console.log(id) + console.log(allFeatures) + const feature = _.find(allFeatures, { uid: id }) return { - const { uid=0, year, artist, title, media=[], address } = feature.properties; + const { uid=0, year, artist='', title, media=[], address='' } = feature; const handleClick = () => { ReactGA.event({ @@ -16,7 +16,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { value: uid, }) - onClick(feature.id) + onClick(feature.uid) } const handleKeyPress = (event) => { @@ -33,7 +33,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { onKeyPress={handleKeyPress} tabIndex={0} role="button" - aria-expanded={activeFeature && (activeFeature.properties.uid === uid) ? 'true' : 'false'} + aria-expanded={activeFeature && (activeFeature.uid === uid) ? 'true' : 'false'} aria-controls='detail' id={uid} > diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 8762105..678e31d 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -125,8 +125,8 @@ class InteractiveMap extends React.Component { render() { - const { loaded, google, activeFeature, onFeatureMapClick } = this.props; - const { allFeatures, visibleFeatureIds } = this.state; + const { allFeatures, loaded, google, activeFeature, onFeatureMapClick } = this.props; + const { visibleFeatureIds } = this.state; const zoom = activeFeature ? constants.MAP_ZOOM_LEVEL.FEATURE : constants.MAP_ZOOM_LEVEL.DEFAULT const settings = { ...this.mapSettings, zoom } const center = activeFeature ? null : constants.DEFAULT_MAP_CENTER; @@ -149,11 +149,11 @@ class InteractiveMap extends React.Component { { visibleFeatureIds.map((id) => { console.log(id) - const feature = _.find(allFeatures, { id }); - if (!feature) { console.log(feature) } + console.log(allFeatures) + const feature = _.find(allFeatures, { uid: id }); const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] - const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" - const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid + const program = validPrograms.includes(feature.program_details?.program_name) ? feature.program_details.program_name : "Other" + const isSelected = activeFeature && feature.uid === activeFeature.properties.uid const icon = { url: constants.ICONS_REG[program].icon, anchor: isSelected ? new google.maps.Point(20, 20) : new google.maps.Point(10, 10), @@ -163,10 +163,10 @@ class InteractiveMap extends React.Component { return( onFeatureMapClick(feature.id) } + position={{ lng: feature.location_details?.longitude, lat: feature.location_details?.latitude }} + onClick={ () => onFeatureMapClick(feature.uid) } zIndex={isSelected ? 2 : 1} /> ) diff --git a/src/lib/components/MapMarkers.js b/src/lib/components/MapMarkers.js index 48c4bed..7691f6f 100644 --- a/src/lib/components/MapMarkers.js +++ b/src/lib/components/MapMarkers.js @@ -67,7 +67,7 @@ const MapMarkers = ({ features, activeFeature, onFeatureMapClick }) => { key={feature.properties.uid} position={[feature.geometry.coordinates[1], feature.geometry.coordinates[0]]} icon={icon} - onClick={() => onFeatureMapClick(feature.id) } + onClick={() => onFeatureMapClick(feature.uid) } zIndexOffset={isSelected ? 9999 : 0} /> ) diff --git a/src/lib/constants.js b/src/lib/constants.js index 06529f1..105bcaf 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -3,6 +3,8 @@ import markerRed from './assets/img/marker-red.svg'; import markerGreen from './assets/img/marker-green.svg'; import markerYellow from './assets/img/marker-yellow.svg'; +export const GRAPHQL_ENDPOINT = 'https://start-v3.hasura.app/v1/graphql'; + export const ICONS_REG = { "Partnership Program": { icon: markerBlue From 00e1ab67d26eb7f0588eda5dc0272a4a4428c49f Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Tue, 9 Mar 2021 17:10:51 -0500 Subject: [PATCH 13/27] Ensure able to feature from hasura api. --- public/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/public/index.html b/public/index.html index ac37a0c..8540bf0 100644 --- a/public/index.html +++ b/public/index.html @@ -19,6 +19,7 @@ font-src 'self' fonts.gstatic.com; media-src dl.airtable.com; style-src 'self' 'unsafe-inline' fonts.googleapis.com unpkg.com; + connect-src 'self' %REACT_APP_GRAPHQL_HOST%; " /> From 4994bdefb49ce7fa3fe2c4529009a97a4c5df8ae Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Tue, 9 Mar 2021 17:12:08 -0500 Subject: [PATCH 14/27] Force sort on initial load. --- src/lib/components/App.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 8a0684e..ee05af9 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -104,7 +104,10 @@ export default class App extends React.Component { this.setState({ allFeaturesNew: result.data.artworks, visibleFeatureIdsNew: result.data.artworks.map(f => f.uid), - }) + }, + // Sort after first load. + () => { this.sortList() } + ) }); fetch(this.props.featuresDataSource) From 8577757bba69774f692eb8e5476eac019476294b Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Tue, 9 Mar 2021 23:43:46 -0500 Subject: [PATCH 15/27] Got filtering working. --- src/lib/components/App.js | 34 +++++----------------------- src/lib/components/FeatureList.js | 2 -- src/lib/components/InteractiveMap.js | 2 -- 3 files changed, 6 insertions(+), 32 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index ee05af9..8486c7a 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -35,6 +35,7 @@ const GET_ARTWORKS = gql` uid title year + ward program_details { program_name } @@ -100,7 +101,6 @@ export default class App extends React.Component { client.query({ query: GET_ARTWORKS }) .then(result => { - console.log(result.data.artworks) this.setState({ allFeaturesNew: result.data.artworks, visibleFeatureIdsNew: result.data.artworks.map(f => f.uid), @@ -163,35 +163,13 @@ export default class App extends React.Component { * @returns {undefined} */ filterFeatures = (activeYearOpts, activeWardOpts, activeProgramOpts) => { - - const checkForKeep = (feature, propName, activeOpts) => { - for (let i = 0; i < activeOpts.length; i++) { - if (feature.properties[propName] && - feature.properties[propName].toString() === activeOpts[i].value.toString() - ) { - return true; - } - } - return false; - } - - const isArtwork = (feature) => ( - feature.geometry !== null && - feature.geometry.type === 'Point' + const visibleFeatureIds = _.intersection( + _.filter(this.state.allFeaturesNew, f => _.includes(activeYearOpts.map(o => o.value), f.year.toString())).map(f => f.uid), + _.filter(this.state.allFeaturesNew, f => _.includes(activeWardOpts.map(o => o.value), f.ward.toString())).map(f => f.uid), + _.filter(this.state.allFeaturesNew, f => _.includes(activeProgramOpts.map(o => o.value), f.program_details?.program_name.toString())).map(f => f.uid), ) - - const visibleFeatures = this.state.allFeatures.filter(feature => { - if (!isArtwork(feature)) { return false } - - let keepForYear = checkForKeep(feature, 'year', activeYearOpts) - let keepForWard = checkForKeep(feature, 'ward', activeWardOpts) - let keepForProgram = checkForKeep(feature, 'program', activeProgramOpts) - - return keepForYear && keepForWard && keepForProgram; - }) - - this.setVisibleFeatureIds(visibleFeatures.map(f => f.uid)); + this.setVisibleFeatureIds(visibleFeatureIds); } handleSelectYears = (selectedOptions) => { diff --git a/src/lib/components/FeatureList.js b/src/lib/components/FeatureList.js index 99b2825..6d20b12 100644 --- a/src/lib/components/FeatureList.js +++ b/src/lib/components/FeatureList.js @@ -15,8 +15,6 @@ const FeatureList = ({ allFeatures = [], featureIds = [], onItemClick, isMobile,

        {featureIds.length} Results

          {featureIds.map(id => { - console.log(id) - console.log(allFeatures) const feature = _.find(allFeatures, { uid: id }) return { - console.log(id) - console.log(allFeatures) const feature = _.find(allFeatures, { uid: id }); const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.program_details?.program_name) ? feature.program_details.program_name : "Other" From 77a0a531f436450dc9adfd4699261e53fa2bff72 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 12:31:00 -0500 Subject: [PATCH 16/27] Get full-size images working. --- src/lib/components/App.js | 1 + src/lib/components/FeatureListItem.js | 10 +++++----- src/lib/utils.js | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 8486c7a..e1b743f 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -36,6 +36,7 @@ const GET_ARTWORKS = gql` title year ward + featured_media program_details { program_name } diff --git a/src/lib/components/FeatureListItem.js b/src/lib/components/FeatureListItem.js index fc7e9d9..8c51732 100644 --- a/src/lib/components/FeatureListItem.js +++ b/src/lib/components/FeatureListItem.js @@ -6,7 +6,7 @@ import * as utils from "../utils"; const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { - const { uid=0, year, artist='', title, media=[], address='' } = feature; + const { uid=0, year, artist='', title, featured_media, address='' } = feature; const handleClick = () => { ReactGA.event({ @@ -16,7 +16,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { value: uid, }) - onClick(feature.uid) + onClick(uid) } const handleKeyPress = (event) => { @@ -44,7 +44,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { aria-label="Thumbnail Preview" alt="Photo of artwork" className="list-img" - src={utils.getCoverImage(media)} + src={utils.getCoverImage(featured_media)} onError={utils.handleMissingImage} /> @@ -72,7 +72,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { FeatureListItem.propTypes = { uid: PropTypes.number, - media: PropTypes.arrayOf(PropTypes.object), + featured_media: PropTypes.arrayOf(PropTypes.string), artistName: PropTypes.string, address: PropTypes.string, year: PropTypes.number, @@ -81,7 +81,7 @@ FeatureListItem.propTypes = { FeatureListItem.defaultProps = { uid: 0, - media: [], + featured_media: [], } export default FeatureListItem diff --git a/src/lib/utils.js b/src/lib/utils.js index 7cd2b03..f69e8c8 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -7,9 +7,9 @@ export function handleMissingImage(e) { export const getCoverImage = (media) => { for (const mediaItem of media) { - if (!!mediaItem.thumbnails) { - return mediaItem.thumbnails.large.url - } + // TODO: Check on pending feature request for thumbnails from SyncInc. + // Not currently available. + return mediaItem } return '' } From f23997edff2474bb71ae2ca60d376af384e6ece0 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 12:31:23 -0500 Subject: [PATCH 17/27] Ensure scroll to list item works after artwork focus. --- src/lib/components/App.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index e1b743f..23d3bfa 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -263,9 +263,8 @@ export default class App extends React.Component { handleCloseFeature = () => { - const uid = this.state.allFeatures[this.state.activeFeatureId].properties.uid if (typeof(document) !== 'undefined') { - const featureBtn = document.getElementById(uid) + const featureBtn = document.getElementById(this.state.activeFeatureId) featureBtn.scrollIntoView() featureBtn.focus() } From 0355b36dbe634750e06a8b37626b510b988bf956 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 16:03:02 -0500 Subject: [PATCH 18/27] Bugfix for selecting active artwork. --- src/lib/components/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 23d3bfa..95a9ca8 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -303,7 +303,7 @@ export default class App extends React.Component { showWardLayer, } = this.state; - const activeFeature = _.find(allFeatures, { id: activeFeatureId }) + const activeFeature = _.find(allFeaturesNew, { uid: activeFeatureId }) return (
          From aeb244a5c9be2ce99bcb07d752158dc1805728f1 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 16:04:38 -0500 Subject: [PATCH 19/27] Get media slider working with direct links instead of thumbnail data. --- package-lock.json | 18 +++++++++++++----- package.json | 3 ++- src/lib/components/FeatureDetail.js | 29 ++++++++++++++--------------- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index b643a11..650c5b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9707,14 +9707,22 @@ "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true }, "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.29", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz", + "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==", "requires": { - "mime-db": "1.44.0" + "mime-db": "1.46.0" + }, + "dependencies": { + "mime-db": { + "version": "1.46.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz", + "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==" + } } }, "mimic-fn": { diff --git a/package.json b/package.json index 1591b01..c2e0f1c 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,9 @@ "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", "fast-sort": "^1.5.6", - "lodash-es": "^4.17.21", "graphql": "^15.5.0", + "lodash-es": "^4.17.21", + "mime-types": "^2.1.29", "node-sass": "^4.13.1", "react-app-polyfill": "^1.0.6", "react-ga": "^2.7.0", diff --git a/src/lib/components/FeatureDetail.js b/src/lib/components/FeatureDetail.js index 84e9812..3891079 100644 --- a/src/lib/components/FeatureDetail.js +++ b/src/lib/components/FeatureDetail.js @@ -1,5 +1,6 @@ import React, { lazy, Suspense } from "react"; import PropTypes from "prop-types"; +import mime from "mime-types"; import { BackToListViewButton } from './Buttons' import placeholder from '../assets/img/placeholder.jpg'; @@ -31,22 +32,20 @@ class FeatureDetail extends React.Component { getMediaData = (ftr) => { let mediaData = []; - if (ftr.geometry.type === "Point") { - if (ftr.properties.media) { - mediaData = ftr.properties.media.map( mediaItem => ({ - type: mediaItem.type, - mediaSrc: mediaItem.thumbnails ? mediaItem.thumbnails.large.url : mediaItem.url, - mediaAltText: "Photo of artwork.", - })) - } else { - mediaData = [{ - type: 'image/', - mediaSrc: placeholder, - mediaAltText: "Image not available.", - }] - } + if (ftr.featured_media) { + mediaData = ftr.featured_media.map( mediaItem => ({ + type: mime.lookup(mediaItem), + mediaSrc: mediaItem, + mediaAltText: "Photo of artwork.", + })) + } else { + mediaData = [{ + type: mime.lookup(placeholder), + mediaSrc: placeholder, + mediaAltText: "Image not available.", + }] } - return mediaData; + return mediaData.reverse(); } From ba0bc8c6fa9e92f522454340c37d4ef0e63a178e Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 16:05:22 -0500 Subject: [PATCH 20/27] Final migration to live data for all components, minus organizations data. --- src/lib/components/App.js | 7 ++++++- src/lib/components/FeatureDetail.js | 16 ++++++++-------- src/lib/components/FeatureListItem.js | 4 ++-- src/lib/components/InteractiveMap.js | 6 +++--- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 95a9ca8..ce88cc7 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -31,9 +31,10 @@ const client = new ApolloClient({ const GET_ARTWORKS = gql` query GetArtworks { - artworks(limit: 100, order_by: { uid: asc }) { + artworks(limit: 500, order_by: { uid: asc }) { uid title + description year ward featured_media @@ -41,9 +42,13 @@ const GET_ARTWORKS = gql` program_name } location_details { + address latitude longitude } + artist_details { + preferred_name + } } } `; diff --git a/src/lib/components/FeatureDetail.js b/src/lib/components/FeatureDetail.js index 3891079..92ef7bd 100644 --- a/src/lib/components/FeatureDetail.js +++ b/src/lib/components/FeatureDetail.js @@ -63,21 +63,21 @@ class FeatureDetail extends React.Component {

          - {feature.properties['title']} + {feature.title}

          - {feature.properties['artist']} + {feature.artist_details.preferred_name}

          - {feature.properties['address']} + {feature.location_details.address}

          - {feature.properties['description']} + {feature.description}

          { - feature.properties['organizations'] && + feature.properties &&
          Partner organization
          {feature.properties['organizations']}
          @@ -85,15 +85,15 @@ class FeatureDetail extends React.Component { }
          Ward
          -
          {feature.properties['ward']}
          +
          {feature.ward[0] || ''}
          Program
          -
          {feature.properties['program']}
          +
          {feature.program_details?.program_name}
          Year
          -
          {feature.properties['year']}
          +
          {feature.year}
          diff --git a/src/lib/components/FeatureListItem.js b/src/lib/components/FeatureListItem.js index 8c51732..b3b041e 100644 --- a/src/lib/components/FeatureListItem.js +++ b/src/lib/components/FeatureListItem.js @@ -6,7 +6,7 @@ import * as utils from "../utils"; const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => { - const { uid=0, year, artist='', title, featured_media, address='' } = feature; + const { uid, year, artist='', title, featured_media, location_details } = feature; const handleClick = () => { ReactGA.event({ @@ -59,7 +59,7 @@ const FeatureListItem = ({ feature, onClick, isMobile, activeFeature }) => {

          }

          - {address} + {location_details?.address}

          {year} diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 981b552..7c4bb17 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -48,8 +48,8 @@ class InteractiveMap extends React.Component { if (!this.props.isMobile && this.props.activeFeature && prevProps.activeFeature !== this.props.activeFeature) { const map = this.map if (map != null) { - const coords = this.props.activeFeature.geometry.coordinates - let center = new this.props.google.maps.LatLng(coords[1], coords[0]) + const coords = this.props.activeFeature.location_details + let center = new this.props.google.maps.LatLng(coords.latitude, coords.longitude) map.panTo(center) } } @@ -151,7 +151,7 @@ class InteractiveMap extends React.Component { const feature = _.find(allFeatures, { uid: id }); const validPrograms = ["StART Support", "Partnership Program", "Outside the Box"] const program = validPrograms.includes(feature.program_details?.program_name) ? feature.program_details.program_name : "Other" - const isSelected = activeFeature && feature.uid === activeFeature.properties.uid + const isSelected = activeFeature && feature.uid === activeFeature.uid const icon = { url: constants.ICONS_REG[program].icon, anchor: isSelected ? new google.maps.Point(20, 20) : new google.maps.Point(10, 10), From 6d97eb6d83c30c04f9fc1ad04c15837cb3785526 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 23:36:07 -0500 Subject: [PATCH 21/27] Fix media carousel in detail view. --- src/lib/components/FeatureDetail.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/components/FeatureDetail.js b/src/lib/components/FeatureDetail.js index 92ef7bd..a17693d 100644 --- a/src/lib/components/FeatureDetail.js +++ b/src/lib/components/FeatureDetail.js @@ -32,7 +32,7 @@ class FeatureDetail extends React.Component { getMediaData = (ftr) => { let mediaData = []; - if (ftr.featured_media) { + if (ftr.featured_media.length > 0) { mediaData = ftr.featured_media.map( mediaItem => ({ type: mime.lookup(mediaItem), mediaSrc: mediaItem, @@ -40,7 +40,7 @@ class FeatureDetail extends React.Component { })) } else { mediaData = [{ - type: mime.lookup(placeholder), + type: 'image/jpg', mediaSrc: placeholder, mediaAltText: "Image not available.", }] From 83fa8931def3bbb4f26de357d98f87213d28fe80 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 23:36:54 -0500 Subject: [PATCH 22/27] Resolve edgecase bugs with accessing deep object values. --- src/lib/components/App.js | 4 ++-- src/lib/components/FeatureDetail.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index ce88cc7..23bf5b4 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -235,10 +235,10 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).title?.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).title.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).title?.toLowerCase()) break case 'year-asc': sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).year) diff --git a/src/lib/components/FeatureDetail.js b/src/lib/components/FeatureDetail.js index a17693d..0a2937d 100644 --- a/src/lib/components/FeatureDetail.js +++ b/src/lib/components/FeatureDetail.js @@ -66,10 +66,10 @@ class FeatureDetail extends React.Component { {feature.title}

          - {feature.artist_details.preferred_name} + {feature.artist_details?.preferred_name}

          - {feature.location_details.address} + {feature.location_details?.address}

          {feature.description} From 2221c3964c2f0948654083e27c444fd812fc58a4 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 23:37:16 -0500 Subject: [PATCH 23/27] Added support to showing partner organizations. --- src/lib/components/App.js | 3 +++ src/lib/components/FeatureDetail.js | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 23bf5b4..672142f 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -49,6 +49,9 @@ const GET_ARTWORKS = gql` artist_details { preferred_name } + organization_details { + name + } } } `; diff --git a/src/lib/components/FeatureDetail.js b/src/lib/components/FeatureDetail.js index 0a2937d..9caafaa 100644 --- a/src/lib/components/FeatureDetail.js +++ b/src/lib/components/FeatureDetail.js @@ -77,10 +77,10 @@ class FeatureDetail extends React.Component {

          { - feature.properties && + feature.organization_details &&
          Partner organization
          -
          {feature.properties['organizations']}
          +
          {feature.organization_details.name}
          }
          From 26aeece0ecf1b802505db49060399517b351f501 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 23:37:53 -0500 Subject: [PATCH 24/27] Instead of crash, default to center of map when no latlng exists for artwork. --- src/lib/components/InteractiveMap.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 7c4bb17..c85233c 100644 --- a/src/lib/components/InteractiveMap.js +++ b/src/lib/components/InteractiveMap.js @@ -48,8 +48,11 @@ class InteractiveMap extends React.Component { if (!this.props.isMobile && this.props.activeFeature && prevProps.activeFeature !== this.props.activeFeature) { const map = this.map if (map != null) { - const coords = this.props.activeFeature.location_details - let center = new this.props.google.maps.LatLng(coords.latitude, coords.longitude) + const { + latitude = constants.DEFAULT_MAP_CENTER.lat, + longitude = constants.DEFAULT_MAP_CENTER.lng, + } = this.props.activeFeature.location_details || {} + let center = new this.props.google.maps.LatLng(latitude, longitude) map.panTo(center) } } From 42f8ccbdcacf313643438bc54891c1f25cc7d6b6 Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Wed, 10 Mar 2021 23:53:28 -0500 Subject: [PATCH 25/27] Rename vars and add helpful comments. --- src/lib/components/App.js | 58 +++++++++------------------- src/lib/components/FeatureDetail.js | 1 + src/lib/components/InteractiveMap.js | 2 + 3 files changed, 21 insertions(+), 40 deletions(-) diff --git a/src/lib/components/App.js b/src/lib/components/App.js index 672142f..04a16c6 100644 --- a/src/lib/components/App.js +++ b/src/lib/components/App.js @@ -63,8 +63,6 @@ export default class App extends React.Component { allFeatures: [], /** Array of visible artwork IDs in maps and lists. */ visibleFeatureIds: [], - allFeaturesNew: [], - visibleFeatureIdsNew: [], /** The type of view. * Options: list, detail, map, filter * Last two only display differently on mobile. */ @@ -103,34 +101,16 @@ export default class App extends React.Component { } fetchFeatures() { - const isArtwork = (feature) => ( - feature.geometry && - feature.geometry.type === 'Point' - ) - client.query({ query: GET_ARTWORKS }) .then(result => { this.setState({ - allFeaturesNew: result.data.artworks, - visibleFeatureIdsNew: result.data.artworks.map(f => f.uid), + allFeatures: result.data.artworks, + visibleFeatureIds: result.data.artworks.map(f => f.uid), }, // Sort after first load. () => { this.sortList() } ) }); - - fetch(this.props.featuresDataSource) - .then(response => response.json()) - .then(json => { - const allFeatures = json.features.map(f => { - if (!isArtwork(f)) return null - return f - }).filter(Boolean) - this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.uid) }, - // Sort after first load. - () => { this.sortList() } - ); - }); } resize() { @@ -153,7 +133,7 @@ export default class App extends React.Component { setVisibleFeatureIds = (visibleFeatureIds) => { this.setState( - { visibleFeatureIdsNew: visibleFeatureIds }, + { visibleFeatureIds }, () => { this.sortList() } ); } @@ -173,9 +153,9 @@ export default class App extends React.Component { */ filterFeatures = (activeYearOpts, activeWardOpts, activeProgramOpts) => { const visibleFeatureIds = _.intersection( - _.filter(this.state.allFeaturesNew, f => _.includes(activeYearOpts.map(o => o.value), f.year.toString())).map(f => f.uid), - _.filter(this.state.allFeaturesNew, f => _.includes(activeWardOpts.map(o => o.value), f.ward.toString())).map(f => f.uid), - _.filter(this.state.allFeaturesNew, f => _.includes(activeProgramOpts.map(o => o.value), f.program_details?.program_name.toString())).map(f => f.uid), + _.filter(this.state.allFeatures, f => _.includes(activeYearOpts.map(o => o.value), f.year.toString())).map(f => f.uid), + _.filter(this.state.allFeatures, f => _.includes(activeWardOpts.map(o => o.value), f.ward.toString())).map(f => f.uid), + _.filter(this.state.allFeatures, f => _.includes(activeProgramOpts.map(o => o.value), f.program_details?.program_name.toString())).map(f => f.uid), ) this.setVisibleFeatureIds(visibleFeatureIds); @@ -238,19 +218,19 @@ export default class App extends React.Component { switch(this.state.sortType) { case 'artist-asc': default: - sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).title?.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { uid: id }).title?.toLowerCase()) break case 'artist-desc': - sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).title?.toLowerCase()) + sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { uid: id }).title?.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visibleFeatureIdsNew).asc(id => _.find(this.state.allFeaturesNew, { uid: id }).year) + sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { uid: id }).year) break case 'year-desc': - sortedList = sort(this.state.visibleFeatureIdsNew).desc(id => _.find(this.state.allFeaturesNew, { uid: id }).year) + sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { uid: id }).year) break } - this.setState({ visibleFeatureIdsNew: sortedList }) + this.setState({ visibleFeatureIds: sortedList }) } handleMapClick = (featureId) => { @@ -302,8 +282,6 @@ export default class App extends React.Component { showSplash, allFeatures, visibleFeatureIds, - allFeaturesNew, - visibleFeatureIdsNew, activeFeatureId, isMobileView, isFiltered, @@ -311,7 +289,7 @@ export default class App extends React.Component { showWardLayer, } = this.state; - const activeFeature = _.find(allFeaturesNew, { uid: activeFeatureId }) + const activeFeature = _.find(allFeatures, { uid: activeFeatureId }) return (
          @@ -329,8 +307,8 @@ export default class App extends React.Component { }> @@ -350,8 +328,8 @@ export default class App extends React.Component { /> @@ -363,8 +341,8 @@ export default class App extends React.Component { Date: Thu, 11 Mar 2021 00:17:22 -0500 Subject: [PATCH 26/27] Added hasura config metadata. --- hasura/metadata_export.json | 141 ++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 hasura/metadata_export.json diff --git a/hasura/metadata_export.json b/hasura/metadata_export.json new file mode 100644 index 0000000..add7e86 --- /dev/null +++ b/hasura/metadata_export.json @@ -0,0 +1,141 @@ +{ + "version": 2, + "tables": [ + { + "table": { + "schema": "public", + "name": "artists" + } + }, + { + "table": { + "schema": "public", + "name": "artworks" + }, + "object_relationships": [ + { + "name": "artist_details", + "using": { + "manual_configuration": { + "remote_table": { + "schema": "public", + "name": "artists" + }, + "column_mapping": { + "artist": "id" + } + } + } + }, + { + "name": "location_details", + "using": { + "manual_configuration": { + "remote_table": { + "schema": "public", + "name": "locations" + }, + "column_mapping": { + "location": "id" + } + } + } + }, + { + "name": "organization_details", + "using": { + "manual_configuration": { + "remote_table": { + "schema": "public", + "name": "organizations" + }, + "column_mapping": { + "organizations": "id" + } + } + } + }, + { + "name": "program_details", + "using": { + "manual_configuration": { + "remote_table": { + "schema": "public", + "name": "programs" + }, + "column_mapping": { + "program": "id" + } + } + } + } + ], + "select_permissions": [ + { + "role": "public", + "permission": { + "columns": [ + "description", + "location", + "program_name", + "status" + ], + "filter": {} + } + } + ] + }, + { + "table": { + "schema": "public", + "name": "locations" + }, + "select_permissions": [ + { + "role": "staff", + "permission": { + "columns": [ + "id", + "created_time", + "address", + "area", + "artworks", + "cabinets", + "comments", + "coordinates", + "edit_location", + "geocode_cache", + "height", + "internal_notes", + "intersection", + "last_modified", + "last_modified_by", + "latitude", + "location_id", + "longitude", + "map_url", + "property_description", + "sqft", + "status", + "ward", + "width" + ], + "filter": {} + } + } + ] + }, + { + "table": { + "schema": "public", + "name": "organizations" + } + }, + { + "table": { + "schema": "public", + "name": "programs" + } + } + ] +} From f6261d245ee186c1a9a7bca2596acaee770b743d Mon Sep 17 00:00:00 2001 From: Patrick Connolly Date: Thu, 11 Mar 2021 00:19:56 -0500 Subject: [PATCH 27/27] Updated metadata with very specific permissions. Added envvars. --- hasura/hasura.env | 4 ++ hasura/metadata_export.json | 74 ++++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 30 deletions(-) create mode 100644 hasura/hasura.env diff --git a/hasura/hasura.env b/hasura/hasura.env new file mode 100644 index 0000000..6000555 --- /dev/null +++ b/hasura/hasura.env @@ -0,0 +1,4 @@ +HASURA_GRAPHQL_UNAUTHORIZED_ROLE=public +HASURA_GRAPHQL_CORS_DOMAIN=* +HASURA_GRAPHQL_ADMIN_SECRET= +HASURA_GRAPHQL_DATABASE_URL= diff --git a/hasura/metadata_export.json b/hasura/metadata_export.json index add7e86..8dc1177 100644 --- a/hasura/metadata_export.json +++ b/hasura/metadata_export.json @@ -5,7 +5,18 @@ "table": { "schema": "public", "name": "artists" - } + }, + "select_permissions": [ + { + "role": "public", + "permission": { + "columns": [ + "preferred_name" + ], + "filter": {} + } + } + ] }, { "table": { @@ -76,9 +87,11 @@ "permission": { "columns": [ "description", - "location", - "program_name", - "status" + "featured_media", + "title", + "uid", + "ward", + "year" ], "filter": {} } @@ -92,33 +105,12 @@ }, "select_permissions": [ { - "role": "staff", + "role": "public", "permission": { "columns": [ - "id", - "created_time", "address", - "area", - "artworks", - "cabinets", - "comments", - "coordinates", - "edit_location", - "geocode_cache", - "height", - "internal_notes", - "intersection", - "last_modified", - "last_modified_by", "latitude", - "location_id", - "longitude", - "map_url", - "property_description", - "sqft", - "status", - "ward", - "width" + "longitude" ], "filter": {} } @@ -129,13 +121,35 @@ "table": { "schema": "public", "name": "organizations" - } + }, + "select_permissions": [ + { + "role": "public", + "permission": { + "columns": [ + "name" + ], + "filter": {} + } + } + ] }, { "table": { "schema": "public", "name": "programs" - } + }, + "select_permissions": [ + { + "role": "public", + "permission": { + "columns": [ + "program_name" + ], + "filter": {} + } + } + ] } ] -} +} \ No newline at end of file