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 63adb0f..b792006 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"; @@ -25,15 +26,16 @@ 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: [], - visFtrs: [], + /** 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. */ viewType: "map", - /** Full object representing active artwork. */ - activeFeature: 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. */ @@ -74,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, visFtrs }, + this.setState({ allFeatures, visibleFeatureIds: allFeatures.map(f => f.id) }, // Sort after first load. () => { this.sortList() } ); @@ -103,9 +105,9 @@ export default class App extends React.Component { }) } - setVisibleFeatures = (visFtrs) => { + setVisibleFeatureIds = (visibleFeatureIds) => { this.setState( - {visFtrs: visFtrs}, + { visibleFeatureIds }, () => { this.sortList() } ); } @@ -152,7 +154,7 @@ export default class App extends React.Component { return keepForYear && keepForWard && keepForProgram; }) - this.setVisibleFeatures(visibleFeatures); + this.setVisibleFeatureIds(visibleFeatures.map(f => f.id)); } handleSelectYears = (selectedOptions) => { @@ -212,47 +214,47 @@ 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.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).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.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.title.toLowerCase()) break case 'year-asc': - sortedList = sort(this.state.visFtrs).asc(u => u.properties.year) + sortedList = sort(this.state.visibleFeatureIds).asc(id => _.find(this.state.allFeatures, { id }).properties?.year) break case 'year-desc': - sortedList = sort(this.state.visFtrs).desc(u => u.properties.year) + sortedList = sort(this.state.visibleFeatureIds).desc(id => _.find(this.state.allFeatures, { id }).properties?.year) break } - this.setState({visFtrs: sortedList}) + this.setState({ visibleFeatureIds: sortedList }) } - handleMapClick = (feature) => { + handleMapClick = (featureId) => { ReactGA.event({ category: 'Map', action: 'Clicked feature', label: 'ward or artwork', }) - this.setActiveFeature(feature) + this.setActiveFeatureId(featureId) } - setActiveFeature = (feature) => { + setActiveFeatureId = (featureId) => { this.setState({ - activeFeature: feature, + activeFeatureId: featureId, }); } handleCloseFeature = () => { - const uid = this.state.activeFeature.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({ - activeFeature: null + activeFeatureId: null }) } @@ -275,14 +277,17 @@ export default class App extends React.Component { render() { const { showSplash, - visFtrs, - activeFeature, + allFeatures, + visibleFeatureIds, + activeFeatureId, isMobileView, isFiltered, viewType, showWardLayer, } = this.state; + const activeFeature = _.find(allFeatures, { id: activeFeatureId }) + return (
@@ -299,8 +304,9 @@ export default class App extends React.Component { }> @@ -319,8 +325,9 @@ export default class App extends React.Component { /> @@ -331,7 +338,8 @@ export default class App extends React.Component { { +const FeatureList = ({ allFeatures = [], featureIds = [], onItemClick, isMobile, activeFeature }) => { useEffect(() => { forceCheck() }); return (
-

{features.length} Results

+

{featureIds.length} Results

    - {features.map(feature => - { + const feature = _.find(allFeatures, { id }) + return - )} + })}
); @@ -29,8 +31,8 @@ const FeatureList = ({ features, onItemClick, isMobile, activeFeature }) => { FeatureListItem.propTypes = { - features: PropTypes.arrayOf(PropTypes.object), + featureIds: PropTypes.arrayOf(PropTypes.number), onItemClick: PropTypes.func, } -export default FeatureList \ No newline at end of file +export default FeatureList diff --git a/src/lib/components/FeatureListItem.js b/src/lib/components/FeatureListItem.js index baca9a0..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) + onClick(feature.id) } const handleKeyPress = (event) => { @@ -84,4 +84,4 @@ FeatureListItem.defaultProps = { media: [], } -export default FeatureListItem \ No newline at end of file +export default FeatureListItem diff --git a/src/lib/components/InteractiveMap.js b/src/lib/components/InteractiveMap.js index 7022765..8762105 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"; @@ -17,8 +18,8 @@ class InteractiveMap extends React.Component { constructor(props) { super(props) this.state = { - prevActiveFeature: {}, - features: this.props.features, + allFeatures: this.props.allFeatures, + visibleFeatureIds: this.props.visibleFeatureIds, wards: {}, } this.mapRef = createRef() @@ -36,8 +37,8 @@ class InteractiveMap extends React.Component { } componentDidUpdate(prevProps) { - if (prevProps.features !== this.props.features) { - this.setState({ features: this.props.features }) + if (prevProps.visibleFeatureIds !== this.props.visibleFeatureIds) { + this.setState({ visibleFeatureIds: this.props.visibleFeatureIds }) } if (prevProps.showWardLayer !== this.props.showWardLayer) { @@ -125,7 +126,7 @@ class InteractiveMap extends React.Component { render() { const { loaded, google, activeFeature, onFeatureMapClick } = this.props; - const { features } = this.state; + const { allFeatures, 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; @@ -146,7 +147,10 @@ class InteractiveMap extends React.Component { > { - features.map((feature, i) => { + 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"] const program = validPrograms.includes(feature.properties.program) ? feature.properties.program : "Other" const isSelected = activeFeature && feature.properties.uid === activeFeature.properties.uid @@ -162,7 +166,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.id) } zIndex={isSelected ? 2 : 1} /> ) 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} /> )