@@ -44,10 +44,11 @@
|
||||
class="!mt-0"
|
||||
>
|
||||
<template #default="{ option }">
|
||||
<o-icon :icon="option.poiInfos.poiIcon.icon" />
|
||||
<b>{{ option.poiInfos.name }}</b
|
||||
><br />
|
||||
<small>{{ option.poiInfos.alternativeName }}</small>
|
||||
<p class="flex gap-1">
|
||||
<o-icon :icon="addressToPoiInfos(option).poiIcon.icon" />
|
||||
<b>{{ addressToPoiInfos(option).name }}</b>
|
||||
</p>
|
||||
<small>{{ addressToPoiInfos(option).alternativeName }}</small>
|
||||
</template>
|
||||
<template #empty>
|
||||
<span v-if="isFetching">{{ t("Searching…") }}</span>
|
||||
@@ -91,15 +92,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="map"
|
||||
v-if="!hideMap && selected && selected.geom && selected.poiInfos"
|
||||
>
|
||||
<div class="map" v-if="!hideMap && selected && selected.geom">
|
||||
<map-leaflet
|
||||
:coords="selected.geom"
|
||||
:marker="{
|
||||
text: [selected.poiInfos.name, selected.poiInfos.alternativeName],
|
||||
icon: selected.poiInfos.poiIcon.icon,
|
||||
text: [
|
||||
addressToPoiInfos(selected).name,
|
||||
addressToPoiInfos(selected).alternativeName,
|
||||
],
|
||||
icon: addressToPoiInfos(selected).poiIcon.icon,
|
||||
}"
|
||||
:updateDraggableMarkerCallback="reverseGeoCode"
|
||||
:options="{ zoom: mapDefaultZoom }"
|
||||
@@ -110,7 +111,13 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { LatLng } from "leaflet";
|
||||
import { Address, IAddress, addressFullName } from "../../types/address.model";
|
||||
import {
|
||||
Address,
|
||||
IAddress,
|
||||
addressFullName,
|
||||
addressToPoiInfos,
|
||||
IPoiInfo,
|
||||
} from "../../types/address.model";
|
||||
import AddressInfo from "../../components/Address/AddressInfo.vue";
|
||||
import { computed, ref, watch, defineAsyncComponent } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
@@ -118,6 +125,7 @@ import { useGeocodingAutocomplete } from "@/composition/apollo/config";
|
||||
import { ADDRESS } from "@/graphql/address";
|
||||
import { useReverseGeocode } from "@/composition/apollo/address";
|
||||
import { useLazyQuery } from "@vue/apollo-composable";
|
||||
import { AddressSearchType } from "@/types/enums";
|
||||
const MapLeaflet = defineAsyncComponent(
|
||||
() => import("@/components/LeafletMap.vue")
|
||||
);
|
||||
@@ -133,6 +141,7 @@ const props = withDefaults(
|
||||
hideMap?: boolean;
|
||||
hideSelected?: boolean;
|
||||
placeholder?: string;
|
||||
resultType?: AddressSearchType;
|
||||
}>(),
|
||||
{
|
||||
labelClass: "",
|
||||
@@ -227,12 +236,13 @@ const { onResult: onAddressSearchResult, load: searchAddress } = useLazyQuery<{
|
||||
onAddressSearchResult((result) => {
|
||||
if (result.loading) return;
|
||||
const { data } = result;
|
||||
addressData.value = data.searchAddress.map(
|
||||
(address: IAddress) => new Address(address)
|
||||
);
|
||||
console.debug("onAddressSearchResult", data.searchAddress);
|
||||
addressData.value = data.searchAddress;
|
||||
isFetching.value = false;
|
||||
});
|
||||
|
||||
const searchQuery = ref("");
|
||||
|
||||
const asyncData = async (query: string): Promise<void> => {
|
||||
if (!query.length) {
|
||||
addressData.value = [];
|
||||
@@ -247,9 +257,12 @@ const asyncData = async (query: string): Promise<void> => {
|
||||
|
||||
isFetching.value = true;
|
||||
|
||||
searchQuery.value = query;
|
||||
|
||||
searchAddress(undefined, {
|
||||
query,
|
||||
query: searchQuery.value,
|
||||
locale: locale.value,
|
||||
type: props.resultType,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -295,12 +308,10 @@ const { onResult: onReverseGeocodeResult, load: loadReverseGeocode } =
|
||||
onReverseGeocodeResult((result) => {
|
||||
if (result.loading !== false) return;
|
||||
const { data } = result;
|
||||
addressData.value = data.reverseGeocode.map(
|
||||
(elem: IAddress) => new Address(elem)
|
||||
);
|
||||
addressData.value = data.reverseGeocode;
|
||||
|
||||
if (addressData.value.length > 0) {
|
||||
const defaultAddress = new Address(addressData.value[0]);
|
||||
const defaultAddress = addressData.value[0];
|
||||
selected.value = defaultAddress;
|
||||
emit("update:modelValue", selected.value);
|
||||
}
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
maxlength="1024"
|
||||
/>
|
||||
<full-address-auto-complete
|
||||
:type="AddressSearchType.ADMINISTRATIVE"
|
||||
:resultType="AddressSearchType.ADMINISTRATIVE"
|
||||
:doGeoLocation="false"
|
||||
v-model="location"
|
||||
:hide-map="true"
|
||||
:hide-selected="true"
|
||||
:default-text="locationDefaultText"
|
||||
labelClass="sr-only"
|
||||
:placeholder="t('e.g. Nantes, Berlin, Cork, …')"
|
||||
/>
|
||||
<o-button native-type="submit" icon-left="magnify">
|
||||
<template v-if="search">{{ t("Go!") }}</template>
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
<slot name="title" />
|
||||
</h2>
|
||||
|
||||
<button
|
||||
<o-button
|
||||
:disabled="doingGeoloc"
|
||||
v-if="suggestGeoloc"
|
||||
class="inline-flex bg-primary rounded text-white flex-initial px-4 py-2 justify-center w-full md:w-min whitespace-nowrap"
|
||||
@click="emit('doGeoLoc')"
|
||||
>
|
||||
{{ t("Geolocate me") }}
|
||||
</button>
|
||||
</o-button>
|
||||
</div>
|
||||
<slot name="subtitle" />
|
||||
</div>
|
||||
@@ -53,8 +54,9 @@ import { useI18n } from "vue-i18n";
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
suggestGeoloc?: boolean;
|
||||
doingGeoloc?: boolean;
|
||||
}>(),
|
||||
{ suggestGeoloc: true }
|
||||
{ suggestGeoloc: true, doingGeoloc: false }
|
||||
);
|
||||
|
||||
const emit = defineEmits(["doGeoLoc"]);
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<template>
|
||||
<close-content
|
||||
class="container mx-auto px-2"
|
||||
v-show="loadingEvents || (events && events.total > 0)"
|
||||
v-show="loading || (events && events.total > 0)"
|
||||
:suggestGeoloc="suggestGeoloc"
|
||||
v-on="attrs"
|
||||
@doGeoLoc="emit('doGeoLoc')"
|
||||
:doingGeoloc="doingGeoloc"
|
||||
>
|
||||
<template #title>
|
||||
<template v-if="userLocationName">
|
||||
@@ -19,7 +20,7 @@
|
||||
v-for="i in 6"
|
||||
class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4"
|
||||
:key="i"
|
||||
v-show="loadingEvents"
|
||||
v-show="loading"
|
||||
/>
|
||||
<event-card
|
||||
v-for="event in events.elements"
|
||||
@@ -35,7 +36,7 @@
|
||||
lat: userLocation.lat?.toString(),
|
||||
lon: userLocation.lon?.toString(),
|
||||
contentType: 'EVENTS',
|
||||
distance: '25_km',
|
||||
distance: `${distance}_km`,
|
||||
},
|
||||
}"
|
||||
:picture="userLocation?.picture"
|
||||
@@ -54,10 +55,10 @@
|
||||
import { LocationType } from "../../types/user-location.model";
|
||||
import MoreContent from "./MoreContent.vue";
|
||||
import CloseContent from "./CloseContent.vue";
|
||||
import { computed, useAttrs } from "vue";
|
||||
import { computed, onMounted, ref, useAttrs } from "vue";
|
||||
import { SEARCH_EVENTS } from "@/graphql/search";
|
||||
import { IEvent } from "@/types/event.model";
|
||||
import { useQuery } from "@vue/apollo-composable";
|
||||
import { useLazyQuery } from "@vue/apollo-composable";
|
||||
import EventCard from "../Event/EventCard.vue";
|
||||
import { Paginate } from "@/types/paginate";
|
||||
import SkeletonEventResult from "../Event/SkeletonEventResult.vue";
|
||||
@@ -66,7 +67,10 @@ import { coordsToGeoHash } from "@/utils/location";
|
||||
import { roundToNearestMinute } from "@/utils/datetime";
|
||||
import RouteName from "@/router/name";
|
||||
|
||||
const props = defineProps<{ userLocation: LocationType }>();
|
||||
const props = defineProps<{
|
||||
userLocation: LocationType;
|
||||
doingGeoloc?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits(["doGeoLoc"]);
|
||||
|
||||
const EVENT_PAGE_LIMIT = 12;
|
||||
@@ -74,34 +78,54 @@ const EVENT_PAGE_LIMIT = 12;
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
const attrs = useAttrs();
|
||||
|
||||
const userLocation = computed(() => props.userLocation);
|
||||
|
||||
const userLocationName = computed(() => {
|
||||
return props.userLocation?.name;
|
||||
return userLocation.value?.name;
|
||||
});
|
||||
const suggestGeoloc = computed(() => props.userLocation?.isIPLocation);
|
||||
const suggestGeoloc = computed(() => userLocation.value?.isIPLocation);
|
||||
|
||||
const geoHash = computed(() =>
|
||||
coordsToGeoHash(props.userLocation.lat, props.userLocation.lon)
|
||||
);
|
||||
|
||||
const { result: eventsResult, loading: loadingEvents } = useQuery<{
|
||||
const distance = computed<number>(() => (suggestGeoloc.value ? 150 : 25));
|
||||
|
||||
const now = computed(() => roundToNearestMinute(new Date()));
|
||||
|
||||
const searchEnabled = computed(() => geoHash.value != undefined);
|
||||
const enabled = ref(false);
|
||||
|
||||
const {
|
||||
result: eventsResult,
|
||||
loading: loadingEvents,
|
||||
load: load,
|
||||
} = useLazyQuery<{
|
||||
searchEvents: Paginate<IEvent>;
|
||||
}>(
|
||||
SEARCH_EVENTS,
|
||||
() => ({
|
||||
location: geoHash.value,
|
||||
beginsOn: roundToNearestMinute(new Date()),
|
||||
beginsOn: now.value,
|
||||
endsOn: undefined,
|
||||
radius: 25,
|
||||
radius: distance.value,
|
||||
eventPage: 1,
|
||||
limit: EVENT_PAGE_LIMIT,
|
||||
type: "IN_PERSON",
|
||||
}),
|
||||
() => ({
|
||||
enabled: geoHash.value !== undefined,
|
||||
enabled: searchEnabled.value,
|
||||
fetchPolicy: "cache-first",
|
||||
})
|
||||
);
|
||||
|
||||
const events = computed(
|
||||
() => eventsResult.value?.searchEvents ?? { elements: [], total: 0 }
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
load();
|
||||
});
|
||||
|
||||
const loading = computed(() => props.doingGeoloc || loadingEvents.value);
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<template>
|
||||
<close-content
|
||||
class="container mx-auto px-2"
|
||||
v-show="loadingGroups || selectedGroups.length > 0"
|
||||
v-show="loading || selectedGroups.length > 0"
|
||||
@do-geo-loc="emit('doGeoLoc')"
|
||||
:suggestGeoloc="userLocation.isIPLocation"
|
||||
:doingGeoloc="doingGeoloc"
|
||||
>
|
||||
<template #title>
|
||||
<template v-if="userLocationName">
|
||||
@@ -22,7 +23,7 @@
|
||||
v-for="i in [...Array(6).keys()]"
|
||||
class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4"
|
||||
:key="i"
|
||||
v-show="loadingGroups"
|
||||
v-show="loading"
|
||||
/>
|
||||
<group-card
|
||||
v-for="group in selectedGroups"
|
||||
@@ -43,7 +44,7 @@
|
||||
lat: userLocation.lat?.toString(),
|
||||
lon: userLocation.lon?.toString(),
|
||||
contentType: 'GROUPS',
|
||||
distance: '25_km',
|
||||
distance: `${distance}_km`,
|
||||
},
|
||||
}"
|
||||
:picture="userLocation.picture"
|
||||
@@ -74,22 +75,35 @@ import { coordsToGeoHash } from "@/utils/location";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import RouteName from "@/router/name";
|
||||
|
||||
const props = defineProps<{ userLocation: LocationType }>();
|
||||
const props = defineProps<{
|
||||
userLocation: LocationType;
|
||||
doingGeoloc?: boolean;
|
||||
}>();
|
||||
const emit = defineEmits(["doGeoLoc"]);
|
||||
|
||||
const { t } = useI18n({ useScope: "global" });
|
||||
|
||||
const userLocation = computed(() => props.userLocation);
|
||||
|
||||
const geoHash = computed(() =>
|
||||
coordsToGeoHash(userLocation.value.lat, userLocation.value.lon)
|
||||
);
|
||||
|
||||
const distance = computed<number>(() =>
|
||||
userLocation.value?.isIPLocation ? 150 : 25
|
||||
);
|
||||
|
||||
const { result: groupsResult, loading: loadingGroups } = useQuery<{
|
||||
searchGroups: Paginate<IGroup>;
|
||||
}>(
|
||||
SEARCH_GROUPS,
|
||||
() => ({
|
||||
location: coordsToGeoHash(props.userLocation.lat, props.userLocation.lon),
|
||||
radius: 25,
|
||||
location: geoHash.value,
|
||||
radius: distance.value,
|
||||
page: 1,
|
||||
limit: 12,
|
||||
}),
|
||||
() => ({ enabled: props.userLocation?.lat !== undefined })
|
||||
() => ({ enabled: geoHash.value !== undefined })
|
||||
);
|
||||
|
||||
const groups = computed(
|
||||
@@ -99,4 +113,6 @@ const groups = computed(
|
||||
const selectedGroups = computed(() => sampleSize(groups.value?.elements, 5));
|
||||
|
||||
const userLocationName = computed(() => props?.userLocation?.name);
|
||||
|
||||
const loading = computed(() => props.doingGeoloc || loadingGroups.value);
|
||||
</script>
|
||||
|
||||
@@ -3,8 +3,14 @@
|
||||
class="bg-white border-gray-200 px-2 sm:px-4 py-2.5 dark:bg-zinc-900"
|
||||
id="navbar"
|
||||
>
|
||||
<div class="container mx-auto flex flex-wrap items-center mx-auto gap-4">
|
||||
<router-link :to="{ name: RouteName.HOME }" class="flex items-center">
|
||||
<div
|
||||
class="container mx-auto flex flex-wrap items-center mx-auto gap-2 sm:gap-4"
|
||||
>
|
||||
<router-link
|
||||
:to="{ name: RouteName.HOME }"
|
||||
class="flex items-center"
|
||||
:class="{ 'flex-1': !currentActor?.id }"
|
||||
>
|
||||
<MobilizonLogo class="w-40" />
|
||||
</router-link>
|
||||
<div class="flex items-center md:order-2 ml-auto" v-if="currentActor?.id">
|
||||
@@ -12,7 +18,7 @@
|
||||
<template #trigger>
|
||||
<button
|
||||
type="button"
|
||||
class="flex mr-3 text-sm rounded-full md:mr-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||
class="flex sm:mr-3 text-sm rounded-full md:mr-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
|
||||
id="user-menu-button"
|
||||
aria-expanded="false"
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user