From 5c57f1ce3ca99351f4f8120cb605f95b6bd2b1a5 Mon Sep 17 00:00:00 2001 From: ty kayn Date: Thu, 17 Dec 2020 11:26:25 +0100 Subject: [PATCH] :zap: if an event has geo coordinates, add links to routing on OSM, with correct place and zoom of 14, 3 buttons to get routig as car, bike, and by feet. Signed-off-by: Baptiste Lemoine --- config/config.exs | 3 + js/schema.graphql | 19 ++- js/src/graphql/config.ts | 3 + js/src/i18n/en_US.json | 9 +- js/src/i18n/fr_FR.json | 5 +- js/src/types/address.model.ts | 8 +- js/src/types/config.model.ts | 5 +- js/src/types/enums.ts | 12 ++ js/src/views/Event/Event.vue | 182 +++++++++++++++++++++++++--- js/src/views/Search.vue | 2 +- js/tests/unit/specs/mocks/config.ts | 3 + lib/graphql/resolvers/config.ex | 3 + lib/graphql/schema/config.ex | 15 ++- lib/mobilizon/config.ex | 4 + yarn.lock | 4 + 15 files changed, 250 insertions(+), 27 deletions(-) mode change 100644 => 100755 js/src/views/Event/Event.vue create mode 100644 yarn.lock diff --git a/config/config.exs b/config/config.exs index 5874de5af..23f3cb4ee 100644 --- a/config/config.exs +++ b/config/config.exs @@ -210,6 +210,9 @@ config :mobilizon, :maps, tiles: [ endpoint: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", attribution: "© The OpenStreetMap Contributors" + ], + routing: [ + type: :openstreetmap ] config :mobilizon, :anonymous, diff --git a/js/schema.graphql b/js/schema.graphql index c9e3930dd..86f757e80 100644 --- a/js/schema.graphql +++ b/js/schema.graphql @@ -238,6 +238,12 @@ type Tag { related: [Tag] } +"Instance map routing configuration" +type Routing { + "The instance's routing type" + type: RoutingType +} + "Language information" type Language { "The iso-639-3 language code" @@ -420,6 +426,9 @@ type Events { type Maps { "The instance's maps tiles configuration" tiles: Tiles + + "The instance's maps routing configuration" + routing: Routing } "Search groups result" @@ -659,6 +668,14 @@ interface Interactable { url: String } +enum RoutingType { + "Redirect to openstreetmap.org's direction endpoint" + OPENSTREETMAP + + "Redirect to Google Maps's direction endpoint" + GOOGLE_MAPS +} + "A struct containing the id of the deleted object" type DeletedObject { id: ID @@ -2856,7 +2873,7 @@ type PaginatedGroupList { total: Int } -"Instance tiles configuration" +"Instance map tiles configuration" type Tiles { "The instance's tiles endpoint" endpoint: String diff --git a/js/src/graphql/config.ts b/js/src/graphql/config.ts index 33d3e40e2..3dd903581 100644 --- a/js/src/graphql/config.ts +++ b/js/src/graphql/config.ts @@ -51,6 +51,9 @@ export const CONFIG = gql` endpoint attribution } + routing { + type + } } geocoding { provider diff --git a/js/src/i18n/en_US.json b/js/src/i18n/en_US.json index c4a686b25..24f6c7b79 100644 --- a/js/src/i18n/en_US.json +++ b/js/src/i18n/en_US.json @@ -813,12 +813,15 @@ "View all events": "View all events", "You will find here all the events you have created or of which you are a participant.": "You will find here all the events you have created or of which you are a participant.", "Create event": "Create event", - "You didn't create or join any event yet": "You didn't create or join any event yet", + "You didn't create or join any event yet.": "You didn't create or join any event yet.", "create an event": "create an event", "explore the events": "explore the events", "Do you wish to {create_event} or {explore_events}?": "Do you wish to {create_event} or {explore_events}?", - "You are not part of any group": "You are not part of any group", + "You are not part of any group.": "You are not part of any group.", "create a group": "create a group", "explore the groups": "explore the groups", - "Do you wish to {create_group} or {explore_groups}?": "Do you wish to {create_group} or {explore_groups}?" + "Do you wish to {create_group} or {explore_groups}?": "Do you wish to {create_group} or {explore_groups}?", + "Type or select a date…": "Type or select a date…", + "Getting there": "Getting there", + "Groups are not enabled on this instance.": "Groups are not enabled on this instance." } diff --git a/js/src/i18n/fr_FR.json b/js/src/i18n/fr_FR.json index 8d4b9a144..f3a5e6570 100644 --- a/js/src/i18n/fr_FR.json +++ b/js/src/i18n/fr_FR.json @@ -914,5 +914,8 @@ "You are not part of any group.": "Vous ne faites partie d'aucun groupe.", "create a group": "créer un groupe", "explore the groups": "explorer les groupes", - "Do you wish to {create_group} or {explore_groups}?": "Voulez-vous {create_group} ou {explore_groups} ?" + "Do you wish to {create_group} or {explore_groups}?": "Voulez-vous {create_group} ou {explore_groups} ?", + "Type or select a date…": "Entrez ou sélectionnez une date…", + "Getting there": "S'y rendre", + "Groups are not enabled on this instance.": "Les groupes ne sont pas activés sur cette instance." } diff --git a/js/src/types/address.model.ts b/js/src/types/address.model.ts index 6bc2f7052..7ba83b53d 100644 --- a/js/src/types/address.model.ts +++ b/js/src/types/address.model.ts @@ -15,6 +15,12 @@ export interface IAddress { originId?: string; } +export interface IPoiInfo { + name: string; + alternativeName: string; + poiIcon: IPOIIcon; +} + export class Address implements IAddress { country = ""; @@ -54,7 +60,7 @@ export class Address implements IAddress { this.originId = hash.originId; } - get poiInfos(): { name: string; alternativeName: string; poiIcon: IPOIIcon } { + get poiInfos(): IPoiInfo { /* generate name corresponding to poi type */ let name = ""; let alternativeName = ""; diff --git a/js/src/types/config.model.ts b/js/src/types/config.model.ts index af43f666e..6d391d0a8 100644 --- a/js/src/types/config.model.ts +++ b/js/src/types/config.model.ts @@ -1,4 +1,4 @@ -import { InstancePrivacyType, InstanceTermsType } from "./enums"; +import { InstancePrivacyType, InstanceTermsType, RoutingType } from "./enums"; import type { IProvider } from "./resource"; export interface IOAuthProvider { @@ -58,6 +58,9 @@ export interface IConfig { endpoint: string; attribution: string | null; }; + routing: { + type: RoutingType; + }; }; geocoding: { provider: string; diff --git a/js/src/types/enums.ts b/js/src/types/enums.ts index 9eeeb5af9..a081a3463 100644 --- a/js/src/types/enums.ts +++ b/js/src/types/enums.ts @@ -160,3 +160,15 @@ export enum Openness { MODERATED = "MODERATED", OPEN = "OPEN", } + +export enum RoutingType { + OPENSTREETMAP = "OPENSTREETMAP", + GOOGLE_MAPS = "GOOGLE_MAPS", +} + +export enum RoutingTransportationType { + FOOT = "FOOT", + BIKE = "BIKE", + TRANSIT = "TRANSIT", + CAR = "CAR", +} diff --git a/js/src/views/Event/Event.vue b/js/src/views/Event/Event.vue old mode 100644 new mode 100755 index 5c665e347..290dee24f --- a/js/src/views/Event/Event.vue +++ b/js/src/views/Event/Event.vue @@ -84,9 +84,9 @@ {{ tag.title }}

- {{ - $t("Draft") - }} + {{ $t("Draft") }} + {{ $t("Show map") }} @@ -526,8 +526,8 @@ class="button" ref="cancelButton" @click="isJoinConfirmationModalActive = false" - >{{ $t("Cancel") }} + >{{ $t("Cancel") }} + {{ $t("Confirm my participation") }} @@ -537,17 +537,77 @@ -
- +
@@ -563,6 +623,8 @@ import { EventStatus, EventVisibility, ParticipantRole, + RoutingTransportationType, + RoutingType, } from "@/types/enums"; import { EVENT_PERSON_PARTICIPATION, @@ -602,6 +664,7 @@ import ActorCard from "../../components/Account/ActorCard.vue"; import PopoverActorCard from "../../components/Account/PopoverActorCard.vue"; import { IParticipant } from "../../types/participant.model"; +// noinspection TypeScriptValidateTypes @Component({ components: { EventMetadataBlock, @@ -734,6 +797,65 @@ export default class Event extends EventMixin { messageForConfirmation = ""; + RoutingParamType = { + [RoutingType.OPENSTREETMAP]: { + [RoutingTransportationType.FOOT]: "engine=fossgis_osrm_foot", + [RoutingTransportationType.BIKE]: "engine=fossgis_osrm_bike", + [RoutingTransportationType.TRANSIT]: null, + [RoutingTransportationType.CAR]: "engine=fossgis_osrm_car", + }, + [RoutingType.GOOGLE_MAPS]: { + [RoutingTransportationType.FOOT]: "dirflg=w", + [RoutingTransportationType.BIKE]: "dirflg=b", + [RoutingTransportationType.TRANSIT]: "dirflg=r", + [RoutingTransportationType.CAR]: "driving", + }, + }; + + makeNavigationPath( + transportationType: RoutingTransportationType + ): string | undefined { + const geometry = this.physicalAddress?.geom; + if (geometry) { + const routingType = this.config.maps.routing.type; + /** + * build urls to routing map + */ + if (!this.RoutingParamType[routingType][transportationType]) { + return; + } + + const urlGeometry = geometry.split(";").reverse().join(","); + + switch (routingType) { + case RoutingType.GOOGLE_MAPS: + return `https://maps.google.com/?saddr=Current+Location&daddr=${urlGeometry}&${this.RoutingParamType[routingType][transportationType]}`; + case RoutingType.OPENSTREETMAP: + default: { + const bboxX = geometry.split(";").reverse()[0]; + const bboxY = geometry.split(";").reverse()[1]; + return `https://www.openstreetmap.org/directions?from=&to=${urlGeometry}&${this.RoutingParamType[routingType][transportationType]}#map=14/${bboxX}/${bboxY}`; + } + } + } + } + + get addressLinkToRouteByCar(): undefined | string { + return this.makeNavigationPath(RoutingTransportationType.CAR); + } + + get addressLinkToRouteByBike(): undefined | string { + return this.makeNavigationPath(RoutingTransportationType.BIKE); + } + + get addressLinkToRouteByFeet(): undefined | string { + return this.makeNavigationPath(RoutingTransportationType.FOOT); + } + + get addressLinkToRouteByTransit(): undefined | string { + return this.makeNavigationPath(RoutingTransportationType.TRANSIT); + } + get eventTitle(): undefined | string { if (!this.event) return undefined; return this.event.title; @@ -1096,6 +1218,7 @@ export default class Event extends EventMixin { get physicalAddress(): Address | null { if (!this.event.physicalAddress) return null; + return new Address(this.event.physicalAddress); } @@ -1225,6 +1348,7 @@ div.sidebar { a { text-decoration: none; } + span { &.tag { margin: 0 2px; @@ -1389,9 +1513,31 @@ a.participations-link { font-size: 1rem; } -div.map { - height: 900px; - width: 100%; - padding: 25px 5px 0; +.map-modal { + .modal-card-head { + justify-content: flex-end; + button.delete { + margin-right: 1rem; + } + } + + section.map { + height: calc(100% - 8rem); + width: calc(100% - 20px); + } + + section.map-footer { + p.address { + margin: 1rem auto; + } + div.buttons { + justify-content: center; + } + } +} + +.no-border { + border: 0; + cursor: auto; } diff --git a/js/src/views/Search.vue b/js/src/views/Search.vue index e02794676..765c96899 100644 --- a/js/src/views/Search.vue +++ b/js/src/views/Search.vue @@ -127,7 +127,7 @@ - {{ $t("Groups are not enabled on your server.") }} + {{ $t("Groups are not enabled on this instance.") }}
diff --git a/js/tests/unit/specs/mocks/config.ts b/js/tests/unit/specs/mocks/config.ts index 84c4f80b1..271d71ce3 100644 --- a/js/tests/unit/specs/mocks/config.ts +++ b/js/tests/unit/specs/mocks/config.ts @@ -56,6 +56,9 @@ export const configMock = { attribution: "© The OpenStreetMap Contributors", endpoint: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", }, + routing: { + type: "OPENSTREETMAP", + }, }, name: "Mobilizon", registrationsAllowlist: false, diff --git a/lib/graphql/resolvers/config.ex b/lib/graphql/resolvers/config.ex index 181caa0e4..a1b8c3e8c 100644 --- a/lib/graphql/resolvers/config.ex +++ b/lib/graphql/resolvers/config.ex @@ -117,6 +117,9 @@ defmodule Mobilizon.GraphQL.Resolvers.Config do tiles: %{ endpoint: Config.instance_maps_tiles_endpoint(), attribution: Config.instance_maps_tiles_attribution() + }, + routing: %{ + type: Config.instance_maps_routing_type() } }, resource_providers: Config.instance_resource_providers(), diff --git a/lib/graphql/schema/config.ex b/lib/graphql/schema/config.ex index c48058446..a2d9d02ab 100644 --- a/lib/graphql/schema/config.ex +++ b/lib/graphql/schema/config.ex @@ -106,16 +106,29 @@ defmodule Mobilizon.GraphQL.Schema.ConfigType do """ object :maps do field(:tiles, :tiles, description: "The instance's maps tiles configuration") + field(:routing, :routing, description: "The instance's maps routing configuration") end @desc """ - Instance tiles configuration + Instance map tiles configuration """ object :tiles do field(:endpoint, :string, description: "The instance's tiles endpoint") field(:attribution, :string, description: "The instance's tiles attribution text") end + @desc """ + Instance map routing configuration + """ + object :routing do + field(:type, :routing_type, description: "The instance's routing type") + end + + enum :routing_type do + value(:openstreetmap, description: "Redirect to openstreetmap.org's direction endpoint") + value(:google_maps, description: "Redirect to Google Maps's direction endpoint") + end + @desc """ Instance anonymous configuration """ diff --git a/lib/mobilizon/config.ex b/lib/mobilizon/config.ex index 87ad8ce5c..600ae63c8 100644 --- a/lib/mobilizon/config.ex +++ b/lib/mobilizon/config.ex @@ -151,6 +151,10 @@ defmodule Mobilizon.Config do def instance_maps_tiles_attribution, do: Application.get_env(:mobilizon, :maps)[:tiles][:attribution] + @spec instance_maps_routing_type :: atom() + def instance_maps_routing_type, + do: Application.get_env(:mobilizon, :maps)[:routing][:type] + @spec anonymous_participation? :: boolean def anonymous_participation?, do: Application.get_env(:mobilizon, :anonymous)[:participation][:allowed] diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..fb57ccd13 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +