From 9f8904d020fdc4cb5fa3d7f27a4a1e981c60624f Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Thu, 19 Sep 2019 17:16:39 -0700 Subject: [PATCH 1/8] what does loading all events look like --- src/App.js | 9 ++++----- src/Map.js | 27 +++++++++++++-------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/App.js b/src/App.js index f2fb760..113d33c 100644 --- a/src/App.js +++ b/src/App.js @@ -15,17 +15,16 @@ function App() { //Makes API call when zipcode entered useEffect(() => { - if(currZip != null){ - fetch("https://api.mobilize.us/v1/organizations/1316/events?timeslot_start=gte_now&zipcode=" + currZip) + + fetch("https://gist.githubusercontent.com/mick/6c85985bbaee7419b6351501edd05de0/raw/f41482f485d3390516c390180c841c25b4213987/events.json") .then((res)=>res.json()) - .then((data)=>setEvents(data['data'])); + .then((data)=>setEvents(data)); //Reset states on new zipcode setHoverEvent(null); setLocFilt(null); - } - }, [currZip]); + }, []); return (
diff --git a/src/Map.js b/src/Map.js index 7d4e20a..80427d0 100644 --- a/src/Map.js +++ b/src/Map.js @@ -14,7 +14,6 @@ export function Map(props){ //Called to set/unset location filter function locationFilter(event, set){ - console.log(event) if(set){ props.selectLoc({ @@ -109,27 +108,27 @@ export function Map(props){ if(props.events != null){ //Initiates map's focus at the first event (typically the closest to the provided zipcode) with a valid lat & long position - let first = 0; - if (!('location' in props.events[first]) || !('location' in props.events[first]['location']) || !('latitude' in props.events[first]['location']['location'])) { - first++; - } + // let first = 0; + // if (!('location' in props.events[first]) || !('location' in props.events[first]['location']) || !('latitude' in props.events[first]['location']['location'])) { + // first++; + // } - var lat = props.events[first]['location']['location']['latitude']; - var long = props.events[first]['location']['location']['longitude']; + // var lat = props.events[first]['location']['location']['latitude']; + // var long = props.events[first]['location']['location']['longitude']; - if(center[0] !== lat || center[0] !== long){ - setCenter([lat, long]); - setNewCenter(true); - } + // if(center[0] !== lat || center[0] !== long){ + // setCenter([lat, long]); + // setNewCenter(true); + // } var places = {}; props.events.forEach(function(event, index) { - + if (index > 500) return; //If has longitude and latitute if ('location' in event && 'location' in event['location'] && 'latitude' in event['location']['location']) { - //Creates string key for {places} dictionary + //Creates string key for {places} dictionary let str = event['location']['location']['latitude'] + "&" + event['location']['location']['longitude']; //Creates or adds to a location - adds HTML code for event list for that location if (str in places) { @@ -139,7 +138,7 @@ export function Map(props){ } } - }); + }); setLocations(places); } From ec88428d05302bad824c616595492c56d8e8d129 Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Sat, 28 Sep 2019 13:38:13 -0700 Subject: [PATCH 2/8] use mapbox-js allowing fast rendering of all events. --- package-lock.json | 253 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 +- src/App.js | 6 +- src/App.scss | 8 ++ src/EventList.js | 33 +++--- src/Map.js | 193 +++++++++++++++++------------------ src/SearchBar.js | 12 ++- 7 files changed, 384 insertions(+), 123 deletions(-) diff --git a/package-lock.json b/package-lock.json index a728cca..8c6cbf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1310,6 +1310,68 @@ "@types/yargs": "^13.0.0" } }, + "@mapbox/geojson-area": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-area/-/geojson-area-0.2.2.tgz", + "integrity": "sha1-GNeBSqNr8j+7zDefjiaiKSfevxA=", + "requires": { + "wgs84": "0.0.0" + } + }, + "@mapbox/geojson-rewind": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.4.0.tgz", + "integrity": "sha512-b+1uPWBERW4Pet/969BNu61ZPDyH2ilIxBjJDFzxyS9TyszF9UrTQyYIl/G38clux3rtpAGGFSGTCSF/qR6UjA==", + "requires": { + "@mapbox/geojson-area": "0.2.2", + "concat-stream": "~1.6.0", + "minimist": "1.2.0", + "sharkdown": "^0.1.0" + } + }, + "@mapbox/geojson-types": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/geojson-types/-/geojson-types-1.0.2.tgz", + "integrity": "sha512-e9EBqHHv3EORHrSfbR9DqecPNn+AmuAoQxV6aL8Xu30bJMJR1o8PZLZzpk1Wq7/NfCbuhmakHTPYRhoqLsXRnw==" + }, + "@mapbox/jsonlint-lines-primitives": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", + "integrity": "sha1-zlblOfg1UrWNENZy6k1vya3HsjQ=" + }, + "@mapbox/mapbox-gl-supported": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@mapbox/mapbox-gl-supported/-/mapbox-gl-supported-1.4.1.tgz", + "integrity": "sha512-yyKza9S6z3ELKuf6w5n6VNUB0Osu6Z93RXPfMHLIlNWohu3KqxewLOq4lMXseYJ92GwkRAxd207Pr/Z98cwmvw==" + }, + "@mapbox/point-geometry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", + "integrity": "sha1-ioP5M1x4YO/6Lu7KJUMyqgru2PI=" + }, + "@mapbox/tiny-sdf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-1.1.1.tgz", + "integrity": "sha512-Ihn1nZcGIswJ5XGbgFAvVumOgWpvIjBX9jiRlIl46uQG9vJOF51ViBYHF95rEZupuyQbEmhLaDPLQlU7fUTsBg==" + }, + "@mapbox/unitbezier": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-0.0.0.tgz", + "integrity": "sha1-FWUb1VOme4WB+zmIEMmK2Go0Uk4=" + }, + "@mapbox/vector-tile": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", + "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "requires": { + "@mapbox/point-geometry": "~0.1.0" + } + }, + "@mapbox/whoots-js": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz", + "integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==" + }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", @@ -1892,6 +1954,11 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, + "ansicolors": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.2.1.tgz", + "integrity": "sha1-vgiVmQl7dKXJxKhKDNvNtivYeu8=" + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -2874,6 +2941,15 @@ "rsvp": "^4.8.4" } }, + "cardinal": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-0.4.4.tgz", + "integrity": "sha1-ylu2iltRG5D+k7ms6km97lwyv+I=", + "requires": { + "ansicolors": "~0.2.1", + "redeyed": "~0.4.0" + } + }, "case-sensitive-paths-webpack-plugin": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.2.0.tgz", @@ -4039,6 +4115,11 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" }, + "csscolorparser": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/csscolorparser/-/csscolorparser-1.0.3.tgz", + "integrity": "sha1-s085HupNqPPpgjHizNjfnAQfFxs=" + }, "cssdb": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", @@ -4583,6 +4664,11 @@ "stream-shift": "^1.0.0" } }, + "earcut": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.1.tgz", + "integrity": "sha512-5jIMi2RB3HtGPHcYd9Yyl0cczo84y+48lgKPxMijliNQaKAHEZJbdzLmKmdxG/mCdS/YD9DQ1gihL8mxzR0F9w==" + }, "ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", @@ -5990,6 +6076,11 @@ "globule": "^1.0.0" } }, + "geojson-vt": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz", + "integrity": "sha512-EvGQQi/zPrDA6zr6BnJD/YhwAkBP8nnJ9emh3EnHQKVMfg/MRVtPbMYdgVy/IaEmn4UfagD2a6fafPDL5hbtwg==" + }, "get-caller-file": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", @@ -6066,6 +6157,11 @@ } } }, + "gl-matrix": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.1.0.tgz", + "integrity": "sha512-526NA+3EA+ztAQi0IZpSWiM0fyQXIp7IbRvfJ4wS/TjjQD0uv0fVybXwwqqSOlq33UckivI0yMDlVtboWm3k7A==" + }, "glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", @@ -6168,6 +6264,11 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" }, + "grid-index": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grid-index/-/grid-index-1.1.0.tgz", + "integrity": "sha512-HZRwumpOGUrHyxO5bqKZL0B0GlUpwtCAzZ42sgxUPniu33R1LSFH5yrIcBCHjkctCAh3mtWKcKd9J4vDDdeVHA==" + }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -8969,6 +9070,11 @@ "object.assign": "^4.1.0" } }, + "kdbush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-3.0.0.tgz", + "integrity": "sha512-hRkd6/XW4HTsA9vjVpY9tuXJYLSlelnkTmVFu4M9/7MIYQtFcHpbugAU7UbOfjOiVSVYl2fqgBuJ32JUmRo5Ew==" + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -9258,6 +9364,43 @@ "object-visit": "^1.0.0" } }, + "mapbox-gl": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.3.1.tgz", + "integrity": "sha512-IF7b0LZd/caTiknPhm8DAcv7bhvOCXO6rsW18rmFxi8Vw0syJXKK8DLLabI5oiJXtUIgLe57XRgduQzAYrb4og==", + "requires": { + "@mapbox/geojson-rewind": "^0.4.0", + "@mapbox/geojson-types": "^1.0.2", + "@mapbox/jsonlint-lines-primitives": "^2.0.2", + "@mapbox/mapbox-gl-supported": "^1.4.0", + "@mapbox/point-geometry": "^0.1.0", + "@mapbox/tiny-sdf": "^1.1.0", + "@mapbox/unitbezier": "^0.0.0", + "@mapbox/vector-tile": "^1.3.1", + "@mapbox/whoots-js": "^3.1.0", + "csscolorparser": "~1.0.2", + "earcut": "^2.1.5", + "geojson-vt": "^3.2.1", + "gl-matrix": "^3.0.0", + "grid-index": "^1.1.0", + "minimist": "0.0.8", + "murmurhash-js": "^1.0.0", + "pbf": "^3.0.5", + "potpack": "^1.0.1", + "quickselect": "^2.0.0", + "rw": "^1.3.3", + "supercluster": "^6.0.1", + "tinyqueue": "^2.0.0", + "vt-pbf": "^3.1.1" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + } + } + }, "md5.js": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", @@ -9552,6 +9695,11 @@ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" }, + "murmurhash-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/murmurhash-js/-/murmurhash-js-1.0.0.tgz", + "integrity": "sha1-sGJ44h/Gw3+lMTcysEEry2rhX1E=" + }, "mute-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", @@ -10267,6 +10415,15 @@ "pinkie-promise": "^2.0.0" } }, + "pbf": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.2.0.tgz", + "integrity": "sha512-98Eh7rsJNJF/Im6XYMLaOW3cLnNyedlOd6hu3iWMD5I7FZGgpw8yN3vQBrmLbLodu7G784Irb9Qsv2yFrxSAGw==", + "requires": { + "ieee754": "^1.1.12", + "resolve-protobuf-schema": "^2.1.0" + } + }, "pbkdf2": { "version": "3.0.17", "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", @@ -11274,6 +11431,11 @@ "uniq": "^1.0.1" } }, + "potpack": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/potpack/-/potpack-1.0.1.tgz", + "integrity": "sha512-15vItUAbViaYrmaB/Pbw7z6qX2xENbFSTA7Ii4tgbPtasxm5v6ryKhKtL91tpWovDJzTiZqdwzhcFBCwiMVdVw==" + }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -11377,6 +11539,11 @@ "react-is": "^16.8.1" } }, + "protocol-buffers-schema": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/protocol-buffers-schema/-/protocol-buffers-schema-3.3.2.tgz", + "integrity": "sha512-Xdayp8sB/mU+sUV4G7ws8xtYMGdQnxbeIfLjyO9TZZRJdztBGhlmbI5x1qcY4TG5hBkIKGnc28i7nXxaugu88w==" + }, "proxy-addr": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", @@ -11484,6 +11651,11 @@ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" }, + "quickselect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", + "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==" + }, "raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -11871,6 +12043,21 @@ "strip-indent": "^1.0.1" } }, + "redeyed": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/redeyed/-/redeyed-0.4.4.tgz", + "integrity": "sha1-N+mQpvKyGyoRwuakj9QTVpjLqX8=", + "requires": { + "esprima": "~1.0.4" + }, + "dependencies": { + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" + } + } + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -12105,6 +12292,14 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" }, + "resolve-protobuf-schema": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/resolve-protobuf-schema/-/resolve-protobuf-schema-2.1.0.tgz", + "integrity": "sha512-kI5ffTiZWmJaS/huM8wZfEMer1eRd7oJQhDuxeCLe3t7N7mX3z94CN0xPxBQxFYQTSNz9T0i+v6inKqSdK8xrQ==", + "requires": { + "protocol-buffers-schema": "^3.3.1" + } + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -12268,6 +12463,11 @@ "aproba": "^1.1.1" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, "rxjs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", @@ -12606,6 +12806,23 @@ } } }, + "sharkdown": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/sharkdown/-/sharkdown-0.1.1.tgz", + "integrity": "sha512-exwooSpmo5s45lrexgz6Q0rFQM574wYIX3iDZ7RLLqOb7IAoQZu9nxlZODU972g19sR69OIpKP2cpHTzU+PHIg==", + "requires": { + "cardinal": "~0.4.2", + "minimist": "0.0.5", + "split": "~0.2.10" + }, + "dependencies": { + "minimist": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=" + } + } + }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -12962,6 +13179,14 @@ } } }, + "split": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/split/-/split-0.2.10.tgz", + "integrity": "sha1-Zwl8YB1pfOE2j0GPBs0gHPBSGlc=", + "requires": { + "through": "2" + } + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -13239,6 +13464,14 @@ } } }, + "supercluster": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/supercluster/-/supercluster-6.0.2.tgz", + "integrity": "sha512-aa0v2HURjBTOpbcknilcfxGDuArM8khklKSmZ/T8ZXL0BuRwb5aRw95lz+2bmWpFvCXDX/+FzqHxmg0TIaJErw==", + "requires": { + "kdbush": "^3.0.0" + } + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -13523,6 +13756,11 @@ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=" }, + "tinyqueue": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tinyqueue/-/tinyqueue-2.0.3.tgz", + "integrity": "sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA==" + }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -13988,6 +14226,16 @@ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.0.tgz", "integrity": "sha512-iq+S7vZJE60yejDYM0ek6zg308+UZsdtPExWP9VZoCFCz1zkJoXFnAX7aZfd/ZwrkidzdUZL0C/ryW+JwAiIGw==" }, + "vt-pbf": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.1.tgz", + "integrity": "sha512-pHjWdrIoxurpmTcbfBWXaPwSmtPAHS105253P1qyEfSTV2HJddqjM+kIHquaT/L6lVJIk9ltTGc0IxR/G47hYA==", + "requires": { + "@mapbox/point-geometry": "0.1.0", + "@mapbox/vector-tile": "^1.3.1", + "pbf": "^3.0.5" + } + }, "w3c-hr-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", @@ -14307,6 +14555,11 @@ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.3.tgz", "integrity": "sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==" }, + "wgs84": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/wgs84/-/wgs84-0.0.0.tgz", + "integrity": "sha1-NP3FVZF7blfPKigu0ENxDASc3HY=" + }, "whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", diff --git a/package.json b/package.json index 9f58acf..bf76120 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "private": true, "dependencies": { "@types/lodash.sortby": "^4.7.6", - "leaflet": "^1.5.1", "lodash.groupby": "^4.6.0", "lodash.sortby": "^4.7.0", + "mapbox-gl": "^1.3.1", "moment": "^2.24.0", "node-sass": "^4.12.0", "react": "^16.9.0", diff --git a/src/App.js b/src/App.js index 113d33c..b9981a7 100644 --- a/src/App.js +++ b/src/App.js @@ -5,7 +5,7 @@ import './App.scss'; function App() { //List of events - const [events, setEvents] = useState(null); + const [events, setEvents] = useState([]); //Current zip code search - input by user const [currZip, setCurrZip] = useState(null); //Current event being hovered over @@ -13,6 +13,8 @@ function App() { //Current selected location location filter const [locFilt, setLocFilt] = useState(null); + const [nearby, setNearby] = useState(null); + //Makes API call when zipcode entered useEffect(() => { @@ -28,7 +30,7 @@ function App() { return (
- setCurrZip(newZip)} events={events} updatedHover={(newHover) => setHoverEvent(newHover)} locFilt={locFilt}/> + setCurrZip(newZip)} events={events} updatedHover={(newHover) => setHoverEvent(newHover)} locFilt={locFilt}/> setLocFilt(newLoc)} locFilt={locFilt}/>
); diff --git a/src/App.scss b/src/App.scss index 0e71153..1db3117 100644 --- a/src/App.scss +++ b/src/App.scss @@ -115,4 +115,12 @@ input[type="number"]::-webkit-inner-spin-button { } input[type="number"] { -moz-appearance: textfield; +} + +.marker { + background-image: url('img/w-marker-icon-2x.png'); + background-size: cover; + width: 25px; + height: 41px; + cursor: pointer; } \ No newline at end of file diff --git a/src/EventList.js b/src/EventList.js index 2ccc0cd..847d045 100644 --- a/src/EventList.js +++ b/src/EventList.js @@ -21,7 +21,7 @@ function EventTimes(props) { ) let sortedDates = Object.keys(sortedTimesByDate).sort(); - + const dateRowFactory = (date) => { let times = sortedTimesByDate[date]; let dayStr = times[0].start.format('ddd M/D') @@ -49,7 +49,12 @@ function EventTimes(props) { } export function EventList(props) { - const listEvents = props.events.map((event) => { + if (!props.locFilt && !props.nearBy) { + + } + + + const listEvents = props.events.map((event, i) => { // Normalize Mobilize's time formatting into // easy-to-use moments @@ -63,23 +68,19 @@ export function EventList(props) { }) //Location filter - if(props.locFilt != null){ - if('location' in event && 'location' in event['location'] && 'latitude' in event['location']['location']){ - if(event['location']['location']['latitude'] !== props.locFilt['lat'] || event['location']['location']['longitude'] != props.locFilt['lng']){ - return(null); - } - } else { - return(null); - } + var locKey = event['location']['location']['longitude'] + '&' + event['location']['location']['latitude']; + + if(props.locFilt && locKey !== props.locFilt) { + return(null); } return ( - { props.updatedHover(event['currentTarget'].getAttribute('coord')) }} + { props.updatedHover(event['currentTarget'].getAttribute('coord')) }} onMouseLeave={(event) => { props.updatedHover(null) }}>
  • diff --git a/src/Map.js b/src/Map.js index 80427d0..8c7f70d 100644 --- a/src/Map.js +++ b/src/Map.js @@ -1,14 +1,16 @@ import React, { useState, useEffect, useRef } from 'react'; -import L from 'leaflet'; +import mapboxgl from 'mapbox-gl'; import gMark from './img/w-marker-icon-2x.png'; import hMark from './img/w-marker-icon-2x-highlighted.png'; import sMark from './img/marker-shadow.png'; +require('mapbox-gl/dist/mapbox-gl.css'); export function Map(props){ - const [center, setCenter] = useState([39.8283, -98.5795]); - const [locations, setLocations] = useState({}); + const [center, setCenter] = useState([-98.5795, 39.8283]); + const [locations, setLocations] = useState([]); const [newCenter, setNewCenter] = useState(false); + const [mapReady, setMapReady] = useState(false); const map = useRef(); const markers = useRef(); @@ -28,120 +30,111 @@ export function Map(props){ //First render useEffect(() => { // Create the map with US center - map.current = L.map('map', { - zoomControl: false - }).setView(center, (props.events != null) ? 8 : 4); - - //Initializes layergroup - markers.current = L.featureGroup().addTo(map.current); - markers.current.on("click", (event) => locationFilter(event, true)); - map.current.on("click", (event) => locationFilter(event, false)); - - - // Set up the OSM layer - L.tileLayer( - 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { - attribution: 'Map data © OpenStreetMap contributors', - maxZoom: 18 - }).addTo(map.current); - - L.control.zoom({ - position: 'topright' - }).addTo(map.current); - - }, []); - - - //When locations are updated, generate new markers - useEffect(() => { - - if(Object.keys(locations).length > 0){ - markers.current.clearLayers(); - - if(newCenter){ - map.current.setView(center, 8); - setNewCenter(false); - } + mapboxgl.accessToken = 'pk.eyJ1IjoibWlja3QiLCJhIjoiLXJIRS1NbyJ9.EfVT76g4A5dyuApW_zuIFQ'; + map.current = new mapboxgl.Map({ + container: 'map', + style: 'mapbox://styles/mickt/ck0rlk9834i721clibn70ajsa', + zoom: 3, + hash: true, + center: center + }); + map.current.on('load', _ => { + + map.current.addSource('locations', { + "type": "geojson", + "data": {type: 'FeatureCollection', features: []} + }); - var generalIcon = new L.Icon({ - iconUrl: gMark, - shadowUrl: sMark, - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], + map.current.addLayer({ + "id": "event-locations", + "source": "locations", + "type": "symbol", + "layout": { + "icon-allow-overlap": true, + "icon-image": "w-marker-icon", + "icon-anchor": "bottom", + "icon-size": 0.5 + }, + "filter": ["!=", "highlight", true] }); - var highlightedIcon = new L.Icon({ - iconUrl: hMark, - shadowUrl: sMark, - iconSize: [25, 41], - iconAnchor: [12, 41], - popupAnchor: [1, -34], - shadowSize: [41, 41], + map.current.addLayer({ + "id": "event-locations-highlight", + "source": "locations", + "type": "symbol", + "layout": { + "icon-allow-overlap": true, + "icon-image": "w-marker-icon-highlighted", + "icon-anchor": "bottom", + "icon-size": 0.6 + }, + "filter": ["==", "highlight", true] }); - for (var key in locations) { - let highlighted = false; - - if(key === props.hoverMarker || (props.locFilt !== null && key === props.locFilt['lat'] + "&" + props.locFilt['lng'])){ - console.log("matching"); - highlighted = true; + // Center the map on the coordinates of any clicked symbol from the 'symbols' layer. + map.current.on('click', 'event-locations', function (e) { + console.log(e.features) + if (e.features && e.features.length > 0) { + props.selectLoc(e.features[0].properties.locKey); } + map.current.flyTo({center: e.features[0].geometry.coordinates}); + }); - let cord = key.split("&"); + // Change the cursor to a pointer when the it enters a feature in the 'symbols' layer. + map.current.on('mouseenter', 'event-locations', function () { + map.current.getCanvas().style.cursor = 'pointer'; + }); - if(highlighted){ - L.marker([parseFloat(cord[0]), parseFloat(cord[1])], {icon: highlightedIcon, zIndexOffset: 1000}).addTo(markers.current); - } else { - L.marker([parseFloat(cord[0]), parseFloat(cord[1])], {icon: generalIcon}).addTo(markers.current); - } + // Change it back to a pointer when it leaves. + map.current.on('mouseleave', 'event-locations', function () { + map.current.getCanvas().style.cursor = ''; + }); + setMapReady(true) + }) + }, []); - } - } - }, [locations, props.hoverMarker, props.locFilt]); - //Iterates through new events useEffect(() => { - if(props.events != null){ - - //Initiates map's focus at the first event (typically the closest to the provided zipcode) with a valid lat & long position - // let first = 0; - // if (!('location' in props.events[first]) || !('location' in props.events[first]['location']) || !('latitude' in props.events[first]['location']['location'])) { - // first++; - // } - - // var lat = props.events[first]['location']['location']['latitude']; - // var long = props.events[first]['location']['location']['longitude']; - - // if(center[0] !== lat || center[0] !== long){ - // setCenter([lat, long]); - // setNewCenter(true); - // } + console.log(props.hoverMarker, props.locFilt) + var key = props.hoverMarker || props.locFilt + var newlocs = locations.map(l => { + l.properties.highlight = (l.properties.locKey === key) + return l; + }) + setLocations(newlocs); - var places = {}; + }, [props.hoverMarker, props.locFilt]) - props.events.forEach(function(event, index) { - if (index > 500) return; - //If has longitude and latitute - if ('location' in event && 'location' in event['location'] && 'latitude' in event['location']['location']) { - - //Creates string key for {places} dictionary - let str = event['location']['location']['latitude'] + "&" + event['location']['location']['longitude']; - //Creates or adds to a location - adds HTML code for event list for that location - if (str in places) { - places[str] = places[str] + 1; - } else { - places[str] = 1; - } + useEffect(() => { + if (mapReady === false) return; - } - }); - setLocations(places); - } + var geojson = {type: 'FeatureCollection', features: locations}; + map.current.getSource('locations').setData(geojson); + }, [locations, mapReady]) + //Iterates through new events + useEffect(() => { + if (props.events === null) return; + + var places = props.events.map(e => { + return { + type: 'Feature', + properties:{ + "highlight": false, + "locKey": e.location.location.longitude + '&' + e.location.location.latitude + }, + geometry: { + type: 'Point', + coordinates: [ + parseFloat(e.location.location.longitude), + parseFloat(e.location.location.latitude) + ] + } + } + }) + setLocations(places); }, [props.events]); diff --git a/src/SearchBar.js b/src/SearchBar.js index dafd20f..06c359c 100644 --- a/src/SearchBar.js +++ b/src/SearchBar.js @@ -17,14 +17,18 @@ export function SearchBar(props){ props.updateZip(input) } + var eventlist = []; + console.log(props) + if (props.locFilt !== null || props.nearby !== null) { + eventlist = ( props.updatedHover(item)}/>) + } + return( -
    +
    - {props.events !== null && - props.updatedHover(item)}/> - } + { eventlist }
    ); } From b223226e82fc5f1ea7f76161d7f50341222f25fb Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Mon, 30 Sep 2019 13:15:16 -0700 Subject: [PATCH 3/8] switch to use events.json from s3 --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index b9981a7..91855f0 100644 --- a/src/App.js +++ b/src/App.js @@ -18,7 +18,7 @@ function App() { //Makes API call when zipcode entered useEffect(() => { - fetch("https://gist.githubusercontent.com/mick/6c85985bbaee7419b6351501edd05de0/raw/f41482f485d3390516c390180c841c25b4213987/events.json") + fetch("https://warren-events.s3.amazonaws.com/data/events.json") .then((res)=>res.json()) .then((data)=>setEvents(data)); From a56f417a19960287f14918dfa3a8a08f399ad4df Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Thu, 3 Oct 2019 18:11:29 -0700 Subject: [PATCH 4/8] show event list based on what is visible on the map, fix zipcode search, add link to virtual events and creating new events --- src/App.js | 36 +++++++++++++++++++++----------- src/App.scss | 7 ++----- src/EventList.js | 24 +++++++++++---------- src/Map.js | 54 +++++++++++++++++++++++------------------------- src/SearchBar.js | 4 +--- 5 files changed, 66 insertions(+), 59 deletions(-) diff --git a/src/App.js b/src/App.js index 91855f0..111afce 100644 --- a/src/App.js +++ b/src/App.js @@ -10,28 +10,40 @@ function App() { const [currZip, setCurrZip] = useState(null); //Current event being hovered over const [hoverEvent, setHoverEvent] = useState(null); - //Current selected location location filter + //Current selected location filter const [locFilt, setLocFilt] = useState(null); + //Events that are within the map viewport. These should be shown in the list + const [inViewEvents, setInViewEvents] = useState([]); - const [nearby, setNearby] = useState(null); - - //Makes API call when zipcode entered + // Load all of the events useEffect(() => { - fetch("https://warren-events.s3.amazonaws.com/data/events.json") .then((res)=>res.json()) .then((data)=>setEvents(data)); - - //Reset states on new zipcode - setHoverEvent(null); - setLocFilt(null); - }, []); + useEffect(() => { + // Use the mobilizemaerica api to look up zipcode to nearest event. + if(currZip == null) return; + fetch("https://api.mobilize.us/v1/organizations/1316/events?timeslot_start=gte_now&zipcode=" + currZip) + .then((res)=>res.json()) + .then(res => { + if (res.data && res.data.length > 0){ + let event = res.data[0] + setLocFilt(event.location.location.longitude +'&'+ event.location.location.latitude) + } + + }) + + // Reset states on new zipcode + setHoverEvent(null); + setLocFilt(null); + }, [currZip]) + return (
    - setCurrZip(newZip)} events={events} updatedHover={(newHover) => setHoverEvent(newHover)} locFilt={locFilt}/> - setLocFilt(newLoc)} locFilt={locFilt}/> + setCurrZip(newZip)} events={events} inViewEvents={inViewEvents} updatedHover={(newHover) => setHoverEvent(newHover)} locFilt={locFilt}/> + setLocFilt(newLoc)} locFilt={locFilt} inViewEvents={(keys) => setInViewEvents(keys)}/>
    ); } diff --git a/src/App.scss b/src/App.scss index 1db3117..8544681 100644 --- a/src/App.scss +++ b/src/App.scss @@ -76,10 +76,6 @@ html, body, #root, .app { text-transform: uppercase; } - :hover{ - color: white; - } - li{ list-style-type: none; padding-left: 10%; @@ -97,8 +93,9 @@ html, body, #root, .app { } - li:hover{ + li.event:hover{ background-color: #232444; + color: white; .eventRSVP{ visibility: visible; diff --git a/src/EventList.js b/src/EventList.js index 847d045..e030cf7 100644 --- a/src/EventList.js +++ b/src/EventList.js @@ -52,9 +52,13 @@ export function EventList(props) { if (!props.locFilt && !props.nearBy) { } + // Filter based on the events that are currently in view. + var inViewEvents = props.events.filter(event => { + var locKey = event['location']['location']['longitude'] + '&' + event['location']['location']['latitude']; + return props.inViewEvents.indexOf(locKey) >= 0 + }) - - const listEvents = props.events.map((event, i) => { + const listEvents = inViewEvents.map((event, i) => { // Normalize Mobilize's time formatting into // easy-to-use moments @@ -67,13 +71,6 @@ export function EventList(props) { } }) - //Location filter - var locKey = event['location']['location']['longitude'] + '&' + event['location']['location']['latitude']; - - if(props.locFilt && locKey !== props.locFilt) { - return(null); - } - return ( { props.updatedHover(event['currentTarget'].getAttribute('coord')) }} onMouseLeave={(event) => { props.updatedHover(null) }}> -
  • +
  • {event['title']}

    {event['location']['venue']} in {event['location']['locality']}

    @@ -91,10 +88,15 @@ export function EventList(props) {
  • - ) }); + listEvents.push((
  • +
    +

    Don't see an event near you?
    Join a virtual event or host your own event!

    +
    +
  • )) + return (
      {listEvents}
    ); diff --git a/src/Map.js b/src/Map.js index 8c7f70d..e6d9cc6 100644 --- a/src/Map.js +++ b/src/Map.js @@ -1,31 +1,12 @@ import React, { useState, useEffect, useRef } from 'react'; import mapboxgl from 'mapbox-gl'; -import gMark from './img/w-marker-icon-2x.png'; -import hMark from './img/w-marker-icon-2x-highlighted.png'; -import sMark from './img/marker-shadow.png'; require('mapbox-gl/dist/mapbox-gl.css'); export function Map(props){ - const [center, setCenter] = useState([-98.5795, 39.8283]); const [locations, setLocations] = useState([]); - const [newCenter, setNewCenter] = useState(false); const [mapReady, setMapReady] = useState(false); const map = useRef(); - const markers = useRef(); - - //Called to set/unset location filter - function locationFilter(event, set){ - - if(set){ - props.selectLoc({ - 'lat': event['latlng']['lat'], - 'lng': event['latlng']['lng'] - }); - } else { - props.selectLoc(null); - } - } //First render useEffect(() => { @@ -36,7 +17,7 @@ export function Map(props){ style: 'mapbox://styles/mickt/ck0rlk9834i721clibn70ajsa', zoom: 3, hash: true, - center: center + center: [-98.5795, 39.8283] }); map.current.on('load', _ => { @@ -73,11 +54,9 @@ export function Map(props){ // Center the map on the coordinates of any clicked symbol from the 'symbols' layer. map.current.on('click', 'event-locations', function (e) { - console.log(e.features) if (e.features && e.features.length > 0) { props.selectLoc(e.features[0].properties.locKey); } - map.current.flyTo({center: e.features[0].geometry.coordinates}); }); // Change the cursor to a pointer when the it enters a feature in the 'symbols' layer. @@ -90,22 +69,41 @@ export function Map(props){ map.current.getCanvas().style.cursor = ''; }); + function inViewFeatures() { + var features = map.current.queryRenderedFeatures({ layers: ['event-locations', 'event-locations-highlight'] }); + var keys = features.map(f => { + return f.properties.locKey; + }); + props.inViewEvents(keys); + } + + // if the map moves, update the list of features in view. + map.current.on('moveend', inViewFeatures); + map.current.on('idle', inViewFeatures); + + setMapReady(true) }) }, []); - - useEffect(() => { - - console.log(props.hoverMarker, props.locFilt) - var key = props.hoverMarker || props.locFilt + function highlight(key, flyto) { var newlocs = locations.map(l => { l.properties.highlight = (l.properties.locKey === key) + if (flyto && l.properties.locKey === key) + map.current.flyTo({center: l.geometry.coordinates, zoom: 10}); return l; }) setLocations(newlocs); + } + + useEffect(() => { + highlight(props.locFilt, true); + }, [ props.locFilt]) + + useEffect(() => { + highlight(props.hoverMarker, false); + }, [props.hoverMarker]) - }, [props.hoverMarker, props.locFilt]) useEffect(() => { if (mapReady === false) return; diff --git a/src/SearchBar.js b/src/SearchBar.js index 06c359c..8b886b0 100644 --- a/src/SearchBar.js +++ b/src/SearchBar.js @@ -8,7 +8,6 @@ export function SearchBar(props){ function onlySetNumbers(event){ let baseValue = event.target.value; let replacedVal = baseValue.replace(/\D*/g, '') - console.log(`baseValue: ${baseValue}, replacedVal: ${replacedVal}`); setInput(replacedVal) } @@ -18,9 +17,8 @@ export function SearchBar(props){ } var eventlist = []; - console.log(props) if (props.locFilt !== null || props.nearby !== null) { - eventlist = ( props.updatedHover(item)}/>) + eventlist = ( props.updatedHover(item)}/>) } return( From 111ff45e1a9775d694d1d681694ba174b2f1320e Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Fri, 4 Oct 2019 16:55:18 -0700 Subject: [PATCH 5/8] perf improvements by more efficiently filtering inview events, rendering fewer events in list, and using mapbox-gl feature state rather than updating source data. Also highlight and scroll into view an event in the eventlist when clicking on the map --- src/App.js | 21 ++++++++-------- src/App.scss | 18 ++++++++------ src/EventList.js | 41 ++++++++++++++++++++++-------- src/Map.js | 65 +++++++++++++++++++++++++++++------------------- src/SearchBar.js | 2 +- 5 files changed, 91 insertions(+), 56 deletions(-) diff --git a/src/App.js b/src/App.js index 111afce..6ba6dc6 100644 --- a/src/App.js +++ b/src/App.js @@ -8,18 +8,18 @@ function App() { const [events, setEvents] = useState([]); //Current zip code search - input by user const [currZip, setCurrZip] = useState(null); - //Current event being hovered over - const [hoverEvent, setHoverEvent] = useState(null); - //Current selected location filter - const [locFilt, setLocFilt] = useState(null); + //Current selected event + const [highlightedEvent, setHighlightedEvent] = useState({}); //Events that are within the map viewport. These should be shown in the list - const [inViewEvents, setInViewEvents] = useState([]); + const [inViewEvents, setInViewEvents] = useState({}); // Load all of the events useEffect(() => { fetch("https://warren-events.s3.amazonaws.com/data/events.json") .then((res)=>res.json()) - .then((data)=>setEvents(data)); + .then((data)=>{ + setEvents(data) + }); }, []); useEffect(() => { @@ -30,20 +30,19 @@ function App() { .then(res => { if (res.data && res.data.length > 0){ let event = res.data[0] - setLocFilt(event.location.location.longitude +'&'+ event.location.location.latitude) + setHighlightedEvent({id: event.id, center:true}) } }) // Reset states on new zipcode - setHoverEvent(null); - setLocFilt(null); + setHighlightedEvent({}); }, [currZip]) return (
    - setCurrZip(newZip)} events={events} inViewEvents={inViewEvents} updatedHover={(newHover) => setHoverEvent(newHover)} locFilt={locFilt}/> - setLocFilt(newLoc)} locFilt={locFilt} inViewEvents={(keys) => setInViewEvents(keys)}/> + setCurrZip(newZip)} events={events} inViewEvents={inViewEvents} updatedHover={(newHover) => setHighlightedEvent(newHover)} highlightedEvent={highlightedEvent}/> + setHighlightedEvent(newLoc)} highlightedEvent={highlightedEvent} inViewEvents={(keys) => setInViewEvents(keys)}/>
    ); } diff --git a/src/App.scss b/src/App.scss index 8544681..e967a25 100644 --- a/src/App.scss +++ b/src/App.scss @@ -101,6 +101,16 @@ html, body, #root, .app { visibility: visible; } } + li.highlighted{ + background-color: #232444; + color: white; + + .eventRSVP{ + visibility: visible; + } + } + + } @@ -112,12 +122,4 @@ input[type="number"]::-webkit-inner-spin-button { } input[type="number"] { -moz-appearance: textfield; -} - -.marker { - background-image: url('img/w-marker-icon-2x.png'); - background-size: cover; - width: 25px; - height: 41px; - cursor: pointer; } \ No newline at end of file diff --git a/src/EventList.js b/src/EventList.js index e030cf7..0363658 100644 --- a/src/EventList.js +++ b/src/EventList.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useLayoutEffect } from 'react'; import moment from 'moment'; import groupBy from 'lodash.groupby'; import sortBy from 'lodash.sortby'; @@ -49,13 +49,28 @@ function EventTimes(props) { } export function EventList(props) { - if (!props.locFilt && !props.nearBy) { - } + // if we have a event selected on the map, and highlighted in the list + // lets scroll to see it. + useLayoutEffect(() => { + if (!props.highlightedEvent.id) return; + var card = document.querySelector(`a.eventCard[eventid='${props.highlightedEvent.id}']`); + if (card){ + card.scrollIntoView(true); + } + }, [props.highlightedEvent, props.inViewEvents]) + + // Filter based on the events that are currently in view. + var eventCount = 0; var inViewEvents = props.events.filter(event => { - var locKey = event['location']['location']['longitude'] + '&' + event['location']['location']['latitude']; - return props.inViewEvents.indexOf(locKey) >= 0 + // limit to top matching events. to avoid list updating perf issues. + if (eventCount > 30) return false; + if (props.inViewEvents[event.id]) { + eventCount +=1; + return true; + } + return false; }) const listEvents = inViewEvents.map((event, i) => { @@ -70,16 +85,18 @@ export function EventList(props) { range: start.twix(end) } }) + var liClass = 'event'; + if (props.highlightedEvent.id === event.id) liClass = 'event highlighted'; return ( { props.updatedHover(event['currentTarget'].getAttribute('coord')) }} - onMouseLeave={(event) => { props.updatedHover(null) }}> -
  • + eventid={event['id']} + onMouseEnter={(event) => { props.updatedHover({id: event['currentTarget'].getAttribute('eventid'), center:false}) }} + onMouseLeave={(event) => { props.updatedHover({}) }}> +
  • {event['title']}

    {event['location']['venue']} in {event['location']['locality']}

    @@ -93,7 +110,11 @@ export function EventList(props) { listEvents.push((
  • )) diff --git a/src/Map.js b/src/Map.js index e6d9cc6..97cb60c 100644 --- a/src/Map.js +++ b/src/Map.js @@ -7,6 +7,7 @@ export function Map(props){ const [locations, setLocations] = useState([]); const [mapReady, setMapReady] = useState(false); const map = useRef(); + const prevHighlightId = useRef() //First render useEffect(() => { @@ -33,12 +34,19 @@ export function Map(props){ "type": "symbol", "layout": { "icon-allow-overlap": true, - "icon-image": "w-marker-icon", "icon-anchor": "bottom", - "icon-size": 0.5 + "icon-size": 0.5, + "icon-image": "w-marker-icon" }, - "filter": ["!=", "highlight", true] + "paint": { + "icon-opacity": [ + "match", ["feature-state", "highlight"], + 1, 0, + 1 + ] + } }); + map.current.addLayer({ "id": "event-locations-highlight", "source": "locations", @@ -49,13 +57,20 @@ export function Map(props){ "icon-anchor": "bottom", "icon-size": 0.6 }, - "filter": ["==", "highlight", true] + "paint": { + "icon-opacity": [ + "match", ["feature-state", "highlight"], + 1, 1, + 0 + ] + } }); // Center the map on the coordinates of any clicked symbol from the 'symbols' layer. map.current.on('click', 'event-locations', function (e) { if (e.features && e.features.length > 0) { - props.selectLoc(e.features[0].properties.locKey); + console.log(e.features[0]) + props.selectEvent({id: e.features[0].id, center: e.features[0].geometry.coordinates}); } }); @@ -71,10 +86,11 @@ export function Map(props){ function inViewFeatures() { var features = map.current.queryRenderedFeatures({ layers: ['event-locations', 'event-locations-highlight'] }); - var keys = features.map(f => { - return f.properties.locKey; + var inView = {}; + features.forEach(f => { + inView[f.id] = true; }); - props.inViewEvents(keys); + props.inViewEvents(inView); } // if the map moves, update the list of features in view. @@ -86,23 +102,22 @@ export function Map(props){ }) }, []); - function highlight(key, flyto) { - var newlocs = locations.map(l => { - l.properties.highlight = (l.properties.locKey === key) - if (flyto && l.properties.locKey === key) - map.current.flyTo({center: l.geometry.coordinates, zoom: 10}); - return l; - }) - setLocations(newlocs); - } + function highlight(currentId, center) { + console.log('highlight', prevHighlightId.current, currentId) + if (prevHighlightId.current) map.current.setFeatureState({source: 'locations', id: prevHighlightId.current}, { highlight: 0}); - useEffect(() => { - highlight(props.locFilt, true); - }, [ props.locFilt]) + if (currentId) map.current.setFeatureState({source: 'locations', id: currentId}, { highlight: 1}); + + prevHighlightId.current = currentId; + if (center) + map.current.flyTo({center: center, zoom: 10}); + } useEffect(() => { - highlight(props.hoverMarker, false); - }, [props.hoverMarker]) + if (mapReady === false) return; + console.log(props.highlightedEvent) + highlight(props.highlightedEvent.id, props.highlightedEvent.center); + }, [ props.highlightedEvent, mapReady]) useEffect(() => { @@ -119,10 +134,8 @@ export function Map(props){ var places = props.events.map(e => { return { type: 'Feature', - properties:{ - "highlight": false, - "locKey": e.location.location.longitude + '&' + e.location.location.latitude - }, + id: e.id, + properties:{}, geometry: { type: 'Point', coordinates: [ diff --git a/src/SearchBar.js b/src/SearchBar.js index 8b886b0..7d951fd 100644 --- a/src/SearchBar.js +++ b/src/SearchBar.js @@ -18,7 +18,7 @@ export function SearchBar(props){ var eventlist = []; if (props.locFilt !== null || props.nearby !== null) { - eventlist = ( props.updatedHover(item)}/>) + eventlist = ( props.updatedHover(item)}/>) } return( From c3d1f483570c6418e80eb67df0a5f7d73ed99856 Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Fri, 4 Oct 2019 18:12:32 -0700 Subject: [PATCH 6/8] fix zipcode entry --- src/App.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.js b/src/App.js index 6ba6dc6..7d8f5f9 100644 --- a/src/App.js +++ b/src/App.js @@ -30,7 +30,7 @@ function App() { .then(res => { if (res.data && res.data.length > 0){ let event = res.data[0] - setHighlightedEvent({id: event.id, center:true}) + setHighlightedEvent({id: event.id, center:[event.location.location.longitude, event.location.location.latitude]}) } }) From 8605eadd548e5caf766e8e2c53b85394062c962d Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Wed, 9 Oct 2019 16:40:56 -0700 Subject: [PATCH 7/8] filter by location when clicking on a map marker. clear location selection by click elsewhere on the map. clear results when moving map because of zipcode --- package-lock.json | 13 +++------ package.json | 2 +- src/App.js | 19 +++++++++----- src/App.scss | 3 ++- src/EventList.js | 52 +++++++++++++++++------------------- src/Map.js | 67 +++++++++++++++++++++++++++++------------------ src/SearchBar.js | 2 +- 7 files changed, 87 insertions(+), 71 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c6cbf6..6edb20c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9115,11 +9115,6 @@ "invert-kv": "^1.0.0" } }, - "leaflet": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.5.1.tgz", - "integrity": "sha512-ekM9KAeG99tYisNBg0IzEywAlp0hYI5XRipsqRXyRTeuU8jcuntilpp+eFf5gaE0xubc9RuSNIVtByEKwqFV0w==" - }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -9365,9 +9360,9 @@ } }, "mapbox-gl": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.3.1.tgz", - "integrity": "sha512-IF7b0LZd/caTiknPhm8DAcv7bhvOCXO6rsW18rmFxi8Vw0syJXKK8DLLabI5oiJXtUIgLe57XRgduQzAYrb4og==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.4.0.tgz", + "integrity": "sha512-4nXRXanISou8oWLU7DH1ZetKvCJR9XtnMlQQ4Ia80wjghIHc5ljmAV/loNCI2UAGyuKINc7QcTiwUXrjE+Kv4w==", "requires": { "@mapbox/geojson-rewind": "^0.4.0", "@mapbox/geojson-types": "^1.0.2", @@ -9379,7 +9374,7 @@ "@mapbox/vector-tile": "^1.3.1", "@mapbox/whoots-js": "^3.1.0", "csscolorparser": "~1.0.2", - "earcut": "^2.1.5", + "earcut": "^2.2.0", "geojson-vt": "^3.2.1", "gl-matrix": "^3.0.0", "grid-index": "^1.1.0", diff --git a/package.json b/package.json index bf76120..9837e2b 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "@types/lodash.sortby": "^4.7.6", "lodash.groupby": "^4.6.0", "lodash.sortby": "^4.7.0", - "mapbox-gl": "^1.3.1", + "mapbox-gl": "^1.4.0", "moment": "^2.24.0", "node-sass": "^4.12.0", "react": "^16.9.0", diff --git a/src/App.js b/src/App.js index 7d8f5f9..f5ac00a 100644 --- a/src/App.js +++ b/src/App.js @@ -8,9 +8,13 @@ function App() { const [events, setEvents] = useState([]); //Current zip code search - input by user const [currZip, setCurrZip] = useState(null); - //Current selected event + //Current highlighted event (hovered in the list) const [highlightedEvent, setHighlightedEvent] = useState({}); - //Events that are within the map viewport. These should be shown in the list + //Used to filter by location, since there may be more than 1 event at a location. + //It's a string in the format lng+'&'+lat + const [locationFilter, setLocationFilter] = useState(null) + //Events that are within the map viewport. These should be shown in the list. + // This is a object keyed by eventid and used to filter the `events` object. const [inViewEvents, setInViewEvents] = useState({}); // Load all of the events @@ -30,19 +34,22 @@ function App() { .then(res => { if (res.data && res.data.length > 0){ let event = res.data[0] + setHighlightedEvent({id: event.id, center:[event.location.location.longitude, event.location.location.latitude]}) } - - }) + }); // Reset states on new zipcode + setInViewEvents({}); setHighlightedEvent({}); + setLocationFilter(null); + }, [currZip]) return (
    - setCurrZip(newZip)} events={events} inViewEvents={inViewEvents} updatedHover={(newHover) => setHighlightedEvent(newHover)} highlightedEvent={highlightedEvent}/> - setHighlightedEvent(newLoc)} highlightedEvent={highlightedEvent} inViewEvents={(keys) => setInViewEvents(keys)}/> + setCurrZip(newZip)} events={events} inViewEvents={inViewEvents} updatedHover={(newHover) => setHighlightedEvent(newHover)} locationFilter={locationFilter}/> + setLocationFilter(locKey)} highlightedEvent={highlightedEvent} inViewEvents={(keys) => setInViewEvents(keys)} locationFilter={locationFilter}/>
    ); } diff --git a/src/App.scss b/src/App.scss index e967a25..101639c 100644 --- a/src/App.scss +++ b/src/App.scss @@ -61,7 +61,8 @@ html, body, #root, .app { .eventList{ background-color: white; - height: 500px; + max-height: 500px; + min-height: 250px; overflow:hidden; overflow-y:scroll; margin-bottom: 0px; diff --git a/src/EventList.js b/src/EventList.js index 0363658..a1cf3b5 100644 --- a/src/EventList.js +++ b/src/EventList.js @@ -50,30 +50,28 @@ function EventTimes(props) { export function EventList(props) { - // if we have a event selected on the map, and highlighted in the list - // lets scroll to see it. - useLayoutEffect(() => { - if (!props.highlightedEvent.id) return; - var card = document.querySelector(`a.eventCard[eventid='${props.highlightedEvent.id}']`); - if (card){ - card.scrollIntoView(true); - } - }, [props.highlightedEvent, props.inViewEvents]) - - - // Filter based on the events that are currently in view. - var eventCount = 0; - var inViewEvents = props.events.filter(event => { - // limit to top matching events. to avoid list updating perf issues. - if (eventCount > 30) return false; - if (props.inViewEvents[event.id]) { - eventCount +=1; - return true; - } - return false; - }) + var visableEvents = []; + if (props.locationFilter) { + visableEvents = props.events.filter(event => { + event.locationKey = event.location.location.longitude + '&' + event.location.location.latitude; + return (props.locationFilter == event.locationKey); + }) + } else { + // Filter based on the events that are currently in view. + var eventCount = 0; + visableEvents = props.events.filter(event => { + // limit to top matching events. to avoid list updating perf issues. + if (eventCount > 30) return false; + event.locationKey = event.location.location.longitude + '&' + event.location.location.latitude; + if (props.inViewEvents[event.locationKey]) { + eventCount +=1; + return true; + } + return false; + }) + } - const listEvents = inViewEvents.map((event, i) => { + const listEvents = visableEvents.map((event, i) => { // Normalize Mobilize's time formatting into // easy-to-use moments @@ -85,18 +83,16 @@ export function EventList(props) { range: start.twix(end) } }) - var liClass = 'event'; - if (props.highlightedEvent.id === event.id) liClass = 'event highlighted'; return ( { props.updatedHover({id: event['currentTarget'].getAttribute('eventid'), center:false}) }} + eventlocation={event['locationKey']} + onMouseEnter={(event) => { props.updatedHover({locationKey: event['currentTarget'].getAttribute('eventlocation'), center:false}) }} onMouseLeave={(event) => { props.updatedHover({}) }}> -
  • +
  • {event['title']}

    {event['location']['venue']} in {event['location']['locality']}

    diff --git a/src/Map.js b/src/Map.js index 97cb60c..5a3b02e 100644 --- a/src/Map.js +++ b/src/Map.js @@ -4,7 +4,7 @@ import mapboxgl from 'mapbox-gl'; require('mapbox-gl/dist/mapbox-gl.css'); export function Map(props){ - const [locations, setLocations] = useState([]); + const [locations, setLocations] = useState({}); const [mapReady, setMapReady] = useState(false); const map = useRef(); const prevHighlightId = useRef() @@ -12,7 +12,7 @@ export function Map(props){ //First render useEffect(() => { // Create the map with US center - mapboxgl.accessToken = 'pk.eyJ1IjoibWlja3QiLCJhIjoiLXJIRS1NbyJ9.EfVT76g4A5dyuApW_zuIFQ'; + mapboxgl.accessToken = 'pk.eyJ1IjoibWlja3QiLCJhIjoiY2sxam1xNmtsMHU5aTNob2N4YndlYXV0byJ9.LWG413QaYVY9bN4kAFu9eg'; map.current = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mickt/ck0rlk9834i721clibn70ajsa', @@ -67,13 +67,21 @@ export function Map(props){ }); // Center the map on the coordinates of any clicked symbol from the 'symbols' layer. + map.current.on('click', function (e) { + props.setLocationFilter(null); + }); + map.current.on('click', 'event-locations', function (e) { - if (e.features && e.features.length > 0) { - console.log(e.features[0]) - props.selectEvent({id: e.features[0].id, center: e.features[0].geometry.coordinates}); - } + props.setLocationFilter(e.features[0].properties.locationKey); + if (map.current.getZoom() < 8) { + map.current.jumpTo({center: e.features[0].geometry.coordinates, zoom: 10}); + } else { + map.current.jumpTo({center: e.features[0].geometry.coordinates}); + } + }); + // Change the cursor to a pointer when the it enters a feature in the 'symbols' layer. map.current.on('mouseenter', 'event-locations', function () { map.current.getCanvas().style.cursor = 'pointer'; @@ -84,46 +92,46 @@ export function Map(props){ map.current.getCanvas().style.cursor = ''; }); - function inViewFeatures() { + function inViewFeatures(e) { var features = map.current.queryRenderedFeatures({ layers: ['event-locations', 'event-locations-highlight'] }); var inView = {}; features.forEach(f => { - inView[f.id] = true; + inView[f.properties.locationKey] = true; }); props.inViewEvents(inView); } - // if the map moves, update the list of features in view. - map.current.on('moveend', inViewFeatures); + // once the map settles in a location, then reset the features in view map.current.on('idle', inViewFeatures); - - setMapReady(true) + setMapReady(true); }) }, []); function highlight(currentId, center) { - console.log('highlight', prevHighlightId.current, currentId) if (prevHighlightId.current) map.current.setFeatureState({source: 'locations', id: prevHighlightId.current}, { highlight: 0}); if (currentId) map.current.setFeatureState({source: 'locations', id: currentId}, { highlight: 1}); prevHighlightId.current = currentId; if (center) - map.current.flyTo({center: center, zoom: 10}); + map.current.jumpTo({center: center, zoom: 10}); } useEffect(() => { if (mapReady === false) return; - console.log(props.highlightedEvent) - highlight(props.highlightedEvent.id, props.highlightedEvent.center); - }, [ props.highlightedEvent, mapReady]) + // if list is fitlered to a location that marker is highlighted, + // otherwise if an event is hovered in the list. + var locKey = props.locationFilter || props.highlightedEvent.locationKey; + var id = (locations[locKey] && locations[locKey].id) || null; + highlight(id, props.highlightedEvent.center); + }, [ props.highlightedEvent, props.locationFilter, mapReady]) useEffect(() => { if (mapReady === false) return; - var geojson = {type: 'FeatureCollection', features: locations}; + var geojson = {type: 'FeatureCollection', features: Object.values(locations)}; map.current.getSource('locations').setData(geojson); }, [locations, mapReady]) @@ -131,11 +139,20 @@ export function Map(props){ useEffect(() => { if (props.events === null) return; - var places = props.events.map(e => { - return { + //deduped locations, so we dont need to render multiple pins for the same location. + var locations = {}; + + props.events.forEach((e, i) => { + + var locationKey = e.location.location.longitude + '&' + e.location.location.latitude; + + locations[locationKey] = { type: 'Feature', - id: e.id, - properties:{}, + id: i+1, // id based on iterator used for feature state lookups to highlight markers. + // 0 id doesnt work (bug) + properties:{ + locationKey: locationKey + }, geometry: { type: 'Point', coordinates: [ @@ -143,9 +160,9 @@ export function Map(props){ parseFloat(e.location.location.latitude) ] } - } - }) - setLocations(places); + }; + }); + setLocations(locations); }, [props.events]); diff --git a/src/SearchBar.js b/src/SearchBar.js index 7d951fd..a5aea5d 100644 --- a/src/SearchBar.js +++ b/src/SearchBar.js @@ -18,7 +18,7 @@ export function SearchBar(props){ var eventlist = []; if (props.locFilt !== null || props.nearby !== null) { - eventlist = ( props.updatedHover(item)}/>) + eventlist = ( props.updatedHover(item)}/>) } return( From 3c7854770abbf902fbbb72800fcf063817b8bc8c Mon Sep 17 00:00:00 2001 From: Mick Thompson Date: Wed, 9 Oct 2019 17:42:23 -0700 Subject: [PATCH 8/8] unused import --- src/EventList.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EventList.js b/src/EventList.js index a1cf3b5..822c54c 100644 --- a/src/EventList.js +++ b/src/EventList.js @@ -1,4 +1,4 @@ -import React, { useLayoutEffect } from 'react'; +import React from 'react'; import moment from 'moment'; import groupBy from 'lodash.groupby'; import sortBy from 'lodash.sortby'; @@ -54,7 +54,7 @@ export function EventList(props) { if (props.locationFilter) { visableEvents = props.events.filter(event => { event.locationKey = event.location.location.longitude + '&' + event.location.location.latitude; - return (props.locationFilter == event.locationKey); + return (props.locationFilter === event.locationKey); }) } else { // Filter based on the events that are currently in view.