Merge branch 'improve-home' into 'main'

Fix address selector

See merge request framasoft/mobilizon!1277
This commit is contained in:
Thomas Citharel 2022-10-05 16:47:06 +00:00
commit d665abcc5d
21 changed files with 895 additions and 736 deletions

View File

@ -53,7 +53,7 @@
"@vue/apollo-composable": "^4.0.0-alpha.17", "@vue/apollo-composable": "^4.0.0-alpha.17",
"@vue/compiler-sfc": "^3.2.37", "@vue/compiler-sfc": "^3.2.37",
"@vueuse/core": "^9.1.0", "@vueuse/core": "^9.1.0",
"@vueuse/head": "^0.7.9", "@vueuse/head": "^0.9.6",
"@vueuse/router": "^9.0.2", "@vueuse/router": "^9.0.2",
"apollo-absinthe-upload-link": "^1.5.0", "apollo-absinthe-upload-link": "^1.5.0",
"autoprefixer": "^10", "autoprefixer": "^10",
@ -93,7 +93,7 @@
"vuedraggable": "^4.1.0" "vuedraggable": "^4.1.0"
}, },
"devDependencies": { "devDependencies": {
"@histoire/plugin-vue": "^0.10.0", "@histoire/plugin-vue": "^0.11.0",
"@playwright/test": "^1.25.1", "@playwright/test": "^1.25.1",
"@rushstack/eslint-patch": "^1.1.4", "@rushstack/eslint-patch": "^1.1.4",
"@tailwindcss/forms": "^0.5.2", "@tailwindcss/forms": "^0.5.2",
@ -122,7 +122,7 @@
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^9.3.0", "eslint-plugin-vue": "^9.3.0",
"flush-promises": "^1.0.2", "flush-promises": "^1.0.2",
"histoire": "^0.10.4", "histoire": "^0.11.0",
"jsdom": "^20.0.0", "jsdom": "^20.0.0",
"mock-apollo-client": "^1.1.0", "mock-apollo-client": "^1.1.0",
"prettier": "^2.2.1", "prettier": "^2.2.1",

View File

@ -69,7 +69,11 @@ import { IConfig } from "@/types/config.model";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
const { result: configResult } = useQuery<{ config: IConfig }>(CONFIG); const { result: configResult } = useQuery<{ config: IConfig }>(
CONFIG,
undefined,
{ fetchPolicy: "cache-only" }
);
const config = computed(() => configResult.value?.config); const config = computed(() => configResult.value?.config);

View File

@ -74,7 +74,7 @@ export const typePolicies: TypePolicies = {
merge: true, merge: true,
}, },
Address: { Address: {
keyFields: ["id"], keyFields: ["id", "originId"],
}, },
RootQueryType: { RootQueryType: {
fields: { fields: {

View File

@ -114,7 +114,7 @@ body {
} }
.autocomplete-item { .autocomplete-item {
@apply py-1.5 px-4; @apply py-1.5 px-4 text-start;
} }
/* Dropdown */ /* Dropdown */

View File

@ -44,10 +44,11 @@
class="!mt-0" class="!mt-0"
> >
<template #default="{ option }"> <template #default="{ option }">
<o-icon :icon="option.poiInfos.poiIcon.icon" /> <p class="flex gap-1">
<b>{{ option.poiInfos.name }}</b <o-icon :icon="addressToPoiInfos(option).poiIcon.icon" />
><br /> <b>{{ addressToPoiInfos(option).name }}</b>
<small>{{ option.poiInfos.alternativeName }}</small> </p>
<small>{{ addressToPoiInfos(option).alternativeName }}</small>
</template> </template>
<template #empty> <template #empty>
<span v-if="isFetching">{{ t("Searching") }}</span> <span v-if="isFetching">{{ t("Searching") }}</span>
@ -91,15 +92,15 @@
</div> </div>
</div> </div>
</div> </div>
<div <div class="map" v-if="!hideMap && selected && selected.geom">
class="map"
v-if="!hideMap && selected && selected.geom && selected.poiInfos"
>
<map-leaflet <map-leaflet
:coords="selected.geom" :coords="selected.geom"
:marker="{ :marker="{
text: [selected.poiInfos.name, selected.poiInfos.alternativeName], text: [
icon: selected.poiInfos.poiIcon.icon, addressToPoiInfos(selected).name,
addressToPoiInfos(selected).alternativeName,
],
icon: addressToPoiInfos(selected).poiIcon.icon,
}" }"
:updateDraggableMarkerCallback="reverseGeoCode" :updateDraggableMarkerCallback="reverseGeoCode"
:options="{ zoom: mapDefaultZoom }" :options="{ zoom: mapDefaultZoom }"
@ -110,7 +111,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { LatLng } from "leaflet"; 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 AddressInfo from "../../components/Address/AddressInfo.vue";
import { computed, ref, watch, defineAsyncComponent } from "vue"; import { computed, ref, watch, defineAsyncComponent } from "vue";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
@ -118,6 +125,7 @@ import { useGeocodingAutocomplete } from "@/composition/apollo/config";
import { ADDRESS } from "@/graphql/address"; import { ADDRESS } from "@/graphql/address";
import { useReverseGeocode } from "@/composition/apollo/address"; import { useReverseGeocode } from "@/composition/apollo/address";
import { useLazyQuery } from "@vue/apollo-composable"; import { useLazyQuery } from "@vue/apollo-composable";
import { AddressSearchType } from "@/types/enums";
const MapLeaflet = defineAsyncComponent( const MapLeaflet = defineAsyncComponent(
() => import("@/components/LeafletMap.vue") () => import("@/components/LeafletMap.vue")
); );
@ -133,6 +141,7 @@ const props = withDefaults(
hideMap?: boolean; hideMap?: boolean;
hideSelected?: boolean; hideSelected?: boolean;
placeholder?: string; placeholder?: string;
resultType?: AddressSearchType;
}>(), }>(),
{ {
labelClass: "", labelClass: "",
@ -227,12 +236,13 @@ const { onResult: onAddressSearchResult, load: searchAddress } = useLazyQuery<{
onAddressSearchResult((result) => { onAddressSearchResult((result) => {
if (result.loading) return; if (result.loading) return;
const { data } = result; const { data } = result;
addressData.value = data.searchAddress.map( console.debug("onAddressSearchResult", data.searchAddress);
(address: IAddress) => new Address(address) addressData.value = data.searchAddress;
);
isFetching.value = false; isFetching.value = false;
}); });
const searchQuery = ref("");
const asyncData = async (query: string): Promise<void> => { const asyncData = async (query: string): Promise<void> => {
if (!query.length) { if (!query.length) {
addressData.value = []; addressData.value = [];
@ -247,9 +257,12 @@ const asyncData = async (query: string): Promise<void> => {
isFetching.value = true; isFetching.value = true;
searchQuery.value = query;
searchAddress(undefined, { searchAddress(undefined, {
query, query: searchQuery.value,
locale: locale.value, locale: locale.value,
type: props.resultType,
}); });
}; };
@ -295,12 +308,10 @@ const { onResult: onReverseGeocodeResult, load: loadReverseGeocode } =
onReverseGeocodeResult((result) => { onReverseGeocodeResult((result) => {
if (result.loading !== false) return; if (result.loading !== false) return;
const { data } = result; const { data } = result;
addressData.value = data.reverseGeocode.map( addressData.value = data.reverseGeocode;
(elem: IAddress) => new Address(elem)
);
if (addressData.value.length > 0) { if (addressData.value.length > 0) {
const defaultAddress = new Address(addressData.value[0]); const defaultAddress = addressData.value[0];
selected.value = defaultAddress; selected.value = defaultAddress;
emit("update:modelValue", selected.value); emit("update:modelValue", selected.value);
} }

View File

@ -19,13 +19,14 @@
maxlength="1024" maxlength="1024"
/> />
<full-address-auto-complete <full-address-auto-complete
:type="AddressSearchType.ADMINISTRATIVE" :resultType="AddressSearchType.ADMINISTRATIVE"
:doGeoLocation="false" :doGeoLocation="false"
v-model="location" v-model="location"
:hide-map="true" :hide-map="true"
:hide-selected="true" :hide-selected="true"
:default-text="locationDefaultText" :default-text="locationDefaultText"
labelClass="sr-only" labelClass="sr-only"
:placeholder="t('e.g. Nantes, Berlin, Cork, …')"
/> />
<o-button native-type="submit" icon-left="magnify"> <o-button native-type="submit" icon-left="magnify">
<template v-if="search">{{ t("Go!") }}</template> <template v-if="search">{{ t("Go!") }}</template>

View File

@ -8,13 +8,14 @@
<slot name="title" /> <slot name="title" />
</h2> </h2>
<button <o-button
:disabled="doingGeoloc"
v-if="suggestGeoloc" 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" 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')" @click="emit('doGeoLoc')"
> >
{{ t("Geolocate me") }} {{ t("Geolocate me") }}
</button> </o-button>
</div> </div>
<slot name="subtitle" /> <slot name="subtitle" />
</div> </div>
@ -53,8 +54,9 @@ import { useI18n } from "vue-i18n";
withDefaults( withDefaults(
defineProps<{ defineProps<{
suggestGeoloc?: boolean; suggestGeoloc?: boolean;
doingGeoloc?: boolean;
}>(), }>(),
{ suggestGeoloc: true } { suggestGeoloc: true, doingGeoloc: false }
); );
const emit = defineEmits(["doGeoLoc"]); const emit = defineEmits(["doGeoLoc"]);

View File

@ -1,10 +1,11 @@
<template> <template>
<close-content <close-content
class="container mx-auto px-2" class="container mx-auto px-2"
v-show="loadingEvents || (events && events.total > 0)" v-show="loading || (events && events.total > 0)"
:suggestGeoloc="suggestGeoloc" :suggestGeoloc="suggestGeoloc"
v-on="attrs" v-on="attrs"
@doGeoLoc="emit('doGeoLoc')" @doGeoLoc="emit('doGeoLoc')"
:doingGeoloc="doingGeoloc"
> >
<template #title> <template #title>
<template v-if="userLocationName"> <template v-if="userLocationName">
@ -19,7 +20,7 @@
v-for="i in 6" v-for="i in 6"
class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4" class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4"
:key="i" :key="i"
v-show="loadingEvents" v-show="loading"
/> />
<event-card <event-card
v-for="event in events.elements" v-for="event in events.elements"
@ -35,7 +36,7 @@
lat: userLocation.lat?.toString(), lat: userLocation.lat?.toString(),
lon: userLocation.lon?.toString(), lon: userLocation.lon?.toString(),
contentType: 'EVENTS', contentType: 'EVENTS',
distance: '25_km', distance: `${distance}_km`,
}, },
}" }"
:picture="userLocation?.picture" :picture="userLocation?.picture"
@ -54,10 +55,10 @@
import { LocationType } from "../../types/user-location.model"; import { LocationType } from "../../types/user-location.model";
import MoreContent from "./MoreContent.vue"; import MoreContent from "./MoreContent.vue";
import CloseContent from "./CloseContent.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 { SEARCH_EVENTS } from "@/graphql/search";
import { IEvent } from "@/types/event.model"; 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 EventCard from "../Event/EventCard.vue";
import { Paginate } from "@/types/paginate"; import { Paginate } from "@/types/paginate";
import SkeletonEventResult from "../Event/SkeletonEventResult.vue"; import SkeletonEventResult from "../Event/SkeletonEventResult.vue";
@ -66,7 +67,10 @@ import { coordsToGeoHash } from "@/utils/location";
import { roundToNearestMinute } from "@/utils/datetime"; import { roundToNearestMinute } from "@/utils/datetime";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
const props = defineProps<{ userLocation: LocationType }>(); const props = defineProps<{
userLocation: LocationType;
doingGeoloc?: boolean;
}>();
const emit = defineEmits(["doGeoLoc"]); const emit = defineEmits(["doGeoLoc"]);
const EVENT_PAGE_LIMIT = 12; const EVENT_PAGE_LIMIT = 12;
@ -74,34 +78,54 @@ const EVENT_PAGE_LIMIT = 12;
const { t } = useI18n({ useScope: "global" }); const { t } = useI18n({ useScope: "global" });
const attrs = useAttrs(); const attrs = useAttrs();
const userLocation = computed(() => props.userLocation);
const userLocationName = computed(() => { 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(() => const geoHash = computed(() =>
coordsToGeoHash(props.userLocation.lat, props.userLocation.lon) 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>; searchEvents: Paginate<IEvent>;
}>( }>(
SEARCH_EVENTS, SEARCH_EVENTS,
() => ({ () => ({
location: geoHash.value, location: geoHash.value,
beginsOn: roundToNearestMinute(new Date()), beginsOn: now.value,
endsOn: undefined, endsOn: undefined,
radius: 25, radius: distance.value,
eventPage: 1, eventPage: 1,
limit: EVENT_PAGE_LIMIT, limit: EVENT_PAGE_LIMIT,
type: "IN_PERSON", type: "IN_PERSON",
}), }),
() => ({ () => ({
enabled: geoHash.value !== undefined, enabled: searchEnabled.value,
fetchPolicy: "cache-first",
}) })
); );
const events = computed( const events = computed(
() => eventsResult.value?.searchEvents ?? { elements: [], total: 0 } () => eventsResult.value?.searchEvents ?? { elements: [], total: 0 }
); );
onMounted(() => {
load();
});
const loading = computed(() => props.doingGeoloc || loadingEvents.value);
</script> </script>

View File

@ -1,9 +1,10 @@
<template> <template>
<close-content <close-content
class="container mx-auto px-2" class="container mx-auto px-2"
v-show="loadingGroups || selectedGroups.length > 0" v-show="loading || selectedGroups.length > 0"
@do-geo-loc="emit('doGeoLoc')" @do-geo-loc="emit('doGeoLoc')"
:suggestGeoloc="userLocation.isIPLocation" :suggestGeoloc="userLocation.isIPLocation"
:doingGeoloc="doingGeoloc"
> >
<template #title> <template #title>
<template v-if="userLocationName"> <template v-if="userLocationName">
@ -22,7 +23,7 @@
v-for="i in [...Array(6).keys()]" v-for="i in [...Array(6).keys()]"
class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4" class="scroll-ml-6 snap-center shrink-0 w-[18rem] my-4"
:key="i" :key="i"
v-show="loadingGroups" v-show="loading"
/> />
<group-card <group-card
v-for="group in selectedGroups" v-for="group in selectedGroups"
@ -43,7 +44,7 @@
lat: userLocation.lat?.toString(), lat: userLocation.lat?.toString(),
lon: userLocation.lon?.toString(), lon: userLocation.lon?.toString(),
contentType: 'GROUPS', contentType: 'GROUPS',
distance: '25_km', distance: `${distance}_km`,
}, },
}" }"
:picture="userLocation.picture" :picture="userLocation.picture"
@ -74,22 +75,35 @@ import { coordsToGeoHash } from "@/utils/location";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import RouteName from "@/router/name"; import RouteName from "@/router/name";
const props = defineProps<{ userLocation: LocationType }>(); const props = defineProps<{
userLocation: LocationType;
doingGeoloc?: boolean;
}>();
const emit = defineEmits(["doGeoLoc"]); const emit = defineEmits(["doGeoLoc"]);
const { t } = useI18n({ useScope: "global" }); 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<{ const { result: groupsResult, loading: loadingGroups } = useQuery<{
searchGroups: Paginate<IGroup>; searchGroups: Paginate<IGroup>;
}>( }>(
SEARCH_GROUPS, SEARCH_GROUPS,
() => ({ () => ({
location: coordsToGeoHash(props.userLocation.lat, props.userLocation.lon), location: geoHash.value,
radius: 25, radius: distance.value,
page: 1, page: 1,
limit: 12, limit: 12,
}), }),
() => ({ enabled: props.userLocation?.lat !== undefined }) () => ({ enabled: geoHash.value !== undefined })
); );
const groups = computed( const groups = computed(
@ -99,4 +113,6 @@ const groups = computed(
const selectedGroups = computed(() => sampleSize(groups.value?.elements, 5)); const selectedGroups = computed(() => sampleSize(groups.value?.elements, 5));
const userLocationName = computed(() => props?.userLocation?.name); const userLocationName = computed(() => props?.userLocation?.name);
const loading = computed(() => props.doingGeoloc || loadingGroups.value);
</script> </script>

View File

@ -3,8 +3,14 @@
class="bg-white border-gray-200 px-2 sm:px-4 py-2.5 dark:bg-zinc-900" class="bg-white border-gray-200 px-2 sm:px-4 py-2.5 dark:bg-zinc-900"
id="navbar" id="navbar"
> >
<div class="container mx-auto flex flex-wrap items-center mx-auto gap-4"> <div
<router-link :to="{ name: RouteName.HOME }" class="flex items-center"> 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" /> <MobilizonLogo class="w-40" />
</router-link> </router-link>
<div class="flex items-center md:order-2 ml-auto" v-if="currentActor?.id"> <div class="flex items-center md:order-2 ml-auto" v-if="currentActor?.id">
@ -12,7 +18,7 @@
<template #trigger> <template #trigger>
<button <button
type="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" id="user-menu-button"
aria-expanded="false" aria-expanded="false"
> >

View File

@ -29,7 +29,7 @@ export function useTimezones() {
loading, loading,
} = useQuery<{ } = useQuery<{
config: Pick<IConfig, "timezones">; config: Pick<IConfig, "timezones">;
}>(TIMEZONES); }>(TIMEZONES, undefined, { fetchPolicy: "cache-first" });
const timezones = computed(() => timezoneResult.value?.config?.timezones); const timezones = computed(() => timezoneResult.value?.config?.timezones);
return { timezones, error, loading }; return { timezones, error, loading };
@ -42,7 +42,7 @@ export function useAnonymousParticipationConfig() {
loading, loading,
} = useQuery<{ } = useQuery<{
config: Pick<IConfig, "anonymous">; config: Pick<IConfig, "anonymous">;
}>(ANONYMOUS_PARTICIPATION_CONFIG); }>(ANONYMOUS_PARTICIPATION_CONFIG, undefined, { fetchPolicy: "cache-only" });
const anonymousParticipationConfig = computed( const anonymousParticipationConfig = computed(
() => configResult.value?.config?.anonymous?.participation () => configResult.value?.config?.anonymous?.participation
@ -58,7 +58,7 @@ export function useAnonymousReportsConfig() {
loading, loading,
} = useQuery<{ } = useQuery<{
config: Pick<IConfig, "anonymous">; config: Pick<IConfig, "anonymous">;
}>(ANONYMOUS_REPORTS_CONFIG); }>(ANONYMOUS_REPORTS_CONFIG, undefined, { fetchPolicy: "cache-only" });
const anonymousReportsConfig = computed( const anonymousReportsConfig = computed(
() => configResult.value?.config?.anonymous?.participation () => configResult.value?.config?.anonymous?.participation
@ -69,7 +69,7 @@ export function useAnonymousReportsConfig() {
export function useInstanceName() { export function useInstanceName() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "name">; config: Pick<IConfig, "name">;
}>(ABOUT); }>(ABOUT, undefined, { fetchPolicy: "cache-only" });
const instanceName = computed(() => result.value?.config?.name); const instanceName = computed(() => result.value?.config?.name);
return { instanceName, error, loading }; return { instanceName, error, loading };
@ -78,7 +78,7 @@ export function useInstanceName() {
export function useAnonymousActorId() { export function useAnonymousActorId() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "anonymous">; config: Pick<IConfig, "anonymous">;
}>(ANONYMOUS_ACTOR_ID); }>(ANONYMOUS_ACTOR_ID, undefined, { fetchPolicy: "cache-only" });
const anonymousActorId = computed( const anonymousActorId = computed(
() => result.value?.config?.anonymous?.actorId () => result.value?.config?.anonymous?.actorId
@ -89,7 +89,7 @@ export function useAnonymousActorId() {
export function useUploadLimits() { export function useUploadLimits() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "uploadLimits">; config: Pick<IConfig, "uploadLimits">;
}>(UPLOAD_LIMITS); }>(UPLOAD_LIMITS, undefined, { fetchPolicy: "cache-only" });
const uploadLimits = computed(() => result.value?.config?.uploadLimits); const uploadLimits = computed(() => result.value?.config?.uploadLimits);
return { uploadLimits, error, loading }; return { uploadLimits, error, loading };
@ -98,7 +98,7 @@ export function useUploadLimits() {
export function useEventCategories() { export function useEventCategories() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "eventCategories">; config: Pick<IConfig, "eventCategories">;
}>(EVENT_CATEGORIES); }>(EVENT_CATEGORIES, undefined, { fetchPolicy: "cache-only" });
const eventCategories = computed(() => result.value?.config.eventCategories); const eventCategories = computed(() => result.value?.config.eventCategories);
return { eventCategories, error, loading }; return { eventCategories, error, loading };
@ -107,7 +107,7 @@ export function useEventCategories() {
export function useRestrictions() { export function useRestrictions() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "restrictions">; config: Pick<IConfig, "restrictions">;
}>(RESTRICTIONS); }>(RESTRICTIONS, undefined, { fetchPolicy: "cache-only" });
const restrictions = computed(() => result.value?.config.restrictions); const restrictions = computed(() => result.value?.config.restrictions);
return { restrictions, error, loading }; return { restrictions, error, loading };
@ -116,7 +116,7 @@ export function useRestrictions() {
export function useExportFormats() { export function useExportFormats() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "exportFormats">; config: Pick<IConfig, "exportFormats">;
}>(EVENT_PARTICIPANTS); }>(EVENT_PARTICIPANTS, undefined, { fetchPolicy: "cache-only" });
const exportFormats = computed(() => result.value?.config?.exportFormats); const exportFormats = computed(() => result.value?.config?.exportFormats);
return { exportFormats, error, loading }; return { exportFormats, error, loading };
} }
@ -124,7 +124,7 @@ export function useExportFormats() {
export function useGeocodingAutocomplete() { export function useGeocodingAutocomplete() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "geocoding">; config: Pick<IConfig, "geocoding">;
}>(GEOCODING_AUTOCOMPLETE); }>(GEOCODING_AUTOCOMPLETE, undefined, { fetchPolicy: "cache-only" });
const geocodingAutocomplete = computed( const geocodingAutocomplete = computed(
() => result.value?.config?.geocoding?.autocomplete () => result.value?.config?.geocoding?.autocomplete
); );
@ -134,7 +134,7 @@ export function useGeocodingAutocomplete() {
export function useMapTiles() { export function useMapTiles() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "maps">; config: Pick<IConfig, "maps">;
}>(MAPS_TILES); }>(MAPS_TILES, undefined, { fetchPolicy: "cache-only" });
const tiles = computed(() => result.value?.config.maps.tiles); const tiles = computed(() => result.value?.config.maps.tiles);
return { tiles, error, loading }; return { tiles, error, loading };
@ -143,7 +143,7 @@ export function useMapTiles() {
export function useRoutingType() { export function useRoutingType() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "maps">; config: Pick<IConfig, "maps">;
}>(ROUTING_TYPE); }>(ROUTING_TYPE, undefined, { fetchPolicy: "cache-only" });
const routingType = computed(() => result.value?.config.maps.routing.type); const routingType = computed(() => result.value?.config.maps.routing.type);
return { routingType, error, loading }; return { routingType, error, loading };
@ -152,7 +152,7 @@ export function useRoutingType() {
export function useFeatures() { export function useFeatures() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "features">; config: Pick<IConfig, "features">;
}>(FEATURES); }>(FEATURES, undefined, { fetchPolicy: "cache-only" });
const features = computed(() => result.value?.config.features); const features = computed(() => result.value?.config.features);
return { features, error, loading }; return { features, error, loading };
@ -161,7 +161,7 @@ export function useFeatures() {
export function useResourceProviders() { export function useResourceProviders() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "resourceProviders">; config: Pick<IConfig, "resourceProviders">;
}>(RESOURCE_PROVIDERS); }>(RESOURCE_PROVIDERS, undefined, { fetchPolicy: "cache-only" });
const resourceProviders = computed( const resourceProviders = computed(
() => result.value?.config.resourceProviders () => result.value?.config.resourceProviders
@ -172,7 +172,7 @@ export function useResourceProviders() {
export function useServerProvidedLocation() { export function useServerProvidedLocation() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "location">; config: Pick<IConfig, "location">;
}>(LOCATION); }>(LOCATION, undefined, { fetchPolicy: "cache-only" });
const location = computed(() => result.value?.config.location); const location = computed(() => result.value?.config.location);
return { location, error, loading }; return { location, error, loading };
@ -181,7 +181,7 @@ export function useServerProvidedLocation() {
export function useIsDemoMode() { export function useIsDemoMode() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "demoMode">; config: Pick<IConfig, "demoMode">;
}>(DEMO_MODE); }>(DEMO_MODE, undefined, { fetchPolicy: "cache-only" });
const isDemoMode = computed(() => result.value?.config.demoMode); const isDemoMode = computed(() => result.value?.config.demoMode);
return { isDemoMode, error, loading }; return { isDemoMode, error, loading };
@ -190,7 +190,7 @@ export function useIsDemoMode() {
export function useAnalytics() { export function useAnalytics() {
const { result, error, loading } = useQuery<{ const { result, error, loading } = useQuery<{
config: Pick<IConfig, "analytics">; config: Pick<IConfig, "analytics">;
}>(ANALYTICS); }>(ANALYTICS, undefined, { fetchPolicy: "cache-only" });
const analytics = computed(() => result.value?.config.analytics); const analytics = computed(() => result.value?.config.analytics);
return { analytics, error, loading }; return { analytics, error, loading };
@ -199,7 +199,7 @@ export function useAnalytics() {
export function useSearchConfig() { export function useSearchConfig() {
const { result, error, loading, onResult } = useQuery<{ const { result, error, loading, onResult } = useQuery<{
config: Pick<IConfig, "search">; config: Pick<IConfig, "search">;
}>(SEARCH_CONFIG); }>(SEARCH_CONFIG, undefined, { fetchPolicy: "cache-only" });
const searchConfig = computed(() => result.value?.config.search); const searchConfig = computed(() => result.value?.config.search);
return { searchConfig, error, loading, onResult }; return { searchConfig, error, loading, onResult };

View File

@ -420,19 +420,3 @@ export const GROUP_TIMELINE = gql`
} }
${ACTOR_FRAGMENT} ${ACTOR_FRAGMENT}
`; `;
export const CLOSE_GROUPS = gql`
query CloseGroups($location: String, $radius: Float) {
searchGroups(location: $location, radius: $radius, page: 1, limit: 10) {
total
elements {
...ActorFragment
physicalAddress {
...AdressFragment
}
}
}
}
${ACTOR_FRAGMENT}
${ADDRESS_FRAGMENT}
`;

View File

@ -109,53 +109,3 @@ export const HOME_USER_QUERIES = gql`
${EVENT_OPTIONS_FRAGMENT} ${EVENT_OPTIONS_FRAGMENT}
${ACTOR_FRAGMENT} ${ACTOR_FRAGMENT}
`; `;
export const CLOSE_CONTENT = gql`
query CloseContent(
$location: String!
$radius: Float
$page: Int
$limit: Int
) {
searchEvents(
location: $location
radius: $radius
page: $page
limit: $limit
) {
total
elements {
id
title
uuid
beginsOn
status
picture {
id
url
}
language
tags {
...TagFragment
}
options {
...EventOptions
}
physicalAddress {
...AdressFragment
}
attributedTo {
...ActorFragment
}
organizerActor {
...ActorFragment
}
__typename
}
}
}
${ADDRESS_FRAGMENT}
${TAG_FRAGMENT}
${EVENT_OPTIONS_FRAGMENT}
${ACTOR_FRAGMENT}
`;

View File

@ -432,7 +432,7 @@ const filteredLanguages = ref<string[]>([]);
const instanceLanguages = computed({ const instanceLanguages = computed({
get() { get() {
const languageCodes = [...adminSettings.value.instanceLanguages] || []; const languageCodes = [...(adminSettings.value?.instanceLanguages ?? [])];
return languageCodes return languageCodes
.map((code) => languageForCode(code)) .map((code) => languageForCode(code))
.filter((language) => language) as string[]; .filter((language) => language) as string[];
@ -468,7 +468,8 @@ saveAdminSettingsError((e) => {
}); });
const updateSettings = async (): Promise<void> => { const updateSettings = async (): Promise<void> => {
const variables = { ...settingsToWrite }; const variables = { ...settingsToWrite.value };
console.debug("updating settings with variables", variables);
saveAdminSettings(variables); saveAdminSettings(variables);
}; };

View File

@ -318,6 +318,7 @@ const buildVariables = computed(() => {
// @ts-ignore // @ts-ignore
delete physicalAddress.__typename; delete physicalAddress.__typename;
} }
delete physicalAddress?.pictureInfo;
delete variables.avatar; delete variables.avatar;
delete variables.banner; delete variables.banner;

View File

@ -131,7 +131,11 @@
</span> </span>
</section> </section>
<!-- Recent events --> <!-- Recent events -->
<CloseEvents @doGeoLoc="performGeoLocation()" :userLocation="userLocation" /> <CloseEvents
@doGeoLoc="performGeoLocation()"
:userLocation="userLocation"
:doingGeoloc="doingGeoloc"
/>
<CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" /> <CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" />
<OnlineEvents /> <OnlineEvents />
<LastEvents v-if="instanceName" :instanceName="instanceName" /> <LastEvents v-if="instanceName" :instanceName="instanceName" />
@ -224,7 +228,9 @@ const { result: aboutConfigResult } = useQuery<{
IConfig, IConfig,
"name" | "description" | "slogan" | "registrationsOpen" "name" | "description" | "slogan" | "registrationsOpen"
>; >;
}>(ABOUT); }>(ABOUT, undefined, {
fetchPolicy: "cache-only",
});
const config = computed(() => aboutConfigResult.value?.config); const config = computed(() => aboutConfigResult.value?.config);
@ -507,10 +513,19 @@ GeolocationPosition) => {
reverseGeoCodeInformation.latitude = latitude; reverseGeoCodeInformation.latitude = latitude;
reverseGeoCodeInformation.longitude = longitude; reverseGeoCodeInformation.longitude = longitude;
reverseGeoCodeInformation.accuracy = accuracy; reverseGeoCodeInformation.accuracy = accuracy;
doingGeoloc.value = false;
}; };
const doingGeoloc = ref(false);
const performGeoLocation = () => { const performGeoLocation = () => {
navigator.geolocation.getCurrentPosition(fetchAndSaveCurrentLocationName); doingGeoloc.value = true;
navigator.geolocation.getCurrentPosition(
fetchAndSaveCurrentLocationName,
() => {
doingGeoloc.value = false;
}
);
}; };
/** /**

View File

@ -16,7 +16,7 @@
<o-field :label="t('Language')" label-for="setting-language"> <o-field :label="t('Language')" label-for="setting-language">
<o-select <o-select
:loading="loadingTimezones || loadingUserSettings" :loading="loadingTimezones || loadingUserSettings"
v-model="locale" v-model="$i18n.locale"
:placeholder="t('Select a language')" :placeholder="t('Select a language')"
id="setting-language" id="setting-language"
> >
@ -64,11 +64,15 @@
<o-field :label="t('City or region')" expanded label-for="setting-city"> <o-field :label="t('City or region')" expanded label-for="setting-city">
<full-address-auto-complete <full-address-auto-complete
v-if="loggedUser?.settings" v-if="loggedUser?.settings"
:type="AddressSearchType.ADMINISTRATIVE" :resultType="AddressSearchType.ADMINISTRATIVE"
:doGeoLocation="false" :doGeoLocation="false"
v-model="address" v-model="address"
:default-text="address?.description"
id="setting-city" id="setting-city"
class="grid" class="grid"
:hideMap="true"
:hideSelected="true"
labelClass="sr-only"
:placeholder="t('e.g. Nantes, Berlin, Cork, …')" :placeholder="t('e.g. Nantes, Berlin, Cork, …')"
/> />
</o-field> </o-field>
@ -108,7 +112,6 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import ngeohash from "ngeohash"; import ngeohash from "ngeohash";
import { saveLocaleData } from "@/utils/auth";
import { import {
USER_SETTINGS, USER_SETTINGS,
SET_USER_SETTINGS, SET_USER_SETTINGS,
@ -133,7 +136,7 @@ const { timezones: serverTimezones, loading: loadingTimezones } =
useTimezones(); useTimezones();
const { loggedUser, loading: loadingUserSettings } = useUserSettings(); const { loggedUser, loading: loadingUserSettings } = useUserSettings();
const { t, locale: i18nLocale } = useI18n({ useScope: "global" }); const { t, locale } = useI18n({ useScope: "global" });
useHead({ useHead({
title: computed(() => t("Preferences")), title: computed(() => t("Preferences")),
@ -161,25 +164,6 @@ const selectedTimezone = computed({
const { mutate: updateUserLocale } = useMutation(UPDATE_USER_LOCALE); const { mutate: updateUserLocale } = useMutation(UPDATE_USER_LOCALE);
const locale = computed({
get(): string {
if (loggedUser.value?.locale) {
return loggedUser.value?.locale;
}
return i18nLocale.value as string;
},
set(newLocale: string) {
if (newLocale) {
updateUserLocale({
locale: newLocale,
});
saveLocaleData(newLocale);
console.debug("changing locale", i18nLocale, newLocale);
i18nLocale.value = newLocale;
}
},
});
const sanitize = (timezone: string): string => { const sanitize = (timezone: string): string => {
return timezone return timezone
.split("_") .split("_")

View File

@ -69,7 +69,7 @@
/> />
</o-field> </o-field>
<p class="text-center my-2" v-if="!submitted"> <p class="text-center my-2">
<o-button <o-button
variant="primary" variant="primary"
size="large" size="large"
@ -227,6 +227,7 @@ const loginAction = (e: Event) => {
} }
submitted.value = true; submitted.value = true;
errors.value = [];
loginMutation({ loginMutation({
email: credentials.email, email: credentials.email,

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do
location = location =
case Map.get(geolix, :city) do case Map.get(geolix, :city) do
%{location: %{} = location} -> location %{location: location} -> location
_ -> nil _ -> nil
end end

View File

@ -237,6 +237,10 @@ defmodule Mobilizon.GraphQL.Resolvers.Group do
end end
end end
else else
Logger.info(
"Profile #{updater_actor.id} tried to update group #{group_id}, but they are not admin"
)
{:error, dgettext("errors", "Profile is not administrator for the group")} {:error, dgettext("errors", "Profile is not administrator for the group")}
end end
end end