diff options
author | JeremyJamesL <jeremyluscombe@gmail.com> | 2024-12-09 21:41:49 +0000 |
---|---|---|
committer | JeremyJamesL <jeremyluscombe@gmail.com> | 2024-12-09 21:41:49 +0000 |
commit | 38db70476872f0e6b8d93aab16dc5a4543f5e56c (patch) | |
tree | 732fab0ee1cfdafc4a64ef02fbd256107b38dd3a | |
parent | 9ab0832af07e3e40dbd3a27b9d6682953e174f0c (diff) |
text search
-rw-r--r-- | .prettierrc | 5 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | index.html | 35 | ||||
-rw-r--r-- | src/refactor.js | 237 |
4 files changed, 167 insertions, 118 deletions
diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..78382bc --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "printWidth": 120, + "proseWrap": "never" +} @@ -19,12 +19,6 @@ Current searches for pubs in the UK are a bit rubbish. This takes inspiration fr ## To do: -[x] Get all pubs using an API -[] Replace null values for lat,lon with real data -[] Change search client to just -[] Don't return \_highlightResult? -[] Run search on first load -[] Clear listeners -[] remove duplication of search requests when selecting shape +[x] Get all pubs using an API [] Init map all one function [] City of London is missing [] Replace null values for lat,lon with real data [] Change search client to just [] Don't return \_highlightResult? [] Run search on first load [] Clear listeners [] remove duplication of search requests when selecting shape [] shape construction should probably be in data controller [] bundle all remove markers, query and result render into one function  @@ -11,10 +11,10 @@ var h, a, k, - p = "The Google Maps JavaScript API", - c = "google", - l = "importLibrary", - q = "__ib__", + p = 'The Google Maps JavaScript API', + c = 'google', + l = 'importLibrary', + q = '__ib__', m = document, b = window; b = b[c] || (b[c] = {}); @@ -24,26 +24,26 @@ u = () => h || (h = new Promise(async (f, n) => { - await (a = m.createElement("script")); - e.set("libraries", [...r] + ""); + await (a = m.createElement('script')); + e.set('libraries', [...r] + ''); for (k in g) e.set( - k.replace(/[A-Z]/g, (t) => "_" + t[0].toLowerCase()), + k.replace(/[A-Z]/g, (t) => '_' + t[0].toLowerCase()), g[k] ); - e.set("callback", c + ".maps." + q); + e.set('callback', c + '.maps.' + q); a.src = `https://maps.${c}apis.com/maps/api/js?` + e; d[q] = f; - a.onerror = () => (h = n(Error(p + " could not load."))); - a.nonce = m.querySelector("script[nonce]")?.nonce || ""; + a.onerror = () => (h = n(Error(p + ' could not load.'))); + a.nonce = m.querySelector('script[nonce]')?.nonce || ''; m.head.append(a); })); d[l] - ? console.warn(p + " only loads once. Ignoring:", g) + ? console.warn(p + ' only loads once. Ignoring:', g) : (d[l] = (f, ...n) => r.add(f) && u().then(() => d[l](f, ...n))); })({ - key: "AIzaSyCtS6CiOrG95FhroSUdDJJokItndcMkrgc", - v: "weekly", + key: 'AIzaSyCtS6CiOrG95FhroSUdDJJokItndcMkrgc', + v: 'weekly', }); </script> </head> @@ -55,19 +55,26 @@ Shape can be <span class="select--rectangle">Rectangular</span> or a <span class="select--polygon">Polygon</span> </div> + + <div>Try around your <span class="select--ip">location</span> instead</div> </div> </header> <main class="py-2"> <div class="container"> <div id="app" class="flex"> + <div> + <div> + <input type="text" placeholder="search..." id="searchbar" /> + </div> + <div id="hits"></div> + </div> <div id="map"></div> </div> </div> </main> <div id="searchbox"></div> - <div id="hits"></div> <!-- prettier-ignore --> <script>(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))}) ({key: "AIzaSyCtS6CiOrG95FhroSUdDJJokItndcMkrgc", v: "weekly"});</script> diff --git a/src/refactor.js b/src/refactor.js index 9374402..d4e50a7 100644 --- a/src/refactor.js +++ b/src/refactor.js @@ -1,19 +1,20 @@ -import { algoliasearch } from "algoliasearch"; -const ALGOLIA_KEY = "9fb3db0222f7b5aef0e2b30791ee6201"; -const INDEX_NAME = "pubfinder"; -const client = algoliasearch("YSWWVAX5RB", ALGOLIA_KEY); +import { algoliasearch } from 'algoliasearch'; +const ALGOLIA_KEY = '9fb3db0222f7b5aef0e2b30791ee6201'; +const INDEX_NAME = 'pubfinder'; +const client = algoliasearch('YSWWVAX5RB', ALGOLIA_KEY); const $ = document.querySelector.bind(document); const algolia_params = { hitsPerPage: 1000, }; const dataController = (function () { - const londonBounds = { - north: 51.532, - south: 51.478, - east: -0.072, - west: -0.16, + const initialRectBounds = { + north: 51.525241, + south: 51.488855, + east: -0.09138, + west: -0.122212, }; + const maxMapSpace = { north: 51.74, south: 51.27, @@ -32,13 +33,13 @@ const dataController = (function () { const centralPosition = { lat: 51.508616, lng: -0.125319 }; return { - londonBounds, + initialRectBounds, centralPosition, maxMapSpace, initialPolygonBounds, - getSearchResults: async function (shape, bounds) { + getSearchResults: async function (query = '', shape = '', bounds = []) { let geoParam; - if (shape === "rectangle") { + if (shape === 'rectangle') { geoParam = { insideBoundingBox: [bounds], }; @@ -51,13 +52,17 @@ const dataController = (function () { const res = await client.searchSingleIndex({ indexName: INDEX_NAME, searchParams: { + query, ...algolia_params, ...geoParam, - attributesToRetrieve: ["_geoloc", "name"], + attributesToRetrieve: ['_geoloc', 'name'], }, }); return res; }, + getCurrentQuery: function () { + return document.querySelector('#searchbar').value; + }, }; })(); @@ -66,28 +71,26 @@ const interfaceController = (function () { let shape; let map; const DOMStrings = { - selectRect: ".select--rectangle", - selectPoly: ".select--polygon", + selectRect: '.select--rectangle', + selectPoly: '.select--polygon', + ip: '.select--ip', + hits: '#hits', + searchBar: '#searchbar', }; - const generateMap = async function ( - bounds, - position, - maxSpace, - initialPolygonBounds, - currentShape - ) { - const { Map, Polygon, Rectangle } = await google.maps.importLibrary("maps"); - map = new Map(document.getElementById("map"), { - zoom: 4, + const generateMap = async function (bounds, position, maxSpace, initialPolygonBounds, currentShape) { + const { Map, Polygon, Rectangle } = await google.maps.importLibrary('maps'); + map = new Map(document.getElementById('map'), { + zoom: 9, center: position, - mapId: "pub_map", + mapId: 'pub_map', restriction: { latLngBounds: maxSpace, strictBounds: true, }, }); - if (currentShape === "rectangle") { + + if (currentShape === 'rectangle') { shape = new Rectangle({ bounds: bounds, editable: true, @@ -96,10 +99,10 @@ const interfaceController = (function () { } else { shape = new Polygon({ paths: initialPolygonBounds, - strokeColor: "#FF0000", + strokeColor: '#FF0000', strokeOpacity: 0.8, strokeWeight: 2, - fillColor: "#FF0000", + fillColor: '#FF0000', fillOpacity: 0.35, editable: true, draggable: true, @@ -113,10 +116,8 @@ const interfaceController = (function () { handleShapeSelect: function (e) {}, initMap: generateMap, embedSearchResults: async function (hits) { - const { AdvancedMarkerElement } = await google.maps.importLibrary( - "marker" - ); - const { InfoWindow } = await google.maps.importLibrary("maps"); + const { AdvancedMarkerElement } = await google.maps.importLibrary('marker'); + const { InfoWindow } = await google.maps.importLibrary('maps'); hits.forEach((hit) => { const marker = new AdvancedMarkerElement({ @@ -131,7 +132,7 @@ const interfaceController = (function () { content: hit.name, }); - marker.addListener("click", function () { + marker.addListener('click', function () { window.open({ anchor: marker, map, @@ -141,6 +142,18 @@ const interfaceController = (function () { markers.push(marker); }); }, + updateHitsList: function (hits) { + const slicedArr = hits.slice(0, 20); + $(DOMStrings.hits).innerHTML = ''; + let html = `<ul class="flex flex-col gap-2">`; + + for (const pub of slicedArr) { + html += `<li>${pub.name}</li>`; + } + html += `</ul>`; + + $(DOMStrings.hits).insertAdjacentHTML('beforeend', html); + }, handleBoundChange: function () { const { south, west, north, east } = shape.getBounds()?.toJSON(); return [north, east, south, west]; @@ -148,6 +161,24 @@ const interfaceController = (function () { getActiveShape: function () { return shape; }, + getShapeBounds: function () { + if (shape instanceof google.maps.Polygon) { + const paths = shape.getPaths(); + let points = []; + + paths.forEach((path) => { + path.forEach((latLng) => { + const lat = latLng.lat(); + const lng = latLng.lng(); + points.push(lat, lng); + }); + }); + return points; + } else { + const { south, west, north, east } = shape.getBounds()?.toJSON(); + return [north, east, south, west]; + } + }, removeMarkers: function () { for (let i = 0; i < markers.length; i++) { markers[i].setMap(null); @@ -161,109 +192,121 @@ const interfaceController = (function () { const controller = (function (dataCTRL, uiCTRL) { const DOM = uiCTRL.getDOMStrings(); - const londonBounds = dataCTRL.londonBounds; + const rectBounds = dataCTRL.initialRectBounds; const centralPoint = dataCTRL.centralPosition; const mapBounds = dataCTRL.maxMapSpace; // This is rectangle bounds const initialPolygonBounds = dataCTRL.initialPolygonBounds; // Polygon bound const initialSetup = async function () { - await uiCTRL.initMap( - londonBounds, - centralPoint, - mapBounds, - initialPolygonBounds, - "rectangle" - ); - - const searchResults = await dataCTRL.getSearchResults("rectangle", [ - londonBounds.north, - londonBounds.east, - londonBounds.south, - londonBounds.west, + await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle'); + + const searchResults = await dataCTRL.getSearchResults('', 'rectangle', [ + rectBounds.north, + rectBounds.east, + rectBounds.south, + rectBounds.west, ]); await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); }; const setupMapListeners = async function () { const shape = uiCTRL.getActiveShape(); - if (shape instanceof google.maps.Rectangle) { - shape.addListener("dragend", async () => { + let debounceTimer; + + shape.addListener('bounds_changed', () => { uiCTRL.removeMarkers(); - const newAlgoliaBounds = uiCTRL.handleBoundChange(); + clearTimeout(debounceTimer); - const searchResults = await dataCTRL.getSearchResults( - "rectangle", - newAlgoliaBounds - ); - await uiCTRL.embedSearchResults(searchResults.hits); + debounceTimer = setTimeout(async () => { + const newAlgoliaBounds = uiCTRL.handleBoundChange(); + const searchResults = await dataCTRL.getSearchResults( + dataCTRL.getCurrentQuery(), + 'rectangle', + newAlgoliaBounds + ); + await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); + }, 200); }); } if (shape instanceof google.maps.Polygon) { + let debounceTimer; const paths = shape.getPaths(); - let points = []; const handlePathDrag = async function () { uiCTRL.removeMarkers(); - points = []; - paths.forEach((path) => { - path.forEach((latLng) => { - const lat = latLng.lat(); - const lng = latLng.lng(); - points.push(lat, lng); - }); - }); - const searchResults = await dataCTRL.getSearchResults( - "polygon", - points - ); - await uiCTRL.embedSearchResults(searchResults.hits); + clearTimeout(debounceTimer); + + debounceTimer = setTimeout(async () => { + const points = uiCTRL.getShapeBounds(); + + const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'polygon', points); + await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); + }, 300); }; paths.forEach((path) => { - path.addListener("set_at", handlePathDrag); - path.addListener("insert_at", handlePathDrag); + path.addListener('set_at', handlePathDrag); + path.addListener('insert_at', handlePathDrag); }); } }; const setupShapeSelectListeners = async function () { - $(DOM.selectRect).addEventListener("click", async () => { - await uiCTRL.initMap( - londonBounds, - centralPoint, - mapBounds, - initialPolygonBounds, - "rectangle" - ); - const searchResults = await dataCTRL.getSearchResults("rectangle", [ - londonBounds.north, - londonBounds.east, - londonBounds.south, - londonBounds.west, + $(DOM.selectRect).addEventListener('click', async () => { + await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle'); + const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'rectangle', [ + rectBounds.north, + rectBounds.east, + rectBounds.south, + rectBounds.west, ]); await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); await setupMapListeners(); }); - $(DOM.selectPoly).addEventListener("click", async () => { - await uiCTRL.initMap( - londonBounds, - centralPoint, - mapBounds, - initialPolygonBounds, - "polygon" - ); - const translatedPolyBounds = initialPolygonBounds.flatMap( - ({ lat, lng }) => [lat, lng] - ); + + $(DOM.selectPoly).addEventListener('click', async () => { + await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'polygon'); + const translatedPolyBounds = initialPolygonBounds.flatMap(({ lat, lng }) => [lat, lng]); const searchResults = await dataCTRL.getSearchResults( - "polygon", + dataCTRL.getCurrentQuery(), + 'polygon', translatedPolyBounds ); await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); await setupMapListeners(); }); + + $(DOM.ip).addEventListener('click', async () => { + const res = await client.searchSingleIndex({ + indexName: INDEX_NAME, + searchParams: { + aroundLatLngViaIP: true, + aroundRadius: 1000, + }, + }); + console.log(res); + }), + $(DOM.searchBar).addEventListener('input', async (e) => { + const q = dataCTRL.getCurrentQuery(); + if (q > 0 && q < 3) return; + const bounds = uiCTRL.getShapeBounds(); + const activeShape = uiCTRL.getActiveShape(); + const searchResults = await dataCTRL.getSearchResults( + q, + activeShape instanceof google.maps.Polygon ? 'polygon' : 'rectangle', + bounds + ); + uiCTRL.removeMarkers(); + await uiCTRL.embedSearchResults(searchResults.hits); + uiCTRL.updateHitsList(searchResults.hits); + }); }; return { |