aboutsummaryrefslogtreecommitdiff
path: root/src/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.js')
-rw-r--r--src/main.js355
1 files changed, 355 insertions, 0 deletions
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 0000000..1466b3f
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,355 @@
+import { algoliasearch } from 'algoliasearch';
+const ALGOLIA_KEY = '9fb3db0222f7b5aef0e2b30791ee6201';
+const INDEX_NAME = 'pubfinder';
+const client = algoliasearch('YSWWVAX5RB', ALGOLIA_KEY);
+const $ = document.querySelector.bind(document);
+
+const dataController = (function () {
+ const initialRectBounds = {
+ north: 51.522899,
+ south: 51.484821,
+ east: -0.07766,
+ west: -0.138968,
+ };
+
+ const maxMapSpace = {
+ north: 51.74,
+ south: 51.27,
+ west: -0.51,
+ east: 0.23,
+ };
+
+ const initialPolygonBounds = [
+ { lat: 51.522786, lng: -0.077603 },
+ { lat: 51.525376, lng: -0.108023 },
+ { lat: 51.520791, lng: -0.138718 },
+ { lat: 51.506604, lng: -0.151214 },
+ { lat: 51.485288, lng: -0.106803 },
+ { lat: 51.493989, lng: -0.085187 },
+ ];
+
+ const centralPosition = { lat: 51.508616, lng: -0.125319 };
+
+ return {
+ initialRectBounds,
+ centralPosition,
+ maxMapSpace,
+ initialPolygonBounds,
+ getSearchResults: async function (query = '', shape = '', bounds = [], hitsToGet) {
+ let geoParam;
+ if (shape === 'rectangle') {
+ geoParam = {
+ insideBoundingBox: [bounds],
+ };
+ } else if (shape === 'polygon') {
+ geoParam = {
+ insidePolygon: [bounds],
+ };
+ } else {
+ geoParam = {
+ aroundLatLng: bounds,
+ // aroundRadius: 1000,
+ };
+ }
+
+ const res = await client.searchSingleIndex({
+ indexName: INDEX_NAME,
+ searchParams: {
+ query,
+ hitsPerPage: hitsToGet,
+ ...geoParam,
+ attributesToRetrieve: ['_geoloc', 'name', 'address', 'postcode'],
+ },
+ });
+ return res;
+ },
+ getCurrentQuery: function () {
+ return document.querySelector('#searchbar').value;
+ },
+ };
+})();
+
+const interfaceController = (function () {
+ let markers = [];
+ let shape;
+ let map;
+ const DOMStrings = {
+ selectRect: '.select--rectangle',
+ selectPoly: '.select--polygon',
+ ip: '.select--ip',
+ ipLoader: '.ip-loader',
+ hits: '#hits',
+ searchBar: '#searchbar',
+ pubCount: '.pub-count',
+ };
+
+ const generateMap = async function (bounds, position, maxSpace, initialPolygonBounds, currentShape, zoom = 12) {
+ const { Map, Polygon, Rectangle } = await google.maps.importLibrary('maps');
+ map = new Map(document.getElementById('map'), {
+ zoom,
+ center: position,
+ mapId: 'pub_map',
+ colorScheme: 'DARK',
+ restriction: {
+ latLngBounds: maxSpace,
+ strictBounds: true,
+ },
+ });
+
+ if (currentShape === 'rectangle') {
+ shape = new Rectangle({
+ bounds: bounds,
+ editable: true,
+ draggable: true,
+ strokeWeight: 2,
+ strokeOpacity: 0.8,
+ strokeColor: '#D97706',
+ fillColor: '#D97706',
+ fillOpacity: 0.35,
+ });
+ shape.setMap(map);
+ } else if (currentShape === 'polygon') {
+ shape = new Polygon({
+ paths: initialPolygonBounds,
+ strokeColor: '#D97706',
+ strokeOpacity: 0.8,
+ strokeWeight: 2,
+ fillColor: '#D97706',
+ fillOpacity: 0.35,
+ editable: true,
+ draggable: true,
+ geodesic: true,
+ });
+ shape.setMap(map);
+ }
+ };
+
+ return {
+ handleShapeSelect: function (e) {},
+ initMap: generateMap,
+ embedSearchResults: async function (hits) {
+ const { AdvancedMarkerElement } = await google.maps.importLibrary('marker');
+ const { InfoWindow } = await google.maps.importLibrary('maps');
+
+ hits.forEach((hit) => {
+ const marker = new AdvancedMarkerElement({
+ map: map,
+ position: {
+ lat: hit._geoloc.lat,
+ lng: hit._geoloc.lng,
+ },
+ });
+
+ const window = new InfoWindow({
+ content: hit.name,
+ });
+
+ marker.addListener('click', function () {
+ window.open({
+ anchor: marker,
+ map,
+ });
+ });
+
+ markers.push(marker);
+ });
+ },
+ updateHitsList: function (hits) {
+ $(DOMStrings.hits).innerHTML = '';
+ let html = ``;
+ if (hits.length > 0) {
+ const slicedArr = hits.slice(0, 20);
+ html += `<ul class="flex flex-col gap-2">`;
+ for (const pub of slicedArr) {
+ html += `<li class="relative bg-[#373737] rounded-lg p-4">
+ <h3 class="font-medium mb-2">${pub.name}</h3>
+ <h4 class="text-sm">${pub.address}, ${pub.postcode}</h4>
+ <a href="https://www.google.co.uk/maps/place/Ye+Old+White+Horse/" class="absolute block top-4 right-4" target="_blank">
+ <svg width="20px" height="20px" viewBox="0 0 24 24" fill="none" >
+ <path d="M5 12V6C5 5.44772 5.44772 5 6 5H18C18.5523 5 19 5.44772 19 6V18C19 18.5523 18.5523 19 18 19H12M8.11111 12H12M12 12V15.8889M12 12L5 19" stroke="#D97706" stroke-linecap="round" stroke-linejoin="round" />
+ </svg>
+ </a>
+ </li>`;
+ }
+ html += `</ul>`;
+ } else {
+ html = `<div class="text-center mt-5">There are no results to show, try refinfing your search</div>`;
+ }
+
+ $(DOMStrings.hits).insertAdjacentHTML('beforeend', html);
+ $(DOMStrings.pubCount).innerHTML = `${hits.length > 0 ? 'Showing ' + hits.length + ' pubs' : 'No pubs'}`;
+ },
+ handleBoundChange: function () {
+ const { south, west, north, east } = shape.getBounds()?.toJSON();
+ return [north, east, south, west];
+ },
+ 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);
+ }
+ },
+ getDOMStrings: function () {
+ return DOMStrings;
+ },
+ };
+})();
+
+const controller = (function (dataCTRL, uiCTRL) {
+ const DOM = uiCTRL.getDOMStrings();
+ const rectBounds = dataCTRL.initialRectBounds;
+ const centralPoint = dataCTRL.centralPosition;
+ const mapBounds = dataCTRL.maxMapSpace; // Rectangle bounds
+ const initialPolygonBounds = dataCTRL.initialPolygonBounds; // Polygon bounds
+
+ const initialSetup = async function () {
+ await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);
+
+ const searchResults = await dataCTRL.getSearchResults(
+ '',
+ 'rectangle',
+ [rectBounds.north, rectBounds.east, rectBounds.south, rectBounds.west],
+ 1000
+ );
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ };
+
+ const setupMapListeners = async function () {
+ const shape = uiCTRL.getActiveShape();
+ if (shape instanceof google.maps.Rectangle) {
+ let debounceTimer;
+
+ shape.addListener('bounds_changed', () => {
+ uiCTRL.removeMarkers();
+ clearTimeout(debounceTimer);
+
+ debounceTimer = setTimeout(async () => {
+ const newAlgoliaBounds = uiCTRL.handleBoundChange();
+ const searchResults = await dataCTRL.getSearchResults(
+ dataCTRL.getCurrentQuery(),
+ 'rectangle',
+ newAlgoliaBounds,
+ 1000
+ );
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ }, 200);
+ });
+ }
+
+ if (shape instanceof google.maps.Polygon) {
+ let debounceTimer;
+ const paths = shape.getPaths();
+
+ const handlePathDrag = async function () {
+ uiCTRL.removeMarkers();
+ clearTimeout(debounceTimer);
+
+ debounceTimer = setTimeout(async () => {
+ const points = uiCTRL.getShapeBounds();
+
+ const searchResults = await dataCTRL.getSearchResults(dataCTRL.getCurrentQuery(), 'polygon', points, 1000);
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ }, 300);
+ };
+
+ paths.forEach((path) => {
+ path.addListener('set_at', handlePathDrag);
+ path.addListener('insert_at', handlePathDrag);
+ });
+ }
+ };
+
+ const setupEventListeners = async function () {
+ $(DOM.selectRect).addEventListener('click', async () => {
+ await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'rectangle', 12);
+ const searchResults = await dataCTRL.getSearchResults(
+ dataCTRL.getCurrentQuery(),
+ 'rectangle',
+ [rectBounds.north, rectBounds.east, rectBounds.south, rectBounds.west],
+ 1000
+ );
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ await setupMapListeners();
+ });
+
+ $(DOM.selectPoly).addEventListener('click', async () => {
+ await uiCTRL.initMap(rectBounds, centralPoint, mapBounds, initialPolygonBounds, 'polygon', 12);
+ const translatedPolyBounds = initialPolygonBounds.flatMap(({ lat, lng }) => [lat, lng]);
+ const searchResults = await dataCTRL.getSearchResults(
+ dataCTRL.getCurrentQuery(),
+ 'polygon',
+ translatedPolyBounds,
+ 1000
+ );
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ await setupMapListeners();
+ });
+
+ // $(DOM.ip).addEventListener('click', async () => {
+ // $(DOM.ipLoader).classList.replace('hidden', 'inline-flex');
+ // navigator.geolocation.getCurrentPosition(async (pos) => {
+ // const position = { lat: pos.coords.latitude, lng: pos.coords.longitude };
+ // await uiCTRL.initMap('', centralPoint, position, '', 'ip', 8);
+ // const searchResults = await dataCTRL.getSearchResults(
+ // dataCTRL.getCurrentQuery(),
+ // 'ip',
+ // `${pos.coords.latitude}, ${pos.coords.longitude}`,
+ // 20
+ // );
+ // await uiCTRL.embedSearchResults(searchResults.hits);
+ // uiCTRL.updateHitsList(searchResults.hits);
+ // $(DOM.ipLoader).classList.replace('inline-flex', 'hidden');
+ // });
+ // }),
+ $(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,
+ 1000
+ );
+ uiCTRL.removeMarkers();
+ await uiCTRL.embedSearchResults(searchResults.hits);
+ uiCTRL.updateHitsList(searchResults.hits);
+ });
+ };
+
+ return {
+ init: async function () {
+ await initialSetup();
+ await setupMapListeners();
+ await setupEventListeners();
+ },
+ };
+})(dataController, interfaceController);
+
+controller.init();