forked from potsda.mn/mobilizon
Merge branch '1475-1469-homepage-location-enhancement-no-activities' into 'main'
fix #1469 and # 1475 Closes #1469 and #1475 See merge request framasoft/mobilizon!1581
This commit is contained in:
commit
2078dbcf55
|
@ -69,13 +69,31 @@ defmodule Mobilizon.GraphQL.Resolvers.Event do
|
||||||
|
|
||||||
@spec list_events(any(), map(), Absinthe.Resolution.t()) ::
|
@spec list_events(any(), map(), Absinthe.Resolution.t()) ::
|
||||||
{:ok, Page.t(Event.t())} | {:error, :events_max_limit_reached}
|
{:ok, Page.t(Event.t())} | {:error, :events_max_limit_reached}
|
||||||
|
def list_events(
|
||||||
|
_parent,
|
||||||
|
%{
|
||||||
|
page: page,
|
||||||
|
limit: limit,
|
||||||
|
order_by: order_by,
|
||||||
|
direction: direction,
|
||||||
|
longevents: longevents,
|
||||||
|
location: location,
|
||||||
|
radius: radius
|
||||||
|
},
|
||||||
|
_resolution
|
||||||
|
)
|
||||||
|
when limit < @event_max_limit do
|
||||||
|
{:ok,
|
||||||
|
Events.list_events(page, limit, order_by, direction, true, longevents, location, radius)}
|
||||||
|
end
|
||||||
|
|
||||||
def list_events(
|
def list_events(
|
||||||
_parent,
|
_parent,
|
||||||
%{page: page, limit: limit, order_by: order_by, direction: direction},
|
%{page: page, limit: limit, order_by: order_by, direction: direction},
|
||||||
_resolution
|
_resolution
|
||||||
)
|
)
|
||||||
when limit < @event_max_limit do
|
when limit < @event_max_limit do
|
||||||
{:ok, Events.list_events(page, limit, order_by, direction)}
|
{:ok, Events.list_events(page, limit, order_by, direction, true)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def list_events(_parent, %{page: _page, limit: _limit}, _resolution) do
|
def list_events(_parent, %{page: _page, limit: _limit}, _resolution) do
|
||||||
|
|
|
@ -375,6 +375,13 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
object :event_queries do
|
object :event_queries do
|
||||||
@desc "Get all events"
|
@desc "Get all events"
|
||||||
field :events, :paginated_event_list do
|
field :events, :paginated_event_list do
|
||||||
|
arg(:location, :string, default_value: nil, description: "A geohash for coordinates")
|
||||||
|
|
||||||
|
arg(:radius, :float,
|
||||||
|
default_value: nil,
|
||||||
|
description: "Radius around the location to search in"
|
||||||
|
)
|
||||||
|
|
||||||
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
arg(:page, :integer, default_value: 1, description: "The page in the paginated event list")
|
||||||
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
arg(:limit, :integer, default_value: 10, description: "The limit of events per page")
|
||||||
|
|
||||||
|
@ -388,6 +395,11 @@ defmodule Mobilizon.GraphQL.Schema.EventType do
|
||||||
description: "Direction for the sort"
|
description: "Direction for the sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
arg(:longevents, :boolean,
|
||||||
|
default_value: nil,
|
||||||
|
description: "if mention filter in or out long events"
|
||||||
|
)
|
||||||
|
|
||||||
middleware(Rajska.QueryAuthorization, permit: :all)
|
middleware(Rajska.QueryAuthorization, permit: :all)
|
||||||
|
|
||||||
resolve(&Event.list_events/3)
|
resolve(&Event.list_events/3)
|
||||||
|
|
|
@ -359,19 +359,34 @@ defmodule Mobilizon.Events do
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of events.
|
Returns the list of events.
|
||||||
"""
|
"""
|
||||||
@spec list_events(integer | nil, integer | nil, atom, atom, boolean) :: Page.t(Event.t())
|
@spec list_events(
|
||||||
|
integer | nil,
|
||||||
|
integer | nil,
|
||||||
|
atom,
|
||||||
|
atom,
|
||||||
|
boolean,
|
||||||
|
boolean | nil,
|
||||||
|
string | nil,
|
||||||
|
float | nil
|
||||||
|
) :: Page.t(Event.t())
|
||||||
def list_events(
|
def list_events(
|
||||||
page \\ nil,
|
page \\ nil,
|
||||||
limit \\ nil,
|
limit \\ nil,
|
||||||
sort \\ :begins_on,
|
sort \\ :begins_on,
|
||||||
direction \\ :asc,
|
direction \\ :asc,
|
||||||
is_future \\ true
|
is_future \\ true,
|
||||||
|
longevents \\ nil,
|
||||||
|
location \\ nil,
|
||||||
|
radius \\ nil
|
||||||
) do
|
) do
|
||||||
Event
|
Event
|
||||||
|> distinct([e], [{^direction, ^sort}, asc: e.id])
|
|> distinct([e], [{^direction, ^sort}, asc: e.id])
|
||||||
|> preload([:organizer_actor, :participants])
|
|> preload([:organizer_actor, :participants])
|
||||||
|> sort(sort, direction)
|
|> sort(sort, direction)
|
||||||
|
|> maybe_join_address(%{location: location, radius: radius})
|
||||||
|
|> events_for_location(%{location: location, radius: radius})
|
||||||
|> filter_future_events(is_future)
|
|> filter_future_events(is_future)
|
||||||
|
|> events_for_longevents(longevents)
|
||||||
|> filter_public_visibility()
|
|> filter_public_visibility()
|
||||||
|> filter_draft()
|
|> filter_draft()
|
||||||
|> filter_cancelled_events()
|
|> filter_cancelled_events()
|
||||||
|
@ -572,7 +587,7 @@ defmodule Mobilizon.Events do
|
||||||
|> events_for_search_query()
|
|> events_for_search_query()
|
||||||
|> events_for_begins_on(Map.get(args, :begins_on, DateTime.utc_now()))
|
|> events_for_begins_on(Map.get(args, :begins_on, DateTime.utc_now()))
|
||||||
|> events_for_ends_on(Map.get(args, :ends_on))
|
|> events_for_ends_on(Map.get(args, :ends_on))
|
||||||
|> events_for_longevents(args)
|
|> events_for_longevents(Map.get(args, :longevents))
|
||||||
|> events_for_category(args)
|
|> events_for_category(args)
|
||||||
|> events_for_categories(args)
|
|> events_for_categories(args)
|
||||||
|> events_for_languages(args)
|
|> events_for_languages(args)
|
||||||
|
@ -1379,15 +1394,13 @@ defmodule Mobilizon.Events do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec events_for_longevents(Ecto.Queryable.t(), map()) :: Ecto.Query.t()
|
@spec events_for_longevents(Ecto.Queryable.t(), Boolean.t() | nil) :: Ecto.Query.t()
|
||||||
defp events_for_longevents(query, args) do
|
defp events_for_longevents(query, longevents) do
|
||||||
duration = Config.get([:instance, :duration_of_long_event], 0)
|
duration = Config.get([:instance, :duration_of_long_event], 0)
|
||||||
|
|
||||||
if duration <= 0 do
|
if duration <= 0 do
|
||||||
query
|
query
|
||||||
else
|
else
|
||||||
longevents = Map.get(args, :longevents)
|
|
||||||
|
|
||||||
case longevents do
|
case longevents do
|
||||||
nil ->
|
nil ->
|
||||||
query
|
query
|
||||||
|
|
10
package-lock.json
generated
10
package-lock.json
generated
|
@ -6,7 +6,7 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mobilizon",
|
"name": "mobilizon",
|
||||||
"version": "4.1.0",
|
"version": "5.0.0-beta.1",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/client": "^3.3.16",
|
"@apollo/client": "^3.3.16",
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
"@fullcalendar/daygrid": "^6.1.10",
|
"@fullcalendar/daygrid": "^6.1.10",
|
||||||
"@fullcalendar/interaction": "^6.1.10",
|
"@fullcalendar/interaction": "^6.1.10",
|
||||||
"@fullcalendar/vue3": "^6.1.10",
|
"@fullcalendar/vue3": "^6.1.10",
|
||||||
"@oruga-ui/oruga-next": "^0.8.10",
|
"@oruga-ui/oruga-next": "0.8.12",
|
||||||
"@oruga-ui/theme-oruga": "^0.2.0",
|
"@oruga-ui/theme-oruga": "^0.2.0",
|
||||||
"@sentry/tracing": "^7.1",
|
"@sentry/tracing": "^7.1",
|
||||||
"@sentry/vue": "^7.1",
|
"@sentry/vue": "^7.1",
|
||||||
|
@ -3138,9 +3138,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@oruga-ui/oruga-next": {
|
"node_modules/@oruga-ui/oruga-next": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.12",
|
||||||
"resolved": "https://registry.npmjs.org/@oruga-ui/oruga-next/-/oruga-next-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@oruga-ui/oruga-next/-/oruga-next-0.8.12.tgz",
|
||||||
"integrity": "sha512-ETPSoGZu1parbj8C3V2ZojQnN4ptQMiJEwS9Hx44NcaDzu4q/FDsYkKYiz6G9kx8cDceXXxvydfOUpZePVVdzw==",
|
"integrity": "sha512-I1jcsTA4J6HQdNpSWgK4cNSqv1cHsghQGtJ12p0yXDSJseek0Y8f4vf9+tDRtfONzWHuRyWUGcHIfePsRKVbiQ==",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"vue": "^3.0.0"
|
"vue": "^3.0.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form
|
||||||
id="search-anchor"
|
id="search-anchor"
|
||||||
class="container mx-auto my-3 px-2 flex flex-wrap flex-col sm:flex-row items-stretch gap-2 text-center items-center justify-center dark:text-slate-100"
|
class="container mx-auto my-3 px-2 flex flex-wrap flex-col sm:flex-row items-stretch gap-2 text-center justify-center dark:text-slate-100"
|
||||||
role="search"
|
role="search"
|
||||||
@submit.prevent="submit"
|
@submit.prevent="submit"
|
||||||
>
|
>
|
||||||
|
@ -38,6 +38,11 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { IAddress } from "@/types/address.model";
|
import { IAddress } from "@/types/address.model";
|
||||||
import { AddressSearchType } from "@/types/enums";
|
import { AddressSearchType } from "@/types/enums";
|
||||||
|
import {
|
||||||
|
addressToLocation,
|
||||||
|
getLocationFromLocal,
|
||||||
|
storeLocationInLocal,
|
||||||
|
} from "@/utils/location";
|
||||||
import { computed, defineAsyncComponent } from "vue";
|
import { computed, defineAsyncComponent } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRouter, useRoute } from "vue-router";
|
import { useRouter, useRoute } from "vue-router";
|
||||||
|
@ -51,6 +56,7 @@ const props = defineProps<{
|
||||||
location: IAddress | null;
|
location: IAddress | null;
|
||||||
locationDefaultText?: string | null;
|
locationDefaultText?: string | null;
|
||||||
search: string;
|
search: string;
|
||||||
|
fromLocalStorage?: boolean | false;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
@ -64,10 +70,19 @@ const emit = defineEmits<{
|
||||||
|
|
||||||
const location = computed({
|
const location = computed({
|
||||||
get(): IAddress | null {
|
get(): IAddress | null {
|
||||||
|
if (props.location) {
|
||||||
return props.location;
|
return props.location;
|
||||||
|
}
|
||||||
|
if (props.fromLocalStorage) {
|
||||||
|
return getLocationFromLocal();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
set(newLocation: IAddress | null) {
|
set(newLocation: IAddress | null) {
|
||||||
emit("update:location", newLocation);
|
emit("update:location", newLocation);
|
||||||
|
if (props.fromLocalStorage) {
|
||||||
|
storeLocationInLocal(newLocation);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,12 +97,7 @@ const search = computed({
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
emit("submit");
|
emit("submit");
|
||||||
const lat = location.value?.geom
|
const { lat, lon } = addressToLocation(location.value);
|
||||||
? parseFloat(location.value?.geom?.split(";")?.[1])
|
|
||||||
: undefined;
|
|
||||||
const lon = location.value?.geom
|
|
||||||
? parseFloat(location.value?.geom?.split(";")?.[0])
|
|
||||||
: undefined;
|
|
||||||
router.push({
|
router.push({
|
||||||
name: RouteName.SEARCH,
|
name: RouteName.SEARCH,
|
||||||
query: {
|
query: {
|
||||||
|
|
|
@ -1,20 +1,36 @@
|
||||||
<template>
|
<template>
|
||||||
<close-content
|
<close-content
|
||||||
class="container mx-auto px-2"
|
class="container mx-auto px-2"
|
||||||
v-show="loading || (events && events.total > 0)"
|
:suggestGeoloc="false"
|
||||||
:suggestGeoloc="suggestGeoloc"
|
|
||||||
v-on="attrs"
|
v-on="attrs"
|
||||||
@doGeoLoc="emit('doGeoLoc')"
|
|
||||||
:doingGeoloc="doingGeoloc"
|
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
<template v-if="userLocationName">
|
<template v-if="userLocation?.name">
|
||||||
{{ t("Events nearby {position}", { position: userLocationName }) }}
|
{{
|
||||||
|
t("Incoming events and activities nearby {position}", {
|
||||||
|
position: userLocation?.name,
|
||||||
|
})
|
||||||
|
}}
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
{{ t("Events close to you") }}
|
{{ t("Incoming events and activities") }}
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
<template #subtitle>
|
||||||
|
<div v-if="!loading && events.total == 0">
|
||||||
|
<template v-if="userLocation?.name">
|
||||||
|
{{
|
||||||
|
t(
|
||||||
|
"No events found nearby {position}. Try removing your position to see all events!",
|
||||||
|
{ position: userLocation?.name }
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ t("No events found") }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<skeleton-event-result
|
<skeleton-event-result
|
||||||
v-for="i in 6"
|
v-for="i in 6"
|
||||||
|
@ -28,25 +44,36 @@
|
||||||
:key="event.uuid"
|
:key="event.uuid"
|
||||||
/>
|
/>
|
||||||
<more-content
|
<more-content
|
||||||
v-if="userLocationName && userLocation?.lat && userLocation?.lon"
|
v-if="userLocation?.name && userLocation?.lat && userLocation?.lon"
|
||||||
:to="{
|
:to="{
|
||||||
name: RouteName.SEARCH,
|
name: RouteName.SEARCH,
|
||||||
query: {
|
query: {
|
||||||
locationName: userLocationName,
|
locationName: userLocation?.name,
|
||||||
lat: userLocation.lat?.toString(),
|
lat: userLocation.lat?.toString(),
|
||||||
lon: userLocation.lon?.toString(),
|
lon: userLocation.lon?.toString(),
|
||||||
contentType: 'EVENTS',
|
contentType: 'ALL',
|
||||||
distance: `${distance}_km`,
|
distance: `${distance}_km`,
|
||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
:picture="userLocation?.picture"
|
:picture="userLocation?.picture"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
t("View more events around {position}", {
|
t("View more events and activities around {position}", {
|
||||||
position: userLocationName,
|
position: userLocation?.name,
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
</more-content>
|
</more-content>
|
||||||
|
<more-content
|
||||||
|
v-else
|
||||||
|
:to="{
|
||||||
|
name: RouteName.SEARCH,
|
||||||
|
query: {
|
||||||
|
contentType: 'ALL',
|
||||||
|
},
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ t("View more events and activities") }}
|
||||||
|
</more-content>
|
||||||
</template>
|
</template>
|
||||||
</close-content>
|
</close-content>
|
||||||
</template>
|
</template>
|
||||||
|
@ -55,76 +82,55 @@
|
||||||
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, onMounted, useAttrs } from "vue";
|
import { watch, computed, useAttrs } from "vue";
|
||||||
import { SEARCH_EVENTS } from "@/graphql/search";
|
import { FETCH_EVENTS } from "@/graphql/event";
|
||||||
import { IEvent } from "@/types/event.model";
|
import { IEvent } from "@/types/event.model";
|
||||||
import { useLazyQuery } from "@vue/apollo-composable";
|
import { useQuery } 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";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { coordsToGeoHash } from "@/utils/location";
|
import { coordsToGeoHash } from "@/utils/location";
|
||||||
import { roundToNearestMinute } from "@/utils/datetime";
|
|
||||||
import RouteName from "@/router/name";
|
import RouteName from "@/router/name";
|
||||||
|
import { EventSortField, SortDirection } from "@/types/enums";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
userLocation: LocationType;
|
userLocation: LocationType;
|
||||||
doingGeoloc?: boolean;
|
doingGeoloc?: boolean;
|
||||||
}>();
|
}>();
|
||||||
const emit = defineEmits(["doGeoLoc"]);
|
defineEmits(["doGeoLoc"]);
|
||||||
|
|
||||||
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 userLocation = computed(() => props.userLocation);
|
||||||
|
|
||||||
const userLocationName = computed(() => {
|
const geoHash = computed(() => {
|
||||||
return userLocation.value?.name;
|
console.debug("userLocation updated", userLocation.value);
|
||||||
|
const geo = coordsToGeoHash(userLocation.value.lat, userLocation.value.lon);
|
||||||
|
console.debug("geohash:", geo);
|
||||||
|
return geo;
|
||||||
});
|
});
|
||||||
const suggestGeoloc = computed(() => userLocation.value?.isIPLocation);
|
|
||||||
|
|
||||||
const geoHash = computed(() =>
|
const distance = computed<number>(() =>
|
||||||
coordsToGeoHash(props.userLocation.lat, props.userLocation.lon)
|
userLocation.value?.isIPLocation ? 150 : 25
|
||||||
);
|
);
|
||||||
|
|
||||||
const distance = computed<number>(() => (suggestGeoloc.value ? 150 : 25));
|
const eventsQuery = useQuery<{
|
||||||
|
|
||||||
const now = computed(() => roundToNearestMinute(new Date()));
|
|
||||||
|
|
||||||
const searchEnabled = computed(() => geoHash.value != undefined);
|
|
||||||
|
|
||||||
const {
|
|
||||||
result: eventsResult,
|
|
||||||
loading: loadingEvents,
|
|
||||||
load: load,
|
|
||||||
} = useLazyQuery<{
|
|
||||||
searchEvents: Paginate<IEvent>;
|
searchEvents: Paginate<IEvent>;
|
||||||
}>(
|
}>(FETCH_EVENTS, () => ({
|
||||||
SEARCH_EVENTS,
|
orderBy: EventSortField.BEGINS_ON,
|
||||||
() => ({
|
direction: SortDirection.ASC,
|
||||||
|
longevents: false,
|
||||||
location: geoHash.value,
|
location: geoHash.value,
|
||||||
beginsOn: now.value,
|
|
||||||
endsOn: undefined,
|
|
||||||
radius: distance.value,
|
radius: distance.value,
|
||||||
eventPage: 1,
|
}));
|
||||||
limit: EVENT_PAGE_LIMIT,
|
|
||||||
type: "IN_PERSON",
|
|
||||||
}),
|
|
||||||
() => ({
|
|
||||||
enabled: searchEnabled.value,
|
|
||||||
fetchPolicy: "cache-first",
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const events = computed(
|
const events = computed(
|
||||||
() => eventsResult.value?.searchEvents ?? { elements: [], total: 0 }
|
() => eventsQuery.result.value?.events ?? { elements: [], total: 0 }
|
||||||
);
|
);
|
||||||
|
watch(events, (e) => console.debug("events: ", e));
|
||||||
|
|
||||||
onMounted(async () => {
|
const loading = computed(() => props.doingGeoloc || eventsQuery.loading.value);
|
||||||
await load();
|
watch(loading, (l) => console.debug("loading: ", l));
|
||||||
});
|
|
||||||
|
|
||||||
const loading = computed(() => props.doingGeoloc || loadingEvents.value);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -60,6 +60,7 @@ const { result: resultEvents, loading: loadingEvents } = useQuery<{
|
||||||
}>(FETCH_EVENTS, {
|
}>(FETCH_EVENTS, {
|
||||||
orderBy: EventSortField.BEGINS_ON,
|
orderBy: EventSortField.BEGINS_ON,
|
||||||
direction: SortDirection.ASC,
|
direction: SortDirection.ASC,
|
||||||
|
longevents: false,
|
||||||
});
|
});
|
||||||
const events = computed(
|
const events = computed(
|
||||||
() => resultEvents.value?.events ?? { total: 0, elements: [] }
|
() => resultEvents.value?.events ?? { total: 0, elements: [] }
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { IDENTITIES, REGISTER_PERSON } from "@/graphql/actor";
|
||||||
import {
|
import {
|
||||||
CURRENT_USER_CLIENT,
|
CURRENT_USER_CLIENT,
|
||||||
LOGGED_USER,
|
LOGGED_USER,
|
||||||
|
LOGGED_USER_LOCATION,
|
||||||
SET_USER_SETTINGS,
|
SET_USER_SETTINGS,
|
||||||
UPDATE_USER_LOCALE,
|
UPDATE_USER_LOCALE,
|
||||||
USER_SETTINGS,
|
USER_SETTINGS,
|
||||||
|
@ -51,6 +52,20 @@ export function useUserSettings() {
|
||||||
return { loggedUser, error, loading };
|
return { loggedUser, error, loading };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useUserLocation() {
|
||||||
|
const {
|
||||||
|
result: userSettingsResult,
|
||||||
|
error,
|
||||||
|
loading,
|
||||||
|
onResult,
|
||||||
|
} = useQuery<{ loggedUser: IUser }>(LOGGED_USER_LOCATION);
|
||||||
|
|
||||||
|
const location = computed(
|
||||||
|
() => userSettingsResult.value?.loggedUser.settings.location
|
||||||
|
);
|
||||||
|
return { location, error, loading, onResult };
|
||||||
|
}
|
||||||
|
|
||||||
export async function doUpdateSetting(
|
export async function doUpdateSetting(
|
||||||
variables: Record<string, unknown>
|
variables: Record<string, unknown>
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
|
|
@ -103,16 +103,22 @@ export const FETCH_EVENT_BASIC = gql`
|
||||||
|
|
||||||
export const FETCH_EVENTS = gql`
|
export const FETCH_EVENTS = gql`
|
||||||
query FetchEvents(
|
query FetchEvents(
|
||||||
|
$location: String
|
||||||
|
$radius: Float
|
||||||
$orderBy: EventOrderBy
|
$orderBy: EventOrderBy
|
||||||
$direction: SortDirection
|
$direction: SortDirection
|
||||||
$page: Int
|
$page: Int
|
||||||
$limit: Int
|
$limit: Int
|
||||||
|
$longevents: Boolean
|
||||||
) {
|
) {
|
||||||
events(
|
events(
|
||||||
|
location: $location
|
||||||
|
radius: $radius
|
||||||
orderBy: $orderBy
|
orderBy: $orderBy
|
||||||
direction: $direction
|
direction: $direction
|
||||||
page: $page
|
page: $page
|
||||||
limit: $limit
|
limit: $limit
|
||||||
|
longevents: $longevents
|
||||||
) {
|
) {
|
||||||
total
|
total
|
||||||
elements {
|
elements {
|
||||||
|
|
|
@ -218,6 +218,7 @@ export const SEARCH_CALENDAR_EVENTS = gql`
|
||||||
endsOn: $endsOn
|
endsOn: $endsOn
|
||||||
page: $eventPage
|
page: $eventPage
|
||||||
limit: $limit
|
limit: $limit
|
||||||
|
longevents: false
|
||||||
) {
|
) {
|
||||||
total
|
total
|
||||||
elements {
|
elements {
|
||||||
|
|
|
@ -138,6 +138,20 @@ export const USER_SETTINGS = gql`
|
||||||
${USER_SETTINGS_FRAGMENT}
|
${USER_SETTINGS_FRAGMENT}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const LOGGED_USER_LOCATION = gql`
|
||||||
|
query LoggedUserLocation {
|
||||||
|
loggedUser {
|
||||||
|
settings {
|
||||||
|
location {
|
||||||
|
range
|
||||||
|
geohash
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const LOGGED_USER_TIMEZONE = gql`
|
export const LOGGED_USER_TIMEZONE = gql`
|
||||||
query LoggedUserTimezone {
|
query LoggedUserTimezone {
|
||||||
loggedUser {
|
loggedUser {
|
||||||
|
|
|
@ -1626,7 +1626,7 @@
|
||||||
"With {participants}": "With {participants}",
|
"With {participants}": "With {participants}",
|
||||||
"Conversations": "Conversations",
|
"Conversations": "Conversations",
|
||||||
"New private message": "New private message",
|
"New private message": "New private message",
|
||||||
"There's no conversations yet": "There's no conversations yet",
|
"There's no conversations yet": "There are no conversations yet",
|
||||||
"Open conversations": "Open conversations",
|
"Open conversations": "Open conversations",
|
||||||
"List of conversations": "List of conversations",
|
"List of conversations": "List of conversations",
|
||||||
"Conversation with {participants}": "Conversation with {participants}",
|
"Conversation with {participants}": "Conversation with {participants}",
|
||||||
|
|
|
@ -1,7 +1,24 @@
|
||||||
import ngeohash from "ngeohash";
|
import ngeohash from "ngeohash";
|
||||||
|
|
||||||
|
import { IAddress, Address } from "@/types/address.model";
|
||||||
|
import { LocationType } from "@/types/user-location.model";
|
||||||
|
import { IUserPreferredLocation } from "@/types/current-user.model";
|
||||||
|
|
||||||
const GEOHASH_DEPTH = 9; // put enough accuracy, radius will be used anyway
|
const GEOHASH_DEPTH = 9; // put enough accuracy, radius will be used anyway
|
||||||
|
|
||||||
|
export const addressToLocation = (
|
||||||
|
address: IAddress
|
||||||
|
): LocationType | undefined => {
|
||||||
|
if (!address.geom) return undefined;
|
||||||
|
const arr = address.geom.split(";");
|
||||||
|
if (arr.length < 2) return undefined;
|
||||||
|
return {
|
||||||
|
lon: parseFloat(arr[0]),
|
||||||
|
lat: parseFloat(arr[1]),
|
||||||
|
name: address.description,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const coordsToGeoHash = (
|
export const coordsToGeoHash = (
|
||||||
lat: number | undefined,
|
lat: number | undefined,
|
||||||
lon: number | undefined,
|
lon: number | undefined,
|
||||||
|
@ -14,9 +31,72 @@ export const coordsToGeoHash = (
|
||||||
};
|
};
|
||||||
|
|
||||||
export const geoHashToCoords = (
|
export const geoHashToCoords = (
|
||||||
geohash: string | undefined
|
geohash: string | undefined | null
|
||||||
): { latitude: number; longitude: number } | undefined => {
|
): { latitude: number; longitude: number } | undefined => {
|
||||||
if (!geohash) return undefined;
|
if (!geohash) return undefined;
|
||||||
const { latitude, longitude } = ngeohash.decode(geohash);
|
const { latitude, longitude } = ngeohash.decode(geohash);
|
||||||
return latitude && longitude ? { latitude, longitude } : undefined;
|
return latitude && longitude ? { latitude, longitude } : undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const storeLocationInLocal = (location: IAddress | null): undefined => {
|
||||||
|
if (location) {
|
||||||
|
window.localStorage.setItem("location", JSON.stringify(location));
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem("location");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLocationFromLocal = (): IAddress | null => {
|
||||||
|
const locationString = window.localStorage.getItem("location");
|
||||||
|
if (!locationString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const location = JSON.parse(locationString) as IAddress;
|
||||||
|
if (!location.description || !location.geom) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const storeRadiusInLocal = (radius: number | null): undefined => {
|
||||||
|
if (radius) {
|
||||||
|
window.localStorage.setItem("radius", radius.toString());
|
||||||
|
} else {
|
||||||
|
window.localStorage.removeItem("radius");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getRadiusFromLocal = (): IAddress | null => {
|
||||||
|
const locationString = window.localStorage.getItem("location");
|
||||||
|
if (!locationString) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const location = JSON.parse(locationString) as IAddress;
|
||||||
|
if (!location.description || !location.geom) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const storeUserLocationAndRadiusFromUserSettings = (
|
||||||
|
location: IUserPreferredLocation | null
|
||||||
|
): undefined => {
|
||||||
|
if (location) {
|
||||||
|
const latlon = geoHashToCoords(location.geohash);
|
||||||
|
if (latlon) {
|
||||||
|
storeLocationInLocal({
|
||||||
|
...new Address(),
|
||||||
|
geom: `${latlon.longitude};${latlon.latitude}`,
|
||||||
|
description: location.name || "",
|
||||||
|
type: "administrative",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (location.range) {
|
||||||
|
storeRadiusInLocal(location.range);
|
||||||
|
} else {
|
||||||
|
console.debug("user has not set a radius");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.debug("user has not set a location");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -26,7 +26,12 @@
|
||||||
<!-- Unlogged introduction -->
|
<!-- Unlogged introduction -->
|
||||||
<unlogged-introduction :config="config" v-if="config && !isLoggedIn" />
|
<unlogged-introduction :config="config" v-if="config && !isLoggedIn" />
|
||||||
<!-- Search fields -->
|
<!-- Search fields -->
|
||||||
<search-fields v-model:search="search" v-model:location="location" />
|
<search-fields
|
||||||
|
v-model:search="search"
|
||||||
|
v-model:location="location"
|
||||||
|
:locationDefaultText="location?.description"
|
||||||
|
:fromLocalStorage="true"
|
||||||
|
/>
|
||||||
<!-- Categories preview -->
|
<!-- Categories preview -->
|
||||||
<categories-preview />
|
<categories-preview />
|
||||||
<!-- Welcome back -->
|
<!-- Welcome back -->
|
||||||
|
@ -143,7 +148,6 @@
|
||||||
/>
|
/>
|
||||||
<CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" />
|
<CloseGroups :userLocation="userLocation" @doGeoLoc="performGeoLocation()" />
|
||||||
<OnlineEvents />
|
<OnlineEvents />
|
||||||
<LastEvents v-if="instanceName" :instanceName="instanceName" />
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
@ -160,7 +164,7 @@ import { IEvent } from "../types/event.model";
|
||||||
// import { IFollowedGroupEvent } from "../types/followedGroupEvent.model";
|
// import { IFollowedGroupEvent } from "../types/followedGroupEvent.model";
|
||||||
import CloseEvents from "@/components/Local/CloseEvents.vue";
|
import CloseEvents from "@/components/Local/CloseEvents.vue";
|
||||||
import CloseGroups from "@/components/Local/CloseGroups.vue";
|
import CloseGroups from "@/components/Local/CloseGroups.vue";
|
||||||
import LastEvents from "@/components/Local/LastEvents.vue";
|
// import LastEvents from "@/components/Local/LastEvents.vue";
|
||||||
import OnlineEvents from "@/components/Local/OnlineEvents.vue";
|
import OnlineEvents from "@/components/Local/OnlineEvents.vue";
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
|
@ -183,7 +187,7 @@ import CategoriesPreview from "@/components/Home/CategoriesPreview.vue";
|
||||||
import UnloggedIntroduction from "@/components/Home/UnloggedIntroduction.vue";
|
import UnloggedIntroduction from "@/components/Home/UnloggedIntroduction.vue";
|
||||||
import SearchFields from "@/components/Home/SearchFields.vue";
|
import SearchFields from "@/components/Home/SearchFields.vue";
|
||||||
import { useHead } from "@unhead/vue";
|
import { useHead } from "@unhead/vue";
|
||||||
import { geoHashToCoords } from "@/utils/location";
|
import { addressToLocation, geoHashToCoords } from "@/utils/location";
|
||||||
import { useServerProvidedLocation } from "@/composition/apollo/config";
|
import { useServerProvidedLocation } from "@/composition/apollo/config";
|
||||||
import { ABOUT } from "@/graphql/config";
|
import { ABOUT } from "@/graphql/config";
|
||||||
import { IConfig } from "@/types/config.model";
|
import { IConfig } from "@/types/config.model";
|
||||||
|
@ -239,6 +243,10 @@ const currentUserParticipations = computed(
|
||||||
const location = ref(null);
|
const location = ref(null);
|
||||||
const search = ref("");
|
const search = ref("");
|
||||||
|
|
||||||
|
watch(location, (newLoc, oldLoc) =>
|
||||||
|
console.debug("LOCATION UPDATED from", { ...oldLoc }, " to ", { ...newLoc })
|
||||||
|
);
|
||||||
|
|
||||||
const isToday = (date: string): boolean => {
|
const isToday = (date: string): boolean => {
|
||||||
return new Date(date).toDateString() === new Date().toDateString();
|
return new Date(date).toDateString() === new Date().toDateString();
|
||||||
};
|
};
|
||||||
|
@ -383,11 +391,7 @@ const coords = computed(() => {
|
||||||
userSettingsLocationGeoHash.value ?? undefined
|
userSettingsLocationGeoHash.value ?? undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
if (userSettingsCoords) {
|
return { ...serverLocation.value, isIPLocation: !userSettingsCoords };
|
||||||
return { ...userSettingsCoords, isIPLocation: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { ...serverLocation.value, isIPLocation: true };
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { result: reverseGeocodeResult } = useQuery<{
|
const { result: reverseGeocodeResult } = useQuery<{
|
||||||
|
@ -428,6 +432,11 @@ const currentUserLocation = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const userLocation = computed(() => {
|
const userLocation = computed(() => {
|
||||||
|
console.debug("new userLocation");
|
||||||
|
if (location.value) {
|
||||||
|
console.debug("userLocation is typed location");
|
||||||
|
return addressToLocation(location.value);
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
!userSettingsLocation.value ||
|
!userSettingsLocation.value ||
|
||||||
(userSettingsLocation.value?.isIPLocation &&
|
(userSettingsLocation.value?.isIPLocation &&
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="max-w-4xl mx-auto">
|
<div class="max-w-4xl mx-auto">
|
||||||
<SearchFields
|
<search-fields
|
||||||
class="md:ml-10 mr-2"
|
class="md:ml-10 mr-2"
|
||||||
v-model:search="search"
|
v-model:search="search"
|
||||||
v-model:location="location"
|
v-model:location="location"
|
||||||
:locationDefaultText="locationName"
|
:locationDefaultText="locationName"
|
||||||
|
:fromLocalStorage="true"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
|
@ -917,6 +918,9 @@ const groupPage = useRouteQuery("groupPage", 1, integerTransformer);
|
||||||
const latitude = useRouteQuery("lat", undefined, floatTransformer);
|
const latitude = useRouteQuery("lat", undefined, floatTransformer);
|
||||||
const longitude = useRouteQuery("lon", undefined, floatTransformer);
|
const longitude = useRouteQuery("lon", undefined, floatTransformer);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// This should be updated with getRadiusFromLocal if we want to use user's
|
||||||
|
// preferences
|
||||||
const distance = useRouteQuery("distance", "10_km");
|
const distance = useRouteQuery("distance", "10_km");
|
||||||
const when = useRouteQuery("when", "any");
|
const when = useRouteQuery("when", "any");
|
||||||
const contentType = useRouteQuery(
|
const contentType = useRouteQuery(
|
||||||
|
|
|
@ -127,15 +127,17 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { LOGIN } from "@/graphql/auth";
|
import { LOGIN } from "@/graphql/auth";
|
||||||
import { LOGIN_CONFIG } from "@/graphql/config";
|
import { LOGIN_CONFIG } from "@/graphql/config";
|
||||||
|
import { LOGGED_USER_LOCATION } from "@/graphql/user";
|
||||||
import { UPDATE_CURRENT_USER_CLIENT } from "@/graphql/user";
|
import { UPDATE_CURRENT_USER_CLIENT } from "@/graphql/user";
|
||||||
import { IConfig } from "@/types/config.model";
|
import { IConfig } from "@/types/config.model";
|
||||||
import { ILogin } from "@/types/login.model";
|
import { IUser } from "@/types/current-user.model";
|
||||||
import { saveUserData, SELECTED_PROVIDERS } from "@/utils/auth";
|
import { saveUserData, SELECTED_PROVIDERS } from "@/utils/auth";
|
||||||
|
import { storeUserLocationAndRadiusFromUserSettings } from "@/utils/location";
|
||||||
import {
|
import {
|
||||||
initializeCurrentActor,
|
initializeCurrentActor,
|
||||||
NoIdentitiesException,
|
NoIdentitiesException,
|
||||||
} from "@/utils/identity";
|
} from "@/utils/identity";
|
||||||
import { useMutation, useQuery } from "@vue/apollo-composable";
|
import { useMutation, useLazyQuery, useQuery } from "@vue/apollo-composable";
|
||||||
import { computed, reactive, ref, onMounted } from "vue";
|
import { computed, reactive, ref, onMounted } from "vue";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
|
@ -153,14 +155,14 @@ const route = useRoute();
|
||||||
|
|
||||||
const { currentUser } = useCurrentUserClient();
|
const { currentUser } = useCurrentUserClient();
|
||||||
|
|
||||||
const { result: configResult } = useQuery<{
|
const configQuery = useQuery<{
|
||||||
config: Pick<
|
config: Pick<
|
||||||
IConfig,
|
IConfig,
|
||||||
"auth" | "registrationsOpen" | "registrationsAllowlist"
|
"auth" | "registrationsOpen" | "registrationsAllowlist"
|
||||||
>;
|
>;
|
||||||
}>(LOGIN_CONFIG);
|
}>(LOGIN_CONFIG);
|
||||||
|
|
||||||
const config = computed(() => configResult.value?.config);
|
const config = computed(() => configQuery.result.value?.config);
|
||||||
|
|
||||||
const canRegister = computed(() => {
|
const canRegister = computed(() => {
|
||||||
return (
|
return (
|
||||||
|
@ -181,22 +183,71 @@ const credentials = reactive({
|
||||||
const redirect = useRouteQuery("redirect", "");
|
const redirect = useRouteQuery("redirect", "");
|
||||||
const errorCode = useRouteQuery("code", null, enumTransformer(LoginErrorCode));
|
const errorCode = useRouteQuery("code", null, enumTransformer(LoginErrorCode));
|
||||||
|
|
||||||
const {
|
// Login
|
||||||
onDone: onLoginMutationDone,
|
const loginMutation = useMutation(LOGIN);
|
||||||
onError: onLoginMutationError,
|
// Load user identities
|
||||||
mutate: loginMutation,
|
const currentUserIdentitiesQuery = useLazyCurrentUserIdentities();
|
||||||
} = useMutation(LOGIN);
|
// Update user in cache
|
||||||
|
const currentUserMutation = useMutation(UPDATE_CURRENT_USER_CLIENT);
|
||||||
|
// Retrieve preferred location
|
||||||
|
const loggedUserLocationQuery = useLazyQuery<{
|
||||||
|
loggedUser: IUser;
|
||||||
|
}>(LOGGED_USER_LOCATION);
|
||||||
|
|
||||||
onLoginMutationDone(async (result) => {
|
// form submit action
|
||||||
const data = result.data;
|
const loginAction = async (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (submitted.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
submitted.value = true;
|
||||||
|
errors.value = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Step 1: login the user
|
||||||
|
const { data: loginData } = await loginMutation.mutate({
|
||||||
|
email: credentials.email,
|
||||||
|
password: credentials.password,
|
||||||
|
});
|
||||||
submitted.value = false;
|
submitted.value = false;
|
||||||
if (data == null) {
|
if (loginData == null) {
|
||||||
throw new Error("Data is undefined");
|
throw new Error("Login: user's data is undefined");
|
||||||
}
|
}
|
||||||
|
|
||||||
saveUserData(data.login);
|
// Login saved to local storage
|
||||||
await setupClientUserAndActors(data.login);
|
saveUserData(loginData.login);
|
||||||
|
|
||||||
|
// Step 2: save login in apollo cache
|
||||||
|
await currentUserMutation.mutate({
|
||||||
|
id: loginData.login.user.id,
|
||||||
|
email: credentials.email,
|
||||||
|
isLoggedIn: true,
|
||||||
|
role: loginData.login.user.role,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Step 3a: Retrieving user location
|
||||||
|
const loggedUserLocationPromise = loggedUserLocationQuery.load();
|
||||||
|
|
||||||
|
// Step 3b: Setuping user's identities
|
||||||
|
// FIXME this promise never resolved the first time
|
||||||
|
// no idea why !
|
||||||
|
// this appends even with the last version of apollo-composable (4.0.2)
|
||||||
|
// may be related to that : https://github.com/vuejs/apollo/issues/1543
|
||||||
|
// EDIT: now it works :shrug:
|
||||||
|
const currentUserIdentitiesResult = await currentUserIdentitiesQuery.load();
|
||||||
|
if (!currentUserIdentitiesResult) {
|
||||||
|
throw new Error("Loading user's identities failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
await initializeCurrentActor(currentUserIdentitiesResult.loggedUser.actors);
|
||||||
|
|
||||||
|
// Step 3a following
|
||||||
|
const loggedUserLocationResult = await loggedUserLocationPromise;
|
||||||
|
storeUserLocationAndRadiusFromUserSettings(
|
||||||
|
loggedUserLocationResult?.loggedUser?.settings?.location
|
||||||
|
);
|
||||||
|
|
||||||
|
// Soft redirect
|
||||||
if (redirect.value) {
|
if (redirect.value) {
|
||||||
console.debug("We have a redirect", redirect.value);
|
console.debug("We have a redirect", redirect.value);
|
||||||
router.push(redirect.value);
|
router.push(redirect.value);
|
||||||
|
@ -208,10 +259,22 @@ onLoginMutationDone(async (result) => {
|
||||||
window.localStorage.setItem("welcome-back", "yes");
|
window.localStorage.setItem("welcome-back", "yes");
|
||||||
}
|
}
|
||||||
router.replace({ name: RouteName.HOME });
|
router.replace({ name: RouteName.HOME });
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
onLoginMutationError((err) => {
|
// Hard redirect
|
||||||
|
// since we fail to refresh the navbar properly, we force a page reload.
|
||||||
|
// see the explanation of the bug bellow
|
||||||
|
// window.location = redirect.value || "/";
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err instanceof NoIdentitiesException && currentUser.value) {
|
||||||
|
console.debug("No identities, redirecting to profile registration");
|
||||||
|
await router.push({
|
||||||
|
name: RouteName.REGISTER_PROFILE,
|
||||||
|
params: {
|
||||||
|
email: currentUser.value.email,
|
||||||
|
userAlreadyActivated: "true",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
submitted.value = false;
|
submitted.value = false;
|
||||||
if (err.graphQLErrors) {
|
if (err.graphQLErrors) {
|
||||||
|
@ -221,66 +284,8 @@ onLoginMutationError((err) => {
|
||||||
} else if (err.networkError) {
|
} else if (err.networkError) {
|
||||||
errors.value.push(err.networkError.message);
|
errors.value.push(err.networkError.message);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const loginAction = (e: Event) => {
|
|
||||||
e.preventDefault();
|
|
||||||
if (submitted.value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
submitted.value = true;
|
|
||||||
errors.value = [];
|
|
||||||
|
|
||||||
loginMutation({
|
|
||||||
email: credentials.email,
|
|
||||||
password: credentials.password,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const { load: loadIdentities } = useLazyCurrentUserIdentities();
|
|
||||||
|
|
||||||
const { onDone: onCurrentUserMutationDone, mutate: updateCurrentUserMutation } =
|
|
||||||
useMutation(UPDATE_CURRENT_USER_CLIENT);
|
|
||||||
|
|
||||||
onCurrentUserMutationDone(async () => {
|
|
||||||
console.debug("Current user mutation done, now setuping actors…");
|
|
||||||
// since we fail to refresh the navbar properly, we force a page reload.
|
|
||||||
// see the explanation of the bug bellow
|
|
||||||
window.location = redirect.value || "/";
|
|
||||||
try {
|
|
||||||
/* FIXME this promise never resolved the first time
|
|
||||||
no idea why !
|
|
||||||
this appends even with the last version of apollo-composable (4.0.2)
|
|
||||||
may be related to that : https://github.com/vuejs/apollo/issues/1543
|
|
||||||
*/
|
|
||||||
const result = await loadIdentities();
|
|
||||||
console.debug("login, loadIdentities resolved");
|
|
||||||
if (!result) return;
|
|
||||||
await initializeCurrentActor(result.loggedUser.actors);
|
|
||||||
} catch (err: any) {
|
|
||||||
if (err instanceof NoIdentitiesException && currentUser.value) {
|
|
||||||
await router.push({
|
|
||||||
name: RouteName.REGISTER_PROFILE,
|
|
||||||
params: {
|
|
||||||
email: currentUser.value.email,
|
|
||||||
userAlreadyActivated: "true",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
throw err;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
const setupClientUserAndActors = async (login: ILogin): Promise<void> => {
|
|
||||||
console.debug("Setuping client user and actors");
|
|
||||||
updateCurrentUserMutation({
|
|
||||||
id: login.user.id,
|
|
||||||
email: credentials.email,
|
|
||||||
isLoggedIn: true,
|
|
||||||
role: login.user.role,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const hasCaseWarning = computed<boolean>(() => {
|
const hasCaseWarning = computed<boolean>(() => {
|
||||||
|
|
Loading…
Reference in a new issue