Merge branch 'explore-locateme' into 'master'

restore locate me button in search form

See merge request framasoft/mobilizon!578
This commit is contained in:
Thomas Citharel 2020-10-05 11:01:03 +02:00
commit 4ff4514fd7
3 changed files with 148 additions and 36 deletions

View File

@ -1,33 +1,51 @@
<template> <template>
<b-autocomplete <div class="address-autocomplete">
:data="addressData" <b-field expanded>
v-model="queryText" <b-autocomplete
:placeholder="placeholder || $t('e.g. 10 Rue Jangot')" :data="addressData"
field="fullName" v-model="queryText"
:loading="isFetching" :placeholder="$t('e.g. 10 Rue Jangot')"
@typing="fetchAsyncData" field="fullName"
icon="map-marker" :loading="isFetching"
expanded @typing="fetchAsyncData"
@select="updateSelected" icon="map-marker"
> expanded
<template slot-scope="{ option }"> @select="updateSelected"
<b-icon :icon="option.poiInfos.poiIcon.icon" /> >
<b>{{ option.poiInfos.name }}</b <template slot-scope="{ option }">
><br /> <b-icon :icon="option.poiInfos.poiIcon.icon" />
<small>{{ option.poiInfos.alternativeName }}</small> <b>{{ option.poiInfos.name }}</b
</template> ><br />
<template slot="empty"> <small>{{ option.poiInfos.alternativeName }}</small>
<span v-if="isFetching">{{ $t("Searching") }}</span> </template>
<div v-else-if="queryText.length >= 3" class="is-enabled"> </b-autocomplete>
<span>{{ $t('No results for "{queryText}"') }}</span> </b-field>
<span>{{ <b-field v-if="isSecureContext()">
$t("You can try another search term or drag and drop the marker on the map", { <b-button type="is-text" v-if="!gettingLocation" icon-right="target" @click="locateMe">{{
queryText, $t("Use my location")
}) }}</b-button>
}}</span> <span v-else>{{ $t("Getting location") }}</span>
</div> </b-field>
</template> <!--
</b-autocomplete> <div v-if="selected && selected.geom" class="control">
<b-checkbox @input="togglemap" />
<label class="label">{{ $t("Show map") }}</label>
</div>
<div class="map" v-if="showmap && selected && selected.geom">
<map-leaflet
:coords="selected.geom"
:marker="{
text: [selected.poiInfos.name, selected.poiInfos.alternativeName],
icon: selected.poiInfos.poiIcon.icon,
}"
:updateDraggableMarkerCallback="reverseGeoCode"
:options="{ zoom: mapDefaultZoom }"
:readOnly="false"
/>
</div>
-->
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import { Component, Prop, Vue, Watch } from "vue-property-decorator";
@ -39,6 +57,9 @@ import { CONFIG } from "../../graphql/config";
import { IConfig } from "../../types/config.model"; import { IConfig } from "../../types/config.model";
@Component({ @Component({
components: {
"map-leaflet": () => import(/* webpackChunkName: "map" */ "@/components/Map.vue"),
},
apollo: { apollo: {
config: CONFIG, config: CONFIG,
}, },
@ -46,8 +67,6 @@ import { IConfig } from "../../types/config.model";
export default class AddressAutoComplete extends Vue { export default class AddressAutoComplete extends Vue {
@Prop({ required: true }) value!: IAddress; @Prop({ required: true }) value!: IAddress;
@Prop({ required: false }) placeholder!: string;
addressData: IAddress[] = []; addressData: IAddress[] = [];
selected: IAddress = new Address(); selected: IAddress = new Address();
@ -56,19 +75,31 @@ export default class AddressAutoComplete extends Vue {
queryText: string = (this.value && new Address(this.value).fullName) || ""; queryText: string = (this.value && new Address(this.value).fullName) || "";
addressModalActive = false;
showmap = false;
private gettingLocation = false;
private location!: Position;
private gettingLocationError: any;
private mapDefaultZoom = 15;
config!: IConfig; config!: IConfig;
fetchAsyncData!: Function; fetchAsyncData!: Function;
// We put this in data because of issues like // We put this in data because of issues like
// https://github.com/vuejs/vue-class-component/issues/263 // https://github.com/vuejs/vue-class-component/issues/263
data() { data(): Record<string, unknown> {
return { return {
fetchAsyncData: debounce(this.asyncData, 200), fetchAsyncData: debounce(this.asyncData, 200),
}; };
} }
async asyncData(query: string) { async asyncData(query: string): Promise<void> {
if (!query.length) { if (!query.length) {
this.addressData = []; this.addressData = [];
this.selected = new Address(); this.selected = new Address();
@ -95,7 +126,7 @@ export default class AddressAutoComplete extends Vue {
} }
@Watch("config") @Watch("config")
watchConfig(config: IConfig) { watchConfig(config: IConfig): void {
if (!config.geocoding.autocomplete) { if (!config.geocoding.autocomplete) {
// If autocomplete is disabled, we put a larger debounce value // If autocomplete is disabled, we put a larger debounce value
// so that we don't request with incomplete address // so that we don't request with incomplete address
@ -104,18 +135,97 @@ export default class AddressAutoComplete extends Vue {
} }
@Watch("value") @Watch("value")
updateEditing() { updateEditing(): void {
if (!(this.value && this.value.id)) return; if (!(this.value && this.value.id)) return;
this.selected = this.value; this.selected = this.value;
const address = new Address(this.selected); const address = new Address(this.selected);
this.queryText = `${address.poiInfos.name} ${address.poiInfos.alternativeName}`; this.queryText = `${address.poiInfos.name} ${address.poiInfos.alternativeName}`;
} }
updateSelected(option: IAddress) { updateSelected(option: IAddress): void {
if (option == null) return; if (option == null) return;
this.selected = option; this.selected = option;
this.$emit("input", this.selected); this.$emit("input", this.selected);
} }
resetPopup(): void {
this.selected = new Address();
}
openNewAddressModal(): void {
this.resetPopup();
this.addressModalActive = true;
}
togglemap(): void {
this.showmap = !this.showmap;
}
async reverseGeoCode(e: LatLng, zoom: number): Promise<void> {
// If the position has been updated through autocomplete selection, no need to geocode it!
if (this.checkCurrentPosition(e)) return;
const result = await this.$apollo.query({
query: REVERSE_GEOCODE,
variables: {
latitude: e.lat,
longitude: e.lng,
zoom,
locale: this.$i18n.locale,
},
});
this.addressData = result.data.reverseGeocode.map((address: IAddress) => new Address(address));
const defaultAddress = new Address(this.addressData[0]);
this.selected = defaultAddress;
this.$emit("input", this.selected);
this.queryText = `${defaultAddress.poiInfos.name} ${defaultAddress.poiInfos.alternativeName}`;
}
checkCurrentPosition(e: LatLng): boolean {
if (!this.selected || !this.selected.geom) return false;
const lat = parseFloat(this.selected.geom.split(";")[1]);
const lon = parseFloat(this.selected.geom.split(";")[0]);
return e.lat === lat && e.lng === lon;
}
async locateMe(): Promise<void> {
this.gettingLocation = true;
try {
this.location = await AddressAutoComplete.getLocation();
this.mapDefaultZoom = 12;
this.reverseGeoCode(
new LatLng(this.location.coords.latitude, this.location.coords.longitude),
12
);
} catch (e) {
console.error(e);
this.gettingLocationError = e.message;
}
this.gettingLocation = false;
}
static async getLocation(): Promise<Position> {
return new Promise((resolve, reject) => {
if (!("geolocation" in navigator)) {
reject(new Error("Geolocation is not available."));
}
navigator.geolocation.getCurrentPosition(
(pos) => {
resolve(pos);
},
(err) => {
reject(err);
}
);
});
}
// eslint-disable-next-line class-methods-use-this
isSecureContext(): boolean {
return window.isSecureContext;
}
} }
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -376,6 +376,7 @@
"Update event {name}": "Update event {name}", "Update event {name}": "Update event {name}",
"Update my event": "Update my event", "Update my event": "Update my event",
"Updated": "Updated", "Updated": "Updated",
"Use my location": "Use my location",
"Username": "Username", "Username": "Username",
"Users": "Users", "Users": "Users",
"View a reply": "|View one reply|View {totalReplies} replies", "View a reply": "|View one reply|View {totalReplies} replies",

View File

@ -689,6 +689,7 @@
"Update my event": "Mettre à jour mon évènement", "Update my event": "Mettre à jour mon évènement",
"Update post": "Mettre à jour le billet", "Update post": "Mettre à jour le billet",
"Updated": "Mis à jour", "Updated": "Mis à jour",
"Use my location": "Utiliser ma position",
"User": "Utilisateur·ice", "User": "Utilisateur·ice",
"Username": "Pseudo", "Username": "Pseudo",
"Users": "Utilisateur⋅ice⋅s", "Users": "Utilisateur⋅ice⋅s",