|
|
|
<template>
|
|
|
|
<QuickNav selected="Discover"></QuickNav>
|
|
|
|
|
|
|
|
<!-- CONTENT -->
|
|
|
|
<section id="Content" class="p-6 pb-24">
|
|
|
|
<!-- Heading -->
|
|
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
|
|
Discover
|
|
|
|
</h1>
|
|
|
|
|
|
|
|
<!-- Quick Search -->
|
|
|
|
<div id="QuickSearch" class="mb-4 flex" v-on:keyup.enter="searchAll()">
|
|
|
|
<input
|
|
|
|
type="text"
|
|
|
|
v-model="searchTerms"
|
|
|
|
placeholder="Search…"
|
|
|
|
class="block w-full rounded-l border border-r-0 border-slate-400 px-3 py-2"
|
|
|
|
/>
|
|
|
|
<button
|
|
|
|
@click="searchAll()"
|
|
|
|
class="px-4 rounded-r bg-slate-200 border border-l-0 border-slate-400"
|
|
|
|
>
|
|
|
|
<fa icon="magnifying-glass" class="fa-fw"></fa>
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Result Tabs -->
|
|
|
|
<div class="text-center text-slate-500 border-b border-slate-300">
|
|
|
|
<ul class="flex flex-wrap justify-center gap-4 -mb-px">
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="#"
|
|
|
|
@click="
|
|
|
|
projects = [];
|
|
|
|
isLocalActive = true;
|
|
|
|
isRemoteActive = false;
|
|
|
|
searchLocal();
|
|
|
|
"
|
|
|
|
v-bind:class="computedLocalTabClassNames()"
|
|
|
|
>
|
|
|
|
Nearby
|
|
|
|
<span
|
|
|
|
class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md"
|
|
|
|
>{{ localCount }}</span
|
|
|
|
>
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
<li>
|
|
|
|
<a
|
|
|
|
href="#"
|
|
|
|
v-bind:class="computedRemoteTabClassNames()"
|
|
|
|
@click="
|
|
|
|
projects = [];
|
|
|
|
isRemoteActive = true;
|
|
|
|
isLocalActive = false;
|
|
|
|
searchAll();
|
|
|
|
"
|
|
|
|
>
|
|
|
|
Remote
|
|
|
|
<span
|
|
|
|
class="font-semibold text-sm bg-slate-200 px-1.5 py-0.5 rounded-md"
|
|
|
|
>{{ remoteCount }}</span
|
|
|
|
>
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-if="isLocalActive">
|
|
|
|
<div v-if="!isChoosingSearchBox">
|
|
|
|
<button
|
|
|
|
class="ml-2 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
|
|
|
|
@click="isChoosingSearchBox = true"
|
|
|
|
>
|
|
|
|
Select a {{ searchBox ? "Different" : "" }} Location for Nearby Search
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<div v-else>
|
|
|
|
<button v-if="!searchBox && !isNewMarkerSet" class="m-4 px-4 py-2">
|
|
|
|
Choose Location Below 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="isNewMarkerSet"
|
|
|
|
class="m-4 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
|
|
|
|
@click="resetLatLong"
|
|
|
|
>
|
|
|
|
Reset Marker
|
|
|
|
</button>
|
|
|
|
<button
|
|
|
|
class="ml-2 px-4 py-2 rounded-md bg-blue-200 text-blue-500"
|
|
|
|
@click="cancelSearchBoxSelect"
|
|
|
|
>
|
|
|
|
Cancel
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Loading Animation -->
|
|
|
|
<div
|
|
|
|
class="fixed left-6 bottom-24 text-center text-4xl leading-none bg-slate-400 text-white w-14 py-2.5 rounded-full"
|
|
|
|
v-if="isLoading"
|
|
|
|
>
|
|
|
|
<fa icon="spinner" class="fa-spin-pulse"></fa>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Results List -->
|
|
|
|
<InfiniteScroll @reached-bottom="loadMoreData" v-if="!isChoosingSearchBox">
|
|
|
|
<ul>
|
|
|
|
<li
|
|
|
|
class="border-b border-slate-300"
|
|
|
|
v-for="project in projects"
|
|
|
|
:key="project.handleId"
|
|
|
|
>
|
|
|
|
<a
|
|
|
|
@click="onClickLoadProject(project.handleId)"
|
|
|
|
class="block py-4 flex gap-4"
|
|
|
|
>
|
|
|
|
<div class="w-12">
|
|
|
|
<EntityIcon
|
|
|
|
:entityId="project.handleId"
|
|
|
|
:iconSize="48"
|
|
|
|
class="block border border-slate-300 rounded-md"
|
|
|
|
></EntityIcon>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="grow">
|
|
|
|
<h2 class="text-base font-semibold">{{ project.name }}</h2>
|
|
|
|
<div class="text-sm">
|
|
|
|
<fa icon="user" class="fa-fw text-slate-400"></fa>
|
|
|
|
{{
|
|
|
|
didInfo(project.issuerDid, activeDid, allMyDids, allContacts)
|
|
|
|
}}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
</li>
|
|
|
|
</ul>
|
|
|
|
</InfiniteScroll>
|
|
|
|
|
|
|
|
<div
|
|
|
|
v-if="isLocalActive && isChoosingSearchBox"
|
|
|
|
style="height: 600px; width: 800px"
|
|
|
|
>
|
|
|
|
<l-map
|
|
|
|
ref="map"
|
|
|
|
:center="[localCenterLat, localCenterLong]"
|
|
|
|
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 "leaflet/dist/leaflet.css";
|
|
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
|
|
import {
|
|
|
|
LMap,
|
|
|
|
LMarker,
|
|
|
|
LRectangle,
|
|
|
|
LTileLayer,
|
|
|
|
} from "@vue-leaflet/vue-leaflet";
|
|
|
|
|
|
|
|
import { accountsDB, db } from "@/db/index";
|
|
|
|
import { Contact } from "@/db/tables/contacts";
|
|
|
|
import { BoundingBox, MASTER_SETTINGS_KEY } from "@/db/tables/settings";
|
|
|
|
import { accessToken } from "@/libs/crypto";
|
|
|
|
import { didInfo, ProjectData } from "@/libs/endorserServer";
|
|
|
|
import QuickNav from "@/components/QuickNav.vue";
|
|
|
|
import InfiniteScroll from "@/components/InfiniteScroll.vue";
|
|
|
|
import EntityIcon from "@/components/EntityIcon.vue";
|
|
|
|
import { RawAxiosRequestHeaders } from "axios";
|
|
|
|
|
|
|
|
const DEFAULT_LAT_LONG_DIFF = 0.01;
|
|
|
|
const WORLD_ZOOM = 2;
|
|
|
|
const DEFAULT_ZOOM = 2;
|
|
|
|
|
|
|
|
interface Notification {
|
|
|
|
group: string;
|
|
|
|
type: string;
|
|
|
|
title: string;
|
|
|
|
text: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Component({
|
|
|
|
components: {
|
|
|
|
LRectangle,
|
|
|
|
QuickNav,
|
|
|
|
InfiniteScroll,
|
|
|
|
EntityIcon,
|
|
|
|
LMap,
|
|
|
|
LMarker,
|
|
|
|
LTileLayer,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
export default class DiscoverView extends Vue {
|
|
|
|
$notify!: (notification: Notification, timeout?: number) => void;
|
|
|
|
|
|
|
|
activeDid = "";
|
|
|
|
allContacts: Array<Contact> = [];
|
|
|
|
allMyDids: Array<string> = [];
|
|
|
|
apiServer = "";
|
|
|
|
searchTerms = "";
|
|
|
|
projects: ProjectData[] = [];
|
|
|
|
isChoosingSearchBox = false;
|
|
|
|
isLocalActive = true;
|
|
|
|
isRemoteActive = false;
|
|
|
|
isNewMarkerSet = false;
|
|
|
|
localCenterLat = 0;
|
|
|
|
localCenterLong = 0;
|
|
|
|
localLatDiff = DEFAULT_LAT_LONG_DIFF;
|
|
|
|
localLongDiff = DEFAULT_LAT_LONG_DIFF;
|
|
|
|
localCount = 0;
|
|
|
|
localZoom = DEFAULT_ZOOM;
|
|
|
|
remoteCount = 0;
|
|
|
|
searchBox: { name: string; bbox: BoundingBox } | null = null;
|
|
|
|
isLoading = false;
|
|
|
|
|
|
|
|
// make this function available to the Vue template
|
|
|
|
didInfo = didInfo;
|
|
|
|
|
|
|
|
async mounted() {
|
|
|
|
await db.open();
|
|
|
|
const settings = await db.settings.get(MASTER_SETTINGS_KEY);
|
|
|
|
this.activeDid = settings?.activeDid || "";
|
|
|
|
this.apiServer = settings?.apiServer || "";
|
|
|
|
this.searchBox = settings?.searchBoxes?.[0] || null;
|
|
|
|
this.resetLatLong();
|
|
|
|
|
|
|
|
this.allContacts = await db.contacts.toArray();
|
|
|
|
|
|
|
|
await accountsDB.open();
|
|
|
|
const allAccounts = await accountsDB.accounts.toArray();
|
|
|
|
this.allMyDids = allAccounts.map((acc) => acc.did);
|
|
|
|
|
|
|
|
this.searchLocal();
|
|
|
|
}
|
|
|
|
|
|
|
|
public async buildHeaders(): Promise<HeadersInit> {
|
|
|
|
const headers: HeadersInit = {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
};
|
|
|
|
|
|
|
|
if (this.activeDid) {
|
|
|
|
await accountsDB.open();
|
|
|
|
const allAccounts = await accountsDB.accounts.toArray();
|
|
|
|
const account = allAccounts.find((acc) => acc.did === this.activeDid);
|
|
|
|
const identity = JSON.parse(account?.identity || "null");
|
|
|
|
|
|
|
|
if (!identity) {
|
|
|
|
throw new Error(
|
|
|
|
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service.",
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
headers["Authorization"] = "Bearer " + (await accessToken(identity));
|
|
|
|
} else {
|
|
|
|
// it's OK without auth... we just won't get any identifiers
|
|
|
|
}
|
|
|
|
return headers;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async searchAll(beforeId?: string) {
|
|
|
|
let queryParams = "claimContents=" + encodeURIComponent(this.searchTerms);
|
|
|
|
|
|
|
|
if (beforeId) {
|
|
|
|
queryParams = queryParams + `&beforeId=${beforeId}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.isLoading = true;
|
|
|
|
const response = await fetch(
|
|
|
|
this.apiServer + "/api/v2/report/plans?" + queryParams,
|
|
|
|
{
|
|
|
|
method: "GET",
|
|
|
|
headers: await this.buildHeaders(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
const details = await response.text();
|
|
|
|
this.$notify(
|
|
|
|
{
|
|
|
|
group: "alert",
|
|
|
|
type: "danger",
|
|
|
|
title: "Error",
|
|
|
|
text: `There was a problem accessing the server. Please try again later. (${details})`,
|
|
|
|
},
|
|
|
|
-1,
|
|
|
|
);
|
|
|
|
|
|
|
|
throw details;
|
|
|
|
}
|
|
|
|
|
|
|
|
const results = await response.json();
|
|
|
|
|
|
|
|
const plans: ProjectData[] = results.data;
|
|
|
|
if (plans) {
|
|
|
|
for (const plan of plans) {
|
|
|
|
const { name, description, handleId, rowid } = plan;
|
|
|
|
this.projects.push({ name, description, handleId, rowid });
|
|
|
|
}
|
|
|
|
this.remoteCount = this.projects.length;
|
|
|
|
} else {
|
|
|
|
throw JSON.stringify(results);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Error with feed load:", e);
|
|
|
|
this.$notify(
|
|
|
|
{
|
|
|
|
group: "alert",
|
|
|
|
type: "danger",
|
|
|
|
title: "Error",
|
|
|
|
text: e.userMessage || "There was a problem retrieving projects.",
|
|
|
|
},
|
|
|
|
-1,
|
|
|
|
);
|
|
|
|
} finally {
|
|
|
|
this.isLoading = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public async searchLocal(beforeId?: string) {
|
|
|
|
if (!this.searchBox) {
|
|
|
|
this.projects = [];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const claimContents =
|
|
|
|
"claimContents=" + encodeURIComponent(this.searchTerms);
|
|
|
|
let queryParams = [
|
|
|
|
claimContents,
|
|
|
|
"minLocLat=" + this.searchBox.bbox.minLat,
|
|
|
|
"maxLocLat=" + this.searchBox.bbox.maxLat,
|
|
|
|
"westLocLon=" + this.searchBox.bbox.westLong,
|
|
|
|
"eastLocLon=" + this.searchBox.bbox.eastLong,
|
|
|
|
].join("&");
|
|
|
|
|
|
|
|
if (beforeId) {
|
|
|
|
queryParams = queryParams + `&beforeId=${beforeId}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.isLoading = true;
|
|
|
|
const response = await fetch(
|
|
|
|
this.apiServer + "/api/v2/report/plansByLocation?" + queryParams,
|
|
|
|
{
|
|
|
|
method: "GET",
|
|
|
|
headers: await this.buildHeaders(),
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if (response.status !== 200) {
|
|
|
|
const details = await response.text();
|
|
|
|
this.$notify(
|
|
|
|
{
|
|
|
|
group: "alert",
|
|
|
|
type: "danger",
|
|
|
|
title: "Error",
|
|
|
|
text: `There was a problem accessing the server. Please try again later. (${details})`,
|
|
|
|
},
|
|
|
|
-1,
|
|
|
|
);
|
|
|
|
throw await response.text();
|
|
|
|
}
|
|
|
|
|
|
|
|
const results = await response.json();
|
|
|
|
|
|
|
|
if (results.data) {
|
|
|
|
if (beforeId) {
|
|
|
|
const plans: ProjectData[] = results.data;
|
|
|
|
for (const plan of plans) {
|
|
|
|
const { name, description, handleId = plan.handleId, rowid } = plan;
|
|
|
|
if (beforeId !== plan["rowid"]) {
|
|
|
|
this.projects.push({ name, description, handleId, rowid });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
this.projects = results.data;
|
|
|
|
}
|
|
|
|
this.localCount = this.projects.length;
|
|
|
|
} else {
|
|
|
|
throw JSON.stringify(results);
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.log("Error with feed load:", e);
|
|
|
|
this.$notify(
|
|
|
|
{
|
|
|
|
group: "alert",
|
|
|
|
type: "danger",
|
|
|
|
title: "Error",
|
|
|
|
text: e.userMessage || "There was a problem retrieving projects.",
|
|
|
|
},
|
|
|
|
-1,
|
|
|
|
);
|
|
|
|
} finally {
|
|
|
|
this.isLoading = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Data loader used by infinite scroller
|
|
|
|
* @param payload is the flag from the InfiniteScroll indicating if it should load
|
|
|
|
**/
|
|
|
|
async loadMoreData(payload: boolean) {
|
|
|
|
if (this.projects.length > 0 && payload) {
|
|
|
|
const latestProject = this.projects[this.projects.length - 1];
|
|
|
|
if (this.isLocalActive) {
|
|
|
|
this.searchLocal(latestProject["rowid"]);
|
|
|
|
} else if (this.isRemoteActive) {
|
|
|
|
this.searchAll(latestProject["rowid"]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle clicking on a project entry found in the list
|
|
|
|
* @param id of the project
|
|
|
|
**/
|
|
|
|
onClickLoadProject(id: string) {
|
|
|
|
localStorage.setItem("projectId", id);
|
|
|
|
const route = {
|
|
|
|
name: "project",
|
|
|
|
};
|
|
|
|
this.$router.push(route);
|
|
|
|
}
|
|
|
|
|
|
|
|
setMapPoint(event) {
|
|
|
|
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.searchLocal();
|
|
|
|
} 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: [],
|
|
|
|
});
|
|
|
|
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;
|
|
|
|
this.searchLocal();
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
public computedLocalTabClassNames() {
|
|
|
|
return {
|
|
|
|
"inline-block": true,
|
|
|
|
"py-3": true,
|
|
|
|
"rounded-t-lg": true,
|
|
|
|
"border-b-2": true,
|
|
|
|
active: this.isLocalActive,
|
|
|
|
"text-blue-600": this.isLocalActive,
|
|
|
|
"border-blue-600": this.isLocalActive,
|
|
|
|
"font-semibold": this.isLocalActive,
|
|
|
|
"border-transparent": !this.isLocalActive,
|
|
|
|
"hover:text-slate-600": !this.isLocalActive,
|
|
|
|
"hover:border-slate-300": !this.isLocalActive,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
public computedRemoteTabClassNames() {
|
|
|
|
return {
|
|
|
|
"inline-block": true,
|
|
|
|
"py-3": true,
|
|
|
|
"rounded-t-lg": true,
|
|
|
|
"border-b-2": true,
|
|
|
|
active: this.isRemoteActive,
|
|
|
|
"text-blue-600": this.isRemoteActive,
|
|
|
|
"border-blue-600": this.isRemoteActive,
|
|
|
|
"font-semibold": this.isRemoteActive,
|
|
|
|
"border-transparent": !this.isRemoteActive,
|
|
|
|
"hover:text-slate-600": !this.isRemoteActive,
|
|
|
|
"hover:border-slate-300": !this.isRemoteActive,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</script>
|