<template> <QuickNav /> <!-- CONTENT --> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <!-- Breadcrumb --> <div class="mb-8"> <!-- Back --> <div class="text-lg text-center font-light relative px-7"> <h1 class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" @click="$router.back()" > <fa icon="chevron-left" class="fa-fw"></fa> </h1> </div> <!-- Heading --> <h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> Area for Nearby Search </h1> </div> <div class="px-2 py-4"> This location is only stored on your device. It is used to show you more appropriate projects but is not stored on any servers. </div> <div> <button v-if="!searchBox && !isNewMarkerSet" class="m-4 px-4 py-2"> Click to Choose a Location for Nearby Search </button> <button v-if="isNewMarkerSet" class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500" @click="storeSearchBox" > Store This Location for Nearby Search </button> <button v-if="searchBox" class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500" @click="forgetSearchBox" > Delete Stored Location </button> <button v-if="searchBox" class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500" @click="resetLatLong" > Reset Marker </button> <button v-if="isNewMarkerSet" class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500" @click="isNewMarkerSet = false" > Erase Marker </button> <div v-if="isNewMarkerSet"> Click on the pin to erase it. Click anywhere else to set a different different corner. </div> </div> <div class="mb-4 aspect-video"> <l-map ref="map" :center="[localCenterLat, localCenterLong]" class="!z-40 rounded-md" v-model:zoom="localZoom" @click="setMapPoint" > <l-tile-layer url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" layer-type="base" name="OpenStreetMap" /> <l-marker v-if="isNewMarkerSet" :lat-lng="[localCenterLat, localCenterLong]" @click="isNewMarkerSet = false" /> <l-rectangle v-if="isNewMarkerSet" :bounds="[ [localCenterLat - localLatDiff, localCenterLong - localLongDiff], [localCenterLat + localLatDiff, localCenterLong + localLongDiff], ]" :weight="1" /> </l-map> </div> </section> </template> <script lang="ts"> import { LeafletMouseEvent } from "leaflet"; import "leaflet/dist/leaflet.css"; import { Component, Vue } from "vue-facing-decorator"; import { LMap, LMarker, LRectangle, LTileLayer, } from "@vue-leaflet/vue-leaflet"; import QuickNav from "@/components/QuickNav.vue"; import { NotificationIface } from "@/constants/app"; import { db } from "@/db/index"; import { BoundingBox, MASTER_SETTINGS_KEY } from "@/db/tables/settings"; const DEFAULT_LAT_LONG_DIFF = 0.01; const WORLD_ZOOM = 2; const DEFAULT_ZOOM = 2; @Component({ components: { QuickNav, LRectangle, LMap, LMarker, LTileLayer, }, }) export default class DiscoverView extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; isChoosingSearchBox = false; isNewMarkerSet = false; // "local" vars are for the currently selected map box localCenterLat = 0; localCenterLong = 0; localLatDiff = DEFAULT_LAT_LONG_DIFF; localLongDiff = DEFAULT_LAT_LONG_DIFF; localZoom = DEFAULT_ZOOM; // searchBox reflects what is stored in the database searchBox: { name: string; bbox: BoundingBox } | null = null; async mounted() { await db.open(); const settings = await db.settings.get(MASTER_SETTINGS_KEY); this.searchBox = settings?.searchBoxes?.[0] || null; this.resetLatLong(); } setMapPoint(event: LeafletMouseEvent) { if (this.isNewMarkerSet) { this.localLatDiff = Math.abs(event.latlng.lat - this.localCenterLat); this.localLongDiff = Math.abs(event.latlng.lng - this.localCenterLong); } else { // marker is not set this.localCenterLat = event.latlng.lat; this.localCenterLong = event.latlng.lng; let latDiff = DEFAULT_LAT_LONG_DIFF; let longDiff = DEFAULT_LAT_LONG_DIFF; // Guess at a size for the bounding box. // This doesn't seem like the right approach but it's the only way I can find to get the screen bounds. const bounds = event.target.boxZoom?._map?.getBounds(); if (bounds) { latDiff = Math.abs(bounds._northEast.lat - bounds._southWest.lat) / 8; longDiff = Math.abs(bounds._northEast.lng - bounds._southWest.lng) / 8; } this.localLatDiff = latDiff; this.localLongDiff = longDiff; this.isNewMarkerSet = true; } } public resetLatLong() { if (this.searchBox?.bbox) { const bbox = this.searchBox.bbox; this.localCenterLat = (bbox.maxLat + bbox.minLat) / 2; this.localCenterLong = (bbox.eastLong + bbox.westLong) / 2; this.localLatDiff = (bbox.maxLat - bbox.minLat) / 2; this.localLongDiff = (bbox.eastLong - bbox.westLong) / 2; this.localZoom = WORLD_ZOOM; this.isNewMarkerSet = true; } else { this.isNewMarkerSet = false; } } public async storeSearchBox() { if (this.localCenterLong || this.localCenterLat) { try { const newSearchBox = { name: "Local", bbox: { eastLong: this.localCenterLong + this.localLongDiff, maxLat: this.localCenterLat + this.localLatDiff, minLat: this.localCenterLat - this.localLatDiff, westLong: this.localCenterLong - this.localLongDiff, }, }; await db.open(); db.settings.update(MASTER_SETTINGS_KEY, { searchBoxes: [newSearchBox], }); this.searchBox = newSearchBox; this.isChoosingSearchBox = false; this.$notify( { group: "alert", type: "success", title: "Saved", text: "That has been saved in your preferences. You can now filter by it on your home screen feed.", }, 7000, ); this.$router.back(); } catch (err) { this.$notify( { group: "alert", type: "danger", title: "Error Updating Search Settings", text: "Try going to a different page and then coming back.", }, -1, ); console.error( "Telling user to retry the location search setting because:", err, ); } } else { this.$notify( { group: "alert", type: "warning", title: "No Location Selected", text: "Select a location on the map.", }, -1, ); } } public async forgetSearchBox() { try { await db.open(); db.settings.update(MASTER_SETTINGS_KEY, { searchBoxes: [], filterFeedByNearby: false, }); this.searchBox = null; this.localCenterLat = 0; this.localCenterLong = 0; this.localLatDiff = DEFAULT_LAT_LONG_DIFF; this.localLongDiff = DEFAULT_LAT_LONG_DIFF; this.localZoom = DEFAULT_ZOOM; this.isChoosingSearchBox = false; this.isNewMarkerSet = false; } catch (err) { this.$notify( { group: "alert", type: "danger", title: "Error Updating Search Settings", text: "Try going to a different page and then coming back.", }, -1, ); console.error( "Telling user to retry the location search setting because:", err, ); } } public cancelSearchBoxSelect() { this.isChoosingSearchBox = false; this.localZoom = WORLD_ZOOM; } } </script>