From b1cc3868a63348ad84b74e47f6258d3fdb0c7164 Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Fri, 12 Feb 2021 18:19:49 +0100 Subject: [PATCH] Add user setting to provide location and show events near location on homepage Signed-off-by: Thomas Citharel --- .../components/Event/AddressAutoComplete.vue | 15 +- js/src/components/Event/EventCard.vue | 25 +++- .../Event/RecentEventCardWrapper.vue | 35 +++++ js/src/graphql/address.ts | 5 +- js/src/graphql/event.ts | 24 ++++ js/src/graphql/user.ts | 7 + js/src/i18n/en_US.json | 11 +- js/src/i18n/fr_FR.json | 11 +- js/src/types/address.model.ts | 7 +- js/src/types/current-user.model.ts | 19 ++- js/src/types/enums.ts | 4 + js/src/views/Home.vue | 135 +++++++++++++----- js/src/views/Search.vue | 2 +- js/src/views/Settings/Notifications.vue | 9 +- js/src/views/Settings/Preferences.vue | 122 ++++++++++++++-- lib/graphql/resolvers/address.ex | 9 +- lib/graphql/schema/address.ex | 11 ++ lib/graphql/schema/event.ex | 2 +- lib/graphql/schema/user.ex | 27 ++++ lib/mobilizon/users/setting.ex | 14 ++ lib/service/geospatial/addok.ex | 22 ++- lib/service/geospatial/google_maps.ex | 28 ++-- lib/service/geospatial/mimirsbrunn.ex | 22 ++- lib/service/geospatial/nominatim.ex | 37 +++-- lib/service/geospatial/pelias.ex | 36 +++-- lib/service/geospatial/provider.ex | 2 + ...10143432_add_location_settings_to_user.exs | 9 ++ 27 files changed, 538 insertions(+), 112 deletions(-) create mode 100644 js/src/components/Event/RecentEventCardWrapper.vue create mode 100644 priv/repo/migrations/20210210143432_add_location_settings_to_user.exs diff --git a/js/src/components/Event/AddressAutoComplete.vue b/js/src/components/Event/AddressAutoComplete.vue index e98c2fbb3..f156469b5 100644 --- a/js/src/components/Event/AddressAutoComplete.vue +++ b/js/src/components/Event/AddressAutoComplete.vue @@ -71,6 +71,7 @@ import { IConfig } from "../../types/config.model"; }) export default class AddressAutoComplete extends Vue { @Prop({ required: true }) value!: IAddress; + @Prop({ required: false, default: false }) type!: string | false; addressData: IAddress[] = []; @@ -118,13 +119,17 @@ export default class AddressAutoComplete extends Vue { } this.isFetching = true; + const variables: { query: string; locale: string; type?: string } = { + query, + locale: this.$i18n.locale, + }; + if (this.type) { + variables.type = this.type; + } const result = await this.$apollo.query({ query: ADDRESS, fetchPolicy: "network-only", - variables: { - query, - locale: this.$i18n.locale, - }, + variables, }); this.addressData = result.data.searchAddress.map( @@ -144,7 +149,7 @@ export default class AddressAutoComplete extends Vue { @Watch("value") updateEditing(): void { - if (!(this.value && this.value.id)) return; + if (!this.value?.id) return; this.selected = this.value; const address = new Address(this.selected); this.queryText = `${address.poiInfos.name} ${address.poiInfos.alternativeName}`; diff --git a/js/src/components/Event/EventCard.vue b/js/src/components/Event/EventCard.vue index f41a537bb..f6f8a7ee0 100644 --- a/js/src/components/Event/EventCard.vue +++ b/js/src/components/Event/EventCard.vue @@ -39,13 +39,24 @@ />
-

{{ event.title }}

-
+

{{ event.title }}

+
- + {{ event.physicalAddress.description }}, {{ event.physicalAddress.locality }} + + {{ event.physicalAddress.description }} +
@@ -130,6 +141,14 @@ export default class EventCard extends Vue { this.event.organizerActor || this.mergedOptions.organizerActor ); } + + get isDescriptionDifferentFromLocality(): boolean { + return ( + this.event?.physicalAddress?.description !== + this.event?.physicalAddress?.locality && + this.event?.physicalAddress?.description !== undefined + ); + } } diff --git a/js/src/components/Event/RecentEventCardWrapper.vue b/js/src/components/Event/RecentEventCardWrapper.vue new file mode 100644 index 000000000..bee20a990 --- /dev/null +++ b/js/src/components/Event/RecentEventCardWrapper.vue @@ -0,0 +1,35 @@ + + + diff --git a/js/src/graphql/address.ts b/js/src/graphql/address.ts index 9e3fdbc0e..527b86258 100644 --- a/js/src/graphql/address.ts +++ b/js/src/graphql/address.ts @@ -15,10 +15,11 @@ originId `; export const ADDRESS = gql` - query($query:String!, $locale: String) { + query($query:String!, $locale: String, $type: AddressSearchType) { searchAddress( query: $query, - locale: $locale + locale: $locale, + type: $type ) { ${$addressFragment} } diff --git a/js/src/graphql/event.ts b/js/src/graphql/event.ts index 834a25f80..1012798c2 100644 --- a/js/src/graphql/event.ts +++ b/js/src/graphql/event.ts @@ -207,6 +207,7 @@ export const FETCH_EVENTS = gql` endsOn status visibility + insertedAt picture { id url @@ -673,3 +674,26 @@ export const FETCH_GROUP_EVENTS = gql` } } `; + +export const CLOSE_EVENTS = gql` + query CloseEvents($location: String, $radius: Float) { + searchEvents(location: $location, radius: $radius, page: 1, limit: 10) { + total + elements { + id + title + uuid + beginsOn + picture { + id + url + } + tags { + slug + title + } + __typename + } + } + } +`; diff --git a/js/src/graphql/user.ts b/js/src/graphql/user.ts index 158be5e71..b794ef1a8 100644 --- a/js/src/graphql/user.ts +++ b/js/src/graphql/user.ts @@ -125,6 +125,11 @@ export const USER_SETTINGS_FRAGMENT = gql` notificationBeforeEvent notificationPendingParticipation notificationPendingMembership + location { + range + geohash + name + } } `; @@ -149,6 +154,7 @@ export const SET_USER_SETTINGS = gql` $notificationBeforeEvent: Boolean $notificationPendingParticipation: NotificationPendingEnum $notificationPendingMembership: NotificationPendingEnum + $location: LocationInput ) { setUserSettings( timezone: $timezone @@ -157,6 +163,7 @@ export const SET_USER_SETTINGS = gql` notificationBeforeEvent: $notificationBeforeEvent notificationPendingParticipation: $notificationPendingParticipation notificationPendingMembership: $notificationPendingMembership + location: $location ) { ...UserSettingFragment } diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index 669100da6..c03e07c49 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -850,5 +850,14 @@ "{instanceName} is an instance of {mobilizon_link}, a free software built with the community.": "{instanceName} is an instance of {mobilizon_link}, a free software built with the community.", "Open a topic on our forum": "Open a topic on our forum", "Open an issue on our bug tracker (advanced users)": "Open an issue on our bug tracker (advanced users)", - "Unable to copy to clipboard": "Unable to copy to clipboard" + "Unable to copy to clipboard": "Unable to copy to clipboard", + "{count} km": "{count} km", + "City or region": "City or region", + "Select a radius": "Select a radius", + "Your city or region and the radius will only be used to suggest you events nearby.": "Your city or region and the radius will only be used to suggest you events nearby.", + "Your upcoming events": "Your upcoming events", + "Last published events": "Last published events", + "On {instance}": "On {instance}", + "Close events": "Close events", + "Within {number} kilometers of {place}": "|Within one kilometer of {place}|Within {number} kilometers of {place}" } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 30038166f..d0c26e188 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -945,5 +945,14 @@ "{instanceName} is an instance of {mobilizon_link}, a free software built with the community.": "{instanceName} est une instance de {mobilizon_link}, un logiciel libre construit de manière communautaire.", "Open a topic on our forum": "Ouvrir un sujet sur notre forum", "Open an issue on our bug tracker (advanced users)": "Ouvrir un ticket sur notre système de suivi des bugs (utilisateur⋅ices avancé⋅es)", - "Unable to copy to clipboard": "Impossible de copier dans le presse-papiers" + "Unable to copy to clipboard": "Impossible de copier dans le presse-papiers", + "Select a radius": "Select a radius", + "{count} km": "{count} km", + "City or region": "Ville ou région", + "Your city or region and the radius will only be used to suggest you events nearby.": "Votre ville ou région et le rayon seront uniquement utilisé pour vous suggérer des événements proches.", + "Your upcoming events": "Vos événements à venir", + "Last published events": "Derniers événements publiés", + "On {instance}": "Sur {instance}", + "Close events": "Événements proches", + "Within {number} kilometers of {place}": "|Dans un rayon d'un kilomètre de {place}|Dans un rayon de {number} kilomètres de {place}" } diff --git a/js/src/types/address.model.ts b/js/src/types/address.model.ts index 7ba83b53d..c1cee4738 100644 --- a/js/src/types/address.model.ts +++ b/js/src/types/address.model.ts @@ -66,9 +66,9 @@ export class Address implements IAddress { let alternativeName = ""; let poiIcon: IPOIIcon = poiIcons.default; // Google Maps doesn't have a type - if (this.type == null && this.description === this.street) + if (this.type == null && this.description === this.street) { this.type = "house"; - + } switch (this.type) { case "house": name = this.description; @@ -123,6 +123,9 @@ export class Address implements IAddress { if (name && alternativeName) { return `${name}, ${alternativeName}`; } + if (name) { + return name; + } return ""; } diff --git a/js/src/types/current-user.model.ts b/js/src/types/current-user.model.ts index 2c834c552..051b31f7f 100644 --- a/js/src/types/current-user.model.ts +++ b/js/src/types/current-user.model.ts @@ -12,13 +12,20 @@ export interface ICurrentUser { defaultActor?: IPerson; } +export interface IUserPreferredLocation { + range?: number; + name?: string; + geohash?: string; +} + export interface IUserSettings { - timezone: string; - notificationOnDay: boolean; - notificationEachWeek: boolean; - notificationBeforeEvent: boolean; - notificationPendingParticipation: INotificationPendingEnum; - notificationPendingMembership: INotificationPendingEnum; + timezone?: string; + notificationOnDay?: boolean; + notificationEachWeek?: boolean; + notificationBeforeEvent?: boolean; + notificationPendingParticipation?: INotificationPendingEnum; + notificationPendingMembership?: INotificationPendingEnum; + location?: IUserPreferredLocation; } export interface IUser extends ICurrentUser { diff --git a/js/src/types/enums.ts b/js/src/types/enums.ts index 18c5d7da6..80f36a31e 100644 --- a/js/src/types/enums.ts +++ b/js/src/types/enums.ts @@ -178,3 +178,7 @@ export enum GroupVisibility { UNLISTED = "UNLISTED", PRIVATE = "PRIVATE", } + +export enum AddressSearchType { + ADMINISTRATIVE = "ADMINISTRATIVE", +} diff --git a/js/src/views/Home.vue b/js/src/views/Home.vue index 1a8a27520..d5ba7ae83 100644 --- a/js/src/views/Home.vue +++ b/js/src/views/Home.vue @@ -47,20 +47,23 @@ -