diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index 12e8bc0..249e884 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -12,6 +12,7 @@ import {Widths} from "./Layers/Widths"; import {StreetWidth} from "./Layouts/StreetWidth"; import {NatureReserves} from "./Layers/NatureReserves"; import {Natuurpunt} from "./Layouts/Natuurpunt"; +import Translations from "../UI/i18n/Translations"; export class AllKnownLayouts { public static allSets = AllKnownLayouts.AllLayouts(); diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index e1dbc86..abfdaa2 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -13,7 +13,7 @@ export class LayerDefinition { /** - * This name is shown in the 'add XXX button' + * This name is used in the 'hide or show this layer'-buttons */ name: string | UIElement; @@ -26,7 +26,12 @@ export class LayerDefinition { * These tags are added whenever a new point is added by the user on the map. * This is the ideal place to add extra info, such as "fixme=added by MapComplete, geometry should be checked" */ - newElementTags: Tag[] + presets: { + tags: Tag[], + title: string | UIElement, + description?: string | UIElement, + icon?: string + }[] /** * Not really used anymore * This is meant to serve as icon in the buttons @@ -91,9 +96,14 @@ export class LayerDefinition { static WAYHANDLING_CENTER_AND_WAY = 2; constructor(options: { - name: string | UIElement, + name: string, description: string | UIElement, - newElementTags: Tag[], + presets: { + tags: Tag[], + title: string | UIElement, + description?: string | UIElement, + icon?: string + }[], icon: string, minzoom: number, overpassFilter: TagsFilter, @@ -112,7 +122,7 @@ export class LayerDefinition { this.name = options.name; this.description = options.description; this.maxAllowedOverlapPercentage = options.maxAllowedOverlapPercentage ?? 0; - this.newElementTags = options.newElementTags; + this.presets = options.presets; this.icon = options.icon; this.minzoom = options.minzoom; this.overpassFilter = options.overpassFilter; diff --git a/Customizations/Layers/BikeCafes.ts b/Customizations/Layers/BikeCafes.ts index 2f9ca6f..0eeb3bd 100644 --- a/Customizations/Layers/BikeCafes.ts +++ b/Customizations/Layers/BikeCafes.ts @@ -9,6 +9,7 @@ import Website from "../Questions/Website"; import CafeRepair from "../Questions/bike/CafeRepair"; import CafeDiy from "../Questions/bike/CafeDiy"; import CafePump from "../Questions/bike/CafePump"; +import {EmailQuestion} from "../Questions/EmailQuestion"; export default class BikeCafes extends LayerDefinition { @@ -25,11 +26,18 @@ export default class BikeCafes extends LayerDefinition { new Tag("pub", "cycling") ]) ]) - this.newElementTags = [ - new Tag("amenity", "pub"), - new Tag("pub", "cycling"), + + this.presets = [ + { + title: Translations.t.cyclofix.cafe.title, + tags : [ + new Tag("amenity", "pub"), + new Tag("pub", "cycling"), + ] + } ] - this.maxAllowedOverlapPercentage = 10 + + this.maxAllowedOverlapPercentage = 10; this.minzoom = 13 this.style = this.generateStyleFunction() @@ -37,8 +45,9 @@ export default class BikeCafes extends LayerDefinition { this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), new CafeName(), - new PhoneNumberQuestion("{name}"), new Website("{name}"), + new PhoneNumberQuestion("{name}"), + new EmailQuestion("{name}"), new CafeRepair(), new CafeDiy(), new CafePump() @@ -59,4 +68,4 @@ export default class BikeCafes extends LayerDefinition { } } } -} +} \ No newline at end of file diff --git a/Customizations/Layers/BikeOtherShops.ts b/Customizations/Layers/BikeOtherShops.ts index 95d218f..60e2bc7 100644 --- a/Customizations/Layers/BikeOtherShops.ts +++ b/Customizations/Layers/BikeOtherShops.ts @@ -27,7 +27,7 @@ export default class BikeOtherShops extends LayerDefinition { anyValueExcept("shop", "bicycle"), new Tag(/^service:bicycle:/, "*"), ]) - this.newElementTags = undefined + this.presets = [] this.maxAllowedOverlapPercentage = 10 this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY diff --git a/Customizations/Layers/BikeParkings.ts b/Customizations/Layers/BikeParkings.ts index 6a45a6c..34ab337 100644 --- a/Customizations/Layers/BikeParkings.ts +++ b/Customizations/Layers/BikeParkings.ts @@ -19,9 +19,13 @@ export default class BikeParkings extends LayerDefinition { this.name = Translations.t.cyclofix.parking.name; this.icon = "./assets/bike/parking.svg"; this.overpassFilter = new Tag("amenity", "bicycle_parking"); - this.newElementTags = [ - new Tag("amenity", "bicycle_parking"), - ]; + this.presets = [{ + title: Translations.t.cyclofix.parking.title, + tags: [ + new Tag("amenity", "bicycle_parking"), + ] + }]; + this.maxAllowedOverlapPercentage = 10; this.minzoom = 13; diff --git a/Customizations/Layers/BikeShops.ts b/Customizations/Layers/BikeShops.ts index 9912981..46e56cb 100644 --- a/Customizations/Layers/BikeShops.ts +++ b/Customizations/Layers/BikeShops.ts @@ -13,6 +13,7 @@ import ShopSecondHand from "../Questions/bike/ShopSecondHand"; import { TagRenderingOptions } from "../TagRendering"; import {PhoneNumberQuestion} from "../Questions/PhoneNumberQuestion"; import Website from "../Questions/Website"; +import {EmailQuestion} from "../Questions/EmailQuestion"; export default class BikeShops extends LayerDefinition { @@ -24,9 +25,12 @@ export default class BikeShops extends LayerDefinition { this.name = Translations.t.cyclofix.shop.name this.icon = "./assets/bike/repair_shop.svg" this.overpassFilter = new Tag("shop", "bicycle"); - this.newElementTags = [ - new Tag("shop", "bicycle"), - ] + this.presets = [{ + title: Translations.t.cyclofix.shop.title, + tags: [ + new Tag("shop", "bicycle"), + ] + }] this.maxAllowedOverlapPercentage = 10 this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY @@ -52,8 +56,9 @@ export default class BikeShops extends LayerDefinition { this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), new ShopName(), - new PhoneNumberQuestion("{name}"), new Website("{name}"), + new PhoneNumberQuestion("{name}"), + new EmailQuestion("{name}"), new ShopRetail(), new ShopRental(), new ShopRepair(), diff --git a/Customizations/Layers/BikeStations.ts b/Customizations/Layers/BikeStations.ts index f7f1b22..6638a79 100644 --- a/Customizations/Layers/BikeStations.ts +++ b/Customizations/Layers/BikeStations.ts @@ -1,5 +1,5 @@ import {LayerDefinition} from "../LayerDefinition"; -import {And, Tag, TagsFilter, Or} from "../../Logic/TagsFilter"; +import {And, Tag, TagsFilter, Or, Not} from "../../Logic/TagsFilter"; import BikeStationChain from "../Questions/bike/StationChain"; import BikeStationPumpTools from "../Questions/bike/StationPumpTools"; import BikeStationStand from "../Questions/bike/StationStand"; @@ -16,10 +16,14 @@ import { TagRenderingOptions } from "../TagRendering"; export default class BikeStations extends LayerDefinition { + private readonly repairStation = new Tag("amenity", "bicycle_repair_station"); private readonly pump = new Tag("service:bicycle:pump", "yes"); + private readonly nopump = new Tag("service:bicycle:pump", "no"); private readonly pumpOperationalAny = new Tag("service:bicycle:pump:operational_status", "yes"); private readonly pumpOperationalOk = new Or([new Tag("service:bicycle:pump:operational_status", "yes"), new Tag("service:bicycle:pump:operational_status", "operational"), new Tag("service:bicycle:pump:operational_status", "ok"), new Tag("service:bicycle:pump:operational_status", "")]); private readonly tools = new Tag("service:bicycle:tools", "yes"); + private readonly notools = new Tag("service:bicycle:tools", "no"); + private readonly to = Translations.t.cyclofix.station constructor() { @@ -27,13 +31,27 @@ export default class BikeStations extends LayerDefinition { this.name = Translations.t.cyclofix.station.name; this.icon = "./assets/bike/repair_station_pump.svg"; - this.overpassFilter = new And([ - new Tag("amenity", "bicycle_repair_station") - ]); + const tr = Translations.t.cyclofix.station + this.overpassFilter = this.repairStation; + this.presets = [ + { + title: tr.titlePump, + description: tr.services.pump, + tags: [this.repairStation, this.pump, this.notools] + }, + { + title: tr.titleRepair, + description: tr.services.tools, + tags: [this.repairStation, this.tools, this.nopump] + }, + { + title: tr.titlePumpAndRepair, + description: tr.services.both, + tags: [this.repairStation, this.tools, this.pump] + }, + + ] - this.newElementTags = [ - new Tag("amenity", "bicycle_repair_station") - ]; this.maxAllowedOverlapPercentage = 10; this.minzoom = 13; diff --git a/Customizations/Layers/Birdhide.ts b/Customizations/Layers/Birdhide.ts index ea9e642..0acb1bd 100644 --- a/Customizations/Layers/Birdhide.ts +++ b/Customizations/Layers/Birdhide.ts @@ -18,7 +18,12 @@ export class Birdhide extends LayerDefinition { icon: "assets/nature/birdhide.svg", minzoom: 12, wayHandling: LayerDefinition.WAYHANDLING_CENTER_AND_WAY, - newElementTags: [Birdhide.birdhide], + presets: [ + { + title: "Vogelkijkplaats", + tags: [Birdhide.birdhide] + } + ], style(tags: any): { color: string; icon: any } { return {color: "", icon: undefined}; }, diff --git a/Customizations/Layers/Bookcases.ts b/Customizations/Layers/Bookcases.ts index 868ce3b..27e7275 100644 --- a/Customizations/Layers/Bookcases.ts +++ b/Customizations/Layers/Bookcases.ts @@ -12,7 +12,11 @@ export class Bookcases extends LayerDefinition { super(); this.name = "boekenkast"; - this.newElementTags = [new Tag("amenity", "public_bookcase")]; + this.presets = [{ + tags: [new Tag("amenity", "public_bookcase")], + description: "Add a new bookcase here", + title: Translations.t.bookcases.bookcase, + }]; this.icon = "./assets/bookcase.svg"; this.overpassFilter = new Tag("amenity", "public_bookcase"); this.minzoom = 11; diff --git a/Customizations/Layers/Bos.ts b/Customizations/Layers/Bos.ts index 36cf4bf..7cfe489 100644 --- a/Customizations/Layers/Bos.ts +++ b/Customizations/Layers/Bos.ts @@ -22,10 +22,15 @@ export class Bos extends LayerDefinition { ); - this.newElementTags = [ - new Tag("landuse", "forest"), - new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen") - ]; + this.presets = [{ + title: "Bos", + description: "Voeg een ontbrekend bos toe aan de kaart", + icon: undefined, + tags: [ + new Tag("landuse", "forest"), + new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen") + ] + }]; this.maxAllowedOverlapPercentage = 10; this.minzoom = 13; diff --git a/Customizations/Layers/DrinkingWater.ts b/Customizations/Layers/DrinkingWater.ts index 579eed4..490fbcb 100644 --- a/Customizations/Layers/DrinkingWater.ts +++ b/Customizations/Layers/DrinkingWater.ts @@ -21,9 +21,10 @@ export class DrinkingWater extends LayerDefinition { ]); - this.newElementTags = [ - new Tag("amenity", "drinking_water"), - ]; + this.presets = [{ + title: Translations.t.cyclofix.drinking_water.title, + tags: [new Tag("amenity", "drinking_water")] + }]; this.maxAllowedOverlapPercentage = 10; this.wayHandling = LayerDefinition.WAYHANDLING_CENTER_AND_WAY diff --git a/Customizations/Layers/GhostBike.ts b/Customizations/Layers/GhostBike.ts index 751f72f..77f5158 100644 --- a/Customizations/Layers/GhostBike.ts +++ b/Customizations/Layers/GhostBike.ts @@ -15,6 +15,15 @@ export class GhostBike extends LayerDefinition { this.description = "A ghost bike is a memorial for a cyclist who died in a traffic accident," + " in the form of a white bicycle placed permanently near the accident location."; + + this.presets = [ + { + title: "Ghost bike", + description: "Add a missing ghost bike to the map", + tags: [new Tag("historic", "memorial"), new Tag("memorial", "ghost_bike")] + } + ] + this.elementsToShow = [ new FixedText(this.description), new ImageCarouselWithUploadConstructor(), diff --git a/Customizations/Layers/GrbToFix.ts b/Customizations/Layers/GrbToFix.ts index d92f8b3..baecfb8 100644 --- a/Customizations/Layers/GrbToFix.ts +++ b/Customizations/Layers/GrbToFix.ts @@ -8,7 +8,7 @@ export class GrbToFix extends LayerDefinition { super(); this.name = "grb"; - this.newElementTags = undefined; + this.presets = []; this.icon = "./assets/star.svg"; this.overpassFilter = new Regex("fixme", "GRB"); this.minzoom = 13; diff --git a/Customizations/Layers/InformationBoard.ts b/Customizations/Layers/InformationBoard.ts index 7b696db..61c9db8 100644 --- a/Customizations/Layers/InformationBoard.ts +++ b/Customizations/Layers/InformationBoard.ts @@ -11,7 +11,14 @@ export class InformationBoard extends LayerDefinition { description: "Een informatiebord of kaart", minzoom: 12, overpassFilter: new Tag("tourism", "information"), - newElementTags: [new Tag("tourism", "information")], + presets: [{ + title: "Informatiebord", + tags: [new Tag("tourism", "information")] + }, + { + title: "Kaart", + tags: [new Tag("tourism", "information"), new Tag("information", "map")] + }], maxAllowedOverlapPercentage: 0, icon: "assets/nature/info.png", }); diff --git a/Customizations/Layers/Map.ts b/Customizations/Layers/Map.ts index 183db79..53ab452 100644 --- a/Customizations/Layers/Map.ts +++ b/Customizations/Layers/Map.ts @@ -12,16 +12,18 @@ export class Map extends LayerDefinition { this.minzoom = 12; this.overpassFilter = new Tag("information", "map"); - this.newElementTags = [new Tag("tourism", "information"), new Tag("information", "map")]; + this.presets = [{ + title: "Map", + tags: [new Tag("tourism", "information"), new Tag("information", "map")] + }]; + - const isOsmSource = new Tag("map_source", "OpenStreetMap"); - - - + + this.style = (properties) => { let icon = "assets/map.svg"; - if(isOsmSource.matchesProperties(properties)){ + if (isOsmSource.matchesProperties(properties)) { icon = "assets/osm-logo-white-bg.svg"; const attr = properties["map_source:attribution"]; diff --git a/Customizations/Layers/NatureReserves.ts b/Customizations/Layers/NatureReserves.ts index d3f13c6..bbf42f4 100644 --- a/Customizations/Layers/NatureReserves.ts +++ b/Customizations/Layers/NatureReserves.ts @@ -18,20 +18,25 @@ export class NatureReserves extends LayerDefinition { new Or([new Tag("leisure", "nature_reserve"), new Tag("boundary", "protected_area")]); this.maxAllowedOverlapPercentage = 10; - this.newElementTags = [new Tag("leisure", "nature_reserve"), - new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")] + this.presets = [{ + title: "Natuurreservaat", + description: "Voeg een ontbrekend, erkend natuurreservaat toe, bv. een gebied dat beheerd wordt door het ANB of natuurpunt", + tags: [new Tag("leisure", "nature_reserve"), + new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")] + } + ]; this.minzoom = 13; this.title = new NameInline("natuurreservaat"); this.style = this.generateStyleFunction(); this.elementsToShow = [ new ImageCarouselWithUploadConstructor(), - /* new TagRenderingOptions({ - freeform: { - key: "_surface", - renderTemplate: "{_surface}m²", - template: "$$$" - } - }),*/ + /* new TagRenderingOptions({ + freeform: { + key: "_surface", + renderTemplate: "{_surface}m²", + template: "$$$" + } + }),*/ new NameQuestion(), new AccessTag(), new OperatorTag(), diff --git a/Customizations/Layers/Park.ts b/Customizations/Layers/Park.ts index b8106e7..3089b68 100644 --- a/Customizations/Layers/Park.ts +++ b/Customizations/Layers/Park.ts @@ -50,8 +50,13 @@ export class Park extends LayerDefinition { this.icon = undefined; this.overpassFilter = new Or([new Tag("leisure", "park"), new Tag("landuse", "village_green")]); - this.newElementTags = [new Tag("leisure", "park"), - new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")]; + this.presets = [{ + title: "Park", + description: "Voeg een ontbrekend park toe. Een park is een groene ruimte die openbaar is." + + "Typisch vind je er banken, vuilbakken, standbeelden, ... ", + tags: [new Tag("leisure", "park"), + new Tag("fixme", "Toegevoegd met MapComplete, geometry nog uit te tekenen")] + }]; this.maxAllowedOverlapPercentage = 25; this.minzoom = 13; diff --git a/Customizations/Layers/Viewpoint.ts b/Customizations/Layers/Viewpoint.ts index 442d94e..029d369 100644 --- a/Customizations/Layers/Viewpoint.ts +++ b/Customizations/Layers/Viewpoint.ts @@ -11,12 +11,17 @@ export class Viewpoint extends LayerDefinition { super({ name: "Bezienswaardigheid", description: "Wil je een foto toevoegen van iets dat geen park, bos of natuurgebied is? Dit kan hiermee", - newElementTags: [new Tag("tourism", "viewpoint"), new Tag("fixme", "Added with mapcomplete. This viewpoint should probably me merged with some existing feature")], + presets: [{ + title: "Bezienswaardigheid (andere)", + description: "Wens je een foto toe te voegen dat geen park, bos of (erkend) natuurreservaat is? Dit kan hiermee", + tags: [new Tag("tourism", "viewpoint"), + new Tag("fixme", "Added with mapcomplete. This viewpoint should probably me merged with some existing feature")] + }], icon: "assets/viewpoint.svg", wayHandling: LayerDefinition.WAYHANDLING_CENTER_ONLY, style: tags => { return { - color: undefined, icon:{ + color: undefined, icon: { iconUrl: "assets/viewpoint.svg", iconSize: [20, 20] } diff --git a/Customizations/Layout.ts b/Customizations/Layout.ts index e4a6f75..a5c1c96 100644 --- a/Customizations/Layout.ts +++ b/Customizations/Layout.ts @@ -96,12 +96,12 @@ export class WelcomeMessage extends UIElement { constructor(layout: Layout, languagePicker: UIElement, osmConnection: OsmConnection) { - super(osmConnection.userDetails); + super(osmConnection?.userDetails); this.languagePicker = languagePicker; this.ListenTo(Locale.language); this.osmConnection = osmConnection; this.layout = layout; - this.userDetails = osmConnection.userDetails; + this.userDetails = osmConnection?.userDetails; this.description = layout.welcomeMessage; this.plzLogIn = layout.gettingStartedPlzLogin; @@ -110,9 +110,15 @@ export class WelcomeMessage extends UIElement { } InnerRender(): string { + + let loginStatus = ""; + if (this.userDetails !== undefined) { + loginStatus = (this.userDetails.data.loggedIn ? this.welcomeBack : this.plzLogIn).Render(); + } + return "" + this.description.Render() + - (this.userDetails.data.loggedIn ? this.welcomeBack : this.plzLogIn).Render() + + loginStatus + this.tail.Render() + "
" + this.languagePicker.Render() + diff --git a/Customizations/Questions/EmailQuestion.ts b/Customizations/Questions/EmailQuestion.ts new file mode 100644 index 0000000..ee99bd4 --- /dev/null +++ b/Customizations/Questions/EmailQuestion.ts @@ -0,0 +1,18 @@ +import {TagRenderingOptions} from "../TagRendering"; +import {UIElement} from "../../UI/UIElement"; +import Translations from "../../UI/i18n/Translations"; + +export class EmailQuestion extends TagRenderingOptions { + + constructor(category: string | UIElement) { + super({ + question: Translations.t.general.questions.emailOf.Subs({category: category}), + freeform: { + renderTemplate: Translations.t.general.questions.emailIs.Subs({category: category}), + template: "$email$", + key: "email" + } + }); + } + +} \ No newline at end of file diff --git a/Customizations/Questions/Website.ts b/Customizations/Questions/Website.ts index 34051d3..ac0b67c 100644 --- a/Customizations/Questions/Website.ts +++ b/Customizations/Questions/Website.ts @@ -9,8 +9,8 @@ export default class Website extends TagRenderingOptions { question: Translations.t.general.questions.websiteOf.Subs({category: category}), freeform: { renderTemplate: Translations.t.general.questions.websiteIs, - template: "$phone$", - key: "phone" + template: "website", + key: "website" } }); } diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index 3316421..a111a46 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -360,9 +360,11 @@ class TagRendering extends UIElement implements TagDependantUIElement { return undefined; } - const prepost = Translations.W(freeform.template).InnerRender().split("$"); + const prepost = Translations.W(freeform.template).InnerRender() + .replace("$$$","$string$") + .split("$"); const type = prepost[1]; - + let isValid = TagRenderingOptions.inputValidation[type]; if (isValid === undefined) { isValid = (str) => true; diff --git a/Helpers.ts b/Helpers.ts index 22e7d38..75336dd 100644 --- a/Helpers.ts +++ b/Helpers.ts @@ -1,6 +1,5 @@ -import {OsmConnection} from "./Logic/OsmConnection"; -import {Changes} from "./Logic/Changes"; import {UIEventSource} from "./UI/UIEventSource"; +import {Changes} from "./Logic/Osm/Changes"; export class Helpers { diff --git a/InitUiElements.ts b/InitUiElements.ts index 7d20c4c..9eb596f 100644 --- a/InitUiElements.ts +++ b/InitUiElements.ts @@ -6,25 +6,30 @@ import {ShareScreen} from "./UI/ShareScreen"; import {FixedUiElement} from "./UI/Base/FixedUiElement"; import {CheckBox} from "./UI/Input/CheckBox"; import Combine from "./UI/Base/Combine"; -import {OsmConnection} from "./Logic/OsmConnection"; -import {Basemap} from "./Logic/Basemap"; import {UIEventSource} from "./UI/UIEventSource"; import {UIElement} from "./UI/UIElement"; import {MoreScreen} from "./UI/MoreScreen"; +import {Tag} from "./Logic/TagsFilter"; +import {FilteredLayer} from "./Logic/FilteredLayer"; +import {FeatureInfoBox} from "./UI/FeatureInfoBox"; +import {ElementStorage} from "./Logic/ElementStorage"; +import {Preset} from "./UI/SimpleAddUI"; +import {Changes} from "./Logic/Osm/Changes"; +import {OsmConnection} from "./Logic/Osm/OsmConnection"; +import {Basemap} from "./Logic/Leaflet/Basemap"; export class InitUiElements { - static OnlyIf(featureSwitch: UIEventSource, callback: () => void) { + static OnlyIf(featureSwitch: UIEventSource, callback: () => void) { featureSwitch.addCallback(() => { - if (featureSwitch.data === "false") { - return; + if (featureSwitch.data) { + callback(); } - callback(); }); - if (featureSwitch.data !== "false") { + if (featureSwitch.data) { callback(); } @@ -33,13 +38,15 @@ export class InitUiElements { private static CreateWelcomePane(layoutToUse: Layout, osmConnection: OsmConnection, bm: Basemap) { - const welcome = new WelcomeMessage(layoutToUse, Locale.CreateLanguagePicker(layoutToUse, Translations.t.general.pickLanguage), osmConnection) + const welcome = new WelcomeMessage(layoutToUse, + Locale.CreateLanguagePicker(layoutToUse, Translations.t.general.pickLanguage), + osmConnection) const fullOptions = new TabbedComponent([ {header: ``, content: welcome}, {header: ``, content: Translations.t.general.openStreetMapIntro}, {header: ``, content: new ShareScreen(layoutToUse, bm.Location)}, - {header: ``, content: new MoreScreen(bm.Location)} + {header: ``, content: new MoreScreen(layoutToUse.name, bm.Location)} ]) return fullOptions; @@ -54,7 +61,7 @@ export class InitUiElements { const help = new FixedUiElement(`
help
`); const close = new FixedUiElement(`
close
`); - new CheckBox( + const checkbox = new CheckBox( new Combine([ "", close, "", "", fullOptions.onClick(() => { @@ -62,6 +69,14 @@ export class InitUiElements { new Combine(["", help, ""]) , true ).AttachTo("messagesbox"); + let dontCloseYet = true; + bm.Location.addCallback(() => { + if(dontCloseYet){ + dontCloseYet = false; + return; + } + checkbox.isEnabled.setData(false); + }) const fullOptions2 = this.CreateWelcomePane(layoutToUse, osmConnection, bm); @@ -69,8 +84,78 @@ export class InitUiElements { new FixedUiElement(`
help
`).onClick(() => { fullScreenMessage.setData(fullOptions2) }).AttachTo("help-button-mobile"); - + } + + static InitLayers(layoutToUse: Layout, osmConnection: OsmConnection, + changes: Changes, + allElements: ElementStorage, + bm: Basemap, + fullScreenMessage: UIEventSource, + selectedElement: UIEventSource): { + minZoom: number + flayers: FilteredLayer[], + presets: Preset[] + } { + const addButtons:Preset[] + = []; + + const flayers: FilteredLayer[] = [] + + let minZoom = 0; + + for (const layer of layoutToUse.layers) { + + const generateInfo = (tagsES, feature) => { + + return new FeatureInfoBox( + feature, + tagsES, + layer.title, + layer.elementsToShow, + changes, + osmConnection.userDetails + ) + }; + + minZoom = Math.max(minZoom, layer.minzoom); + + const flayer = FilteredLayer.fromDefinition(layer, bm, allElements, changes, osmConnection.userDetails, selectedElement, generateInfo); + + for (const preset of layer.presets ?? []) { + + if (preset.icon === undefined) { + const tags = {}; + for (const tag of preset.tags) { + const k = tag.key; + if (typeof (k) === "string") { + tags[k] = tag.value; + } + } + preset.icon = layer.style(tags)?.icon?.iconUrl; + } + + const addButton = { + name: preset.title, + description: preset.description, + icon: preset.icon, + tags: preset.tags, + layerToAddTo: flayer + } + addButtons.push(addButton); + } + flayers.push(flayer); + } + + + + return { + minZoom: minZoom, + flayers: flayers, + presets: addButtons + } + } + } \ No newline at end of file diff --git a/Logic/Bounds.ts b/Logic/Bounds.ts new file mode 100644 index 0000000..e11e7d0 --- /dev/null +++ b/Logic/Bounds.ts @@ -0,0 +1,6 @@ +export interface Bounds { + north: number, + east: number, + south: number, + west: number +} \ No newline at end of file diff --git a/Logic/LayerUpdater.ts b/Logic/LayerUpdater.ts index 5739a27..2f7eb02 100644 --- a/Logic/LayerUpdater.ts +++ b/Logic/LayerUpdater.ts @@ -1,9 +1,9 @@ -import {Basemap} from "./Basemap"; -import {Overpass} from "./Overpass"; import {Or, TagsFilter} from "./TagsFilter"; import {UIEventSource} from "../UI/UIEventSource"; import {FilteredLayer} from "./FilteredLayer"; - +import {Bounds} from "./Bounds"; +import {Overpass} from "./Osm/Overpass"; +import {Basemap} from "./Leaflet/Basemap"; export class LayerUpdater { private _map: Basemap; @@ -14,7 +14,7 @@ export class LayerUpdater { /** * The previous bounds for which the query has been run */ - private previousBounds: { north: number, east: number, south: number, west: number }; + private previousBounds: Bounds; private _overpass: Overpass; private _minzoom: number; @@ -93,10 +93,21 @@ export class LayerUpdater { if (this.runningQuery.data) { console.log("Still running a query, skip"); } - var bbox = this.buildBboxFor(); + + const bounds = this._map.map.getBounds(); + + const diff =0.07; + + const n = bounds.getNorth() + diff; + const e = bounds.getEast() + diff; + const s = bounds.getSouth() - diff; + const w = bounds.getWest() - diff; + + this.previousBounds = {north: n, east: e, south: s, west: w}; + this.runningQuery.setData(true); const self = this; - this._overpass.queryGeoJson(bbox, + this._overpass.queryGeoJson(this.previousBounds, function (data) { self.handleData(data) }, @@ -107,19 +118,7 @@ export class LayerUpdater { } - private buildBboxFor(): string { - const b = this._map.map.getBounds(); - const diff =0.07; - - const n = b.getNorth() + diff; - const e = b.getEast() + diff; - const s = b.getSouth() - diff; - const w = b.getWest() - diff; - - this.previousBounds = {north: n, east: e, south: s, west: w}; - - return "[bbox:" + s + "," + w + "," + n + "," + e + "]"; - } + private IsInBounds(): boolean { diff --git a/Logic/Basemap.ts b/Logic/Leaflet/Basemap.ts similarity index 97% rename from Logic/Basemap.ts rename to Logic/Leaflet/Basemap.ts index 2c3edea..b109da8 100644 --- a/Logic/Basemap.ts +++ b/Logic/Leaflet/Basemap.ts @@ -1,6 +1,6 @@ import L from "leaflet" -import {UIEventSource} from "../UI/UIEventSource"; -import {UIElement} from "../UI/UIElement"; +import {UIEventSource} from "../../UI/UIEventSource"; +import {UIElement} from "../../UI/UIElement"; export class BaseLayers { diff --git a/Logic/GeoLocationHandler.ts b/Logic/Leaflet/GeoLocationHandler.ts similarity index 94% rename from Logic/GeoLocationHandler.ts rename to Logic/Leaflet/GeoLocationHandler.ts index 29ae5c5..06b483b 100644 --- a/Logic/GeoLocationHandler.ts +++ b/Logic/Leaflet/GeoLocationHandler.ts @@ -1,8 +1,8 @@ import {Basemap} from "./Basemap"; -import {UIEventSource} from "../UI/UIEventSource"; -import {UIElement} from "../UI/UIElement"; import L from "leaflet"; -import {Helpers} from "../Helpers"; +import {UIEventSource} from "../../UI/UIEventSource"; +import {UIElement} from "../../UI/UIElement"; +import {Helpers} from "../../Helpers"; export class GeoLocationHandler extends UIElement { @@ -65,8 +65,8 @@ export class GeoLocationHandler extends UIElement { self._marker = newMarker; }); - navigator.permissions.query({name: 'geolocation'}) - .then(function (status) { + navigator?.permissions?.query({name: 'geolocation'}) + ?.then(function (status) { console.log("Geolocation is already", status) if (status.state === "granted") { self.StartGeolocating(); diff --git a/Logic/StrayClickHandler.ts b/Logic/Leaflet/StrayClickHandler.ts similarity index 95% rename from Logic/StrayClickHandler.ts rename to Logic/Leaflet/StrayClickHandler.ts index b7fe283..2ba9719 100644 --- a/Logic/StrayClickHandler.ts +++ b/Logic/Leaflet/StrayClickHandler.ts @@ -1,7 +1,7 @@ import {Basemap} from "./Basemap"; import L from "leaflet"; -import {UIEventSource} from "../UI/UIEventSource"; -import {UIElement} from "../UI/UIElement"; +import {UIEventSource} from "../../UI/UIEventSource"; +import {UIElement} from "../../UI/UIElement"; /** * The stray-click-hanlders adds a marker to the map if no feature was clicked. diff --git a/Logic/Changes.ts b/Logic/Osm/Changes.ts similarity index 96% rename from Logic/Changes.ts rename to Logic/Osm/Changes.ts index 488ea50..0ae51c3 100644 --- a/Logic/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -2,11 +2,11 @@ * Handles all changes made to OSM. * Needs an authenticator via OsmConnection */ +import {UIEventSource} from "../../UI/UIEventSource"; import {OsmConnection} from "./OsmConnection"; import {OsmNode, OsmObject} from "./OsmObject"; -import {ElementStorage} from "./ElementStorage"; -import {UIEventSource} from "../UI/UIEventSource"; -import {And, Tag, TagsFilter} from "./TagsFilter"; +import {And, Tag, TagsFilter} from "../TagsFilter"; +import {ElementStorage} from "../ElementStorage"; export class Changes { @@ -261,11 +261,4 @@ console.log("Received change",key, value) }); } - public asQuestions(qs : QuestionDefinition[]){ - let ls = []; - for (var i in qs){ - ls.push(new Question(this, qs[i])); - } - return ls; - } } \ No newline at end of file diff --git a/Logic/Geocoding.ts b/Logic/Osm/Geocoding.ts similarity index 86% rename from Logic/Geocoding.ts rename to Logic/Osm/Geocoding.ts index 275ade1..ea11389 100644 --- a/Logic/Geocoding.ts +++ b/Logic/Osm/Geocoding.ts @@ -1,7 +1,5 @@ -import * as $ from "jquery" -import {UIEventSource} from "../UI/UIEventSource"; -import {Basemap} from "./Basemap"; - +import {Basemap} from "../Leaflet/Basemap"; +import $ from "jquery" export class Geocoding { private static readonly host = "https://nominatim.openstreetmap.org/search?"; diff --git a/Logic/Osm/Notes.ts b/Logic/Osm/Notes.ts new file mode 100644 index 0000000..a3a3e0c --- /dev/null +++ b/Logic/Osm/Notes.ts @@ -0,0 +1,9 @@ +import {Bounds} from "../Bounds"; + +export class Notes { + + + queryGeoJson(bounds: Bounds, continuation: ((any) => void), onFail: ((reason) => void)): void { + + } +} \ No newline at end of file diff --git a/Logic/OsmConnection.ts b/Logic/Osm/OsmConnection.ts similarity index 90% rename from Logic/OsmConnection.ts rename to Logic/Osm/OsmConnection.ts index e07751d..f6f4035 100644 --- a/Logic/OsmConnection.ts +++ b/Logic/Osm/OsmConnection.ts @@ -1,6 +1,6 @@ // @ts-ignore import osmAuth from "osm-auth"; -import {UIEventSource} from "../UI/UIEventSource"; +import {UIEventSource} from "../../UI/UIEventSource"; export class UserDetails { @@ -17,32 +17,53 @@ export class UserDetails { export class OsmConnection { - private auth = new osmAuth({ - oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', - oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', - auto: true // show a login form if the user is not authenticated and - // you try to do a call - }); + public auth; public userDetails: UIEventSource; private _dryRun: boolean; - constructor(dryRun: boolean) { + constructor(dryRun: boolean, oauth_token: UIEventSource) { + + this.auth = new osmAuth({ + oauth_consumer_key: 'hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem', + oauth_secret: 'wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI', + singlepage: true, + landing: window.location.href, + auto: true // show a login form if the user is not authenticated and + // you try to do a call + + }); + + this.userDetails = new UIEventSource(new UserDetails()); this.userDetails.data.osmConnection = this; this.userDetails.data.dryRun = dryRun; this._dryRun = dryRun; + + if(oauth_token.data !== undefined){ + console.log(oauth_token.data) + const self = this; + this.auth.bootstrapToken(oauth_token.data, + (x) => { + console.log("Called back: ", x) + self.AttemptLogin(); + }, this.auth); + + oauth_token.setData(undefined); + + } if (this.auth.authenticated()) { this.AttemptLogin(); // Also updates the user badge - }else{ + } else { console.log("Not authenticated"); } - - if(dryRun){ + + if (dryRun) { console.log("DRYRUN ENABLED"); } + } public LogOut() { diff --git a/Logic/OsmImageUploadHandler.ts b/Logic/Osm/OsmImageUploadHandler.ts similarity index 94% rename from Logic/OsmImageUploadHandler.ts rename to Logic/Osm/OsmImageUploadHandler.ts index cd242c7..35940f5 100644 --- a/Logic/OsmImageUploadHandler.ts +++ b/Logic/Osm/OsmImageUploadHandler.ts @@ -1,11 +1,11 @@ /** * Helps in uplaoding, by generating the rigth title, decription and by adding the tag to the changeset */ -import {UIEventSource} from "../UI/UIEventSource"; -import {ImageUploadFlow} from "../UI/ImageUploadFlow"; import {Changes} from "./Changes"; +import {UIEventSource} from "../../UI/UIEventSource"; +import {ImageUploadFlow} from "../../UI/ImageUploadFlow"; import {UserDetails} from "./OsmConnection"; -import {SlideShow} from "../UI/SlideShow"; +import {SlideShow} from "../../UI/SlideShow"; export class OsmImageUploadHandler { private _tags: UIEventSource; diff --git a/Logic/OsmObject.ts b/Logic/Osm/OsmObject.ts similarity index 100% rename from Logic/OsmObject.ts rename to Logic/Osm/OsmObject.ts diff --git a/Logic/Overpass.ts b/Logic/Osm/Overpass.ts similarity index 77% rename from Logic/Overpass.ts rename to Logic/Osm/Overpass.ts index b3f1d52..f6974ce 100644 --- a/Logic/Overpass.ts +++ b/Logic/Osm/Overpass.ts @@ -1,11 +1,11 @@ -import {TagsFilter} from "./TagsFilter"; -import * as OsmToGeoJson from "osmtogeojson"; -import * as $ from "jquery"; - - /** * Interfaces overpass to get all the latest data */ +import {Bounds} from "../Bounds"; +import {TagsFilter} from "../TagsFilter"; +import $ from "jquery" +import * as OsmToGeoJson from "osmtogeojson"; + export class Overpass { private _filter: TagsFilter public static testUrl: string = null @@ -14,21 +14,21 @@ export class Overpass { this._filter = filter } - public buildQuery(bbox: string): string { + + private buildQuery(bbox: string): string { const filters = this._filter.asOverpass() - console.log(filters) let filter = "" for (const filterOr of filters) { filter += 'nwr' + filterOr + ';' } const query = '[out:json][timeout:25]' + bbox + ';(' + filter + ');out body;>;out skel qt;' - console.log(query) return "https://overpass-api.de/api/interpreter?data=" + encodeURIComponent(query) } - - queryGeoJson(bbox: string, continuation: ((any) => void), onFail: ((reason) => void)): void { - let query = this.buildQuery(bbox) + + queryGeoJson(bounds: Bounds, continuation: ((any) => void), onFail: ((reason) => void)): void { + + let query = this.buildQuery( "[bbox:" + bounds.south + "," + bounds.west + "," + bounds.north + "," + bounds.east + "]") if(Overpass.testUrl !== null){ console.log("Using testing URL") diff --git a/Logic/TagsFilter.ts b/Logic/TagsFilter.ts index 827548a..a94de50 100644 --- a/Logic/TagsFilter.ts +++ b/Logic/TagsFilter.ts @@ -55,8 +55,8 @@ export class Regex extends TagsFilter { export class Tag extends TagsFilter { - public key: string | RegExp - public value: string | RegExp + public key: string + public value: string public invertValue: boolean constructor(key: string | RegExp, value: string | RegExp, invertValue = false) { @@ -69,7 +69,9 @@ export class Tag extends TagsFilter { } super() + // @ts-ignore this.key = key + // @ts-ignore this.value = value this.invertValue = invertValue } @@ -107,10 +109,14 @@ export class Tag extends TagsFilter { } asOverpass(): string[] { + // @ts-ignore const keyIsRegex = this.key instanceof RegExp + // @ts-ignore const key = keyIsRegex ? (this.key as RegExp).source : this.key + // @ts-ignore const valIsRegex = this.value instanceof RegExp + // @ts-ignore const val = valIsRegex ? (this.value as RegExp).source : this.value const regexKeyPrefix = keyIsRegex ? '~' : '' @@ -276,7 +282,8 @@ export class TagUtils { static ApplyTemplate(template: string, tags: any): string { for (const k in tags) { while (template.indexOf("{" + k + "}") >= 0) { - template = template.replace("{" + k + "}", tags[k]); + const escaped = tags[k].replace(//g, '>'); + template = template.replace("{" + k + "}", escaped); } } return template; diff --git a/README.md b/README.md index 4a48c3a..6193a6a 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ Furthermore, it shows images present in the `image` tag or, if a `wikidata` or ` ## Examples -- [Buurtnatuur.be](http://buurntatuur.be), developed for the Belgian [Green party](https://www.groen.be/). They also funded the initial development! -- [Cyclofix](https://pietervdvn.github.io/MapComplete/index.html?quests=pomp), further development on [Open Summer of Code](https://summerofcode.be/) funded by [Brussels Mobility](https://mobilite-mobiliteit.brussels/en) +- [Buurtnatuur.be](http://buurtnatuur.be), developed for the Belgian [Green party](https://www.groen.be/). They also funded the initial development! +- [Cyclofix](https://pietervdvn.github.io/MapComplete/index.html?layout=cyclofix), further development on [Open Summer of Code](https://summerofcode.be/) funded by [Brussels Mobility](https://mobilite-mobiliteit.brussels/en). Landing page at https://cyclofix.osm.be/ - [Bookcases](https://pietervdvn.github.io/MapComplete/index.html?quests=bookcases#element) cause I like to collect them. -- [Map of Maps](https://pietervdvn.github.io/MapComplete/index.html?layout=metamap#element), after a tweet +- [Map of Maps](https://pietervdvn.github.io/MapComplete/index.html?layout=metamap&z=14&lat=50.650&lon=4.2668#element), after a tweet Have a theme idea? Drop it in the [issues](https://github.com/pietervdvn/MapComplete/issues) @@ -100,4 +100,4 @@ https://commons.wikimedia.org/wiki/File:Home-icon.svg Home icon by Timothy Miller, CC-BY-SA 3.0 https://commons.wikimedia.org/wiki/File:Map_icons_by_Scott_de_Jonge_-_bicycle-store.svg -Bicycle logo, Scott de Jonge \ No newline at end of file +Bicycle logo, Scott de Jonge diff --git a/UI/Base/SubtleButton.ts b/UI/Base/SubtleButton.ts index 444e71a..0234d6a 100644 --- a/UI/Base/SubtleButton.ts +++ b/UI/Base/SubtleButton.ts @@ -1,23 +1,36 @@ import {UIElement} from "../UIElement"; import Translations from "../i18n/Translations"; import Combine from "./Combine"; +import {link} from "fs"; export class SubtleButton extends UIElement{ private imageUrl: string; private message: UIElement; + private linkTo: { url: string, newTab?: boolean } = undefined; - constructor(imageUrl: string, message: string | UIElement) { + constructor(imageUrl: string, message: string | UIElement, linkTo: { url: string, newTab?: boolean } = undefined) { super(undefined); + this.linkTo = linkTo; this.message = Translations.W(message); this.imageUrl = imageUrl; - + } InnerRender(): string { + + if(this.linkTo != undefined){ + return new Combine([ + ``, + this.imageUrl !== undefined ? `` : "", + this.message, + '' + ]).Render(); + } + return new Combine([ '', - ``, + this.imageUrl !== undefined ? `` : "", this.message, '' ]).Render(); diff --git a/UI/FeatureInfoBox.ts b/UI/FeatureInfoBox.ts index 3fb0f4d..34913a4 100644 --- a/UI/FeatureInfoBox.ts +++ b/UI/FeatureInfoBox.ts @@ -1,10 +1,6 @@ import {UIElement} from "./UIElement"; import {UIEventSource} from "./UIEventSource"; -import {QuestionPicker} from "./QuestionPicker"; -import {OsmImageUploadHandler} from "../Logic/OsmImageUploadHandler"; import {ImageCarousel} from "./Image/ImageCarousel"; -import {Changes} from "../Logic/Changes"; -import {UserDetails} from "../Logic/OsmConnection"; import {VerticalCombine} from "./Base/VerticalCombine"; import {TagRenderingOptions} from "../Customizations/TagRendering"; import {OsmLink} from "../Customizations/Questions/OsmLink"; @@ -12,6 +8,8 @@ import {WikipediaLink} from "../Customizations/Questions/WikipediaLink"; import {And} from "../Logic/TagsFilter"; import {TagDependantUIElement, TagDependantUIElementConstructor} from "../Customizations/UIElementConstructor"; import Translations from "./i18n/Translations"; +import {Changes} from "../Logic/Osm/Changes"; +import {UserDetails} from "../Logic/Osm/OsmConnection"; export class FeatureInfoBox extends UIElement { @@ -33,7 +31,6 @@ export class FeatureInfoBox extends UIElement { private _infoboxes: TagDependantUIElement[]; - private _questions: QuestionPicker; private _oneSkipped = Translations.t.general.oneSkippedQuestion.Clone(); private _someSkipped = Translations.t.general.skippedQuestions.Clone(); diff --git a/UI/Image/ImageCarousel.ts b/UI/Image/ImageCarousel.ts index 86c7ccf..e69fa3a 100644 --- a/UI/Image/ImageCarousel.ts +++ b/UI/Image/ImageCarousel.ts @@ -4,11 +4,11 @@ import {UIEventSource} from "../UIEventSource"; import {SlideShow} from "../SlideShow"; import {FixedUiElement} from "../Base/FixedUiElement"; import {VerticalCombine} from "../Base/VerticalCombine"; -import {Changes} from "../../Logic/Changes"; import {VariableUiElement} from "../Base/VariableUIElement"; import {ConfirmDialog} from "../ConfirmDialog"; -import {UserDetails} from "../../Logic/OsmConnection"; import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; +import {Changes} from "../../Logic/Osm/Changes"; +import {UserDetails} from "../../Logic/Osm/OsmConnection"; export class ImageCarouselConstructor implements TagDependantUIElementConstructor{ IsKnown(properties: any): boolean { diff --git a/UI/Image/ImageCarouselWithUpload.ts b/UI/Image/ImageCarouselWithUpload.ts index a4a8f1a..dcf2a7f 100644 --- a/UI/Image/ImageCarouselWithUpload.ts +++ b/UI/Image/ImageCarouselWithUpload.ts @@ -1,10 +1,9 @@ import {TagDependantUIElement, TagDependantUIElementConstructor} from "../../Customizations/UIElementConstructor"; import {ImageCarousel} from "./ImageCarousel"; -import {OsmImageUploadHandler} from "../../Logic/OsmImageUploadHandler"; import {UIEventSource} from "../UIEventSource"; -import {Changes} from "../../Logic/Changes"; -import {UserDetails} from "../../Logic/OsmConnection"; import {ImageUploadFlow} from "../ImageUploadFlow"; +import {Changes} from "../../Logic/Osm/Changes"; +import {OsmImageUploadHandler} from "../../Logic/Osm/OsmImageUploadHandler"; export class ImageCarouselWithUploadConstructor implements TagDependantUIElementConstructor{ IsKnown(properties: any): boolean { diff --git a/UI/LayerSelection.ts b/UI/LayerSelection.ts index 28432b9..1e0bcb6 100644 --- a/UI/LayerSelection.ts +++ b/UI/LayerSelection.ts @@ -26,7 +26,7 @@ export class LayerSelection extends UIElement{ this._checkboxes.push(new CheckBox( new Combine([checkbox, icon, name]), new Combine([ - Img.checkmark, + Img.no_checkmark, icon, layer.layerDef.name]), layer.isDisplayed)); diff --git a/UI/MoreScreen.ts b/UI/MoreScreen.ts index b7018b7..5d49f2e 100644 --- a/UI/MoreScreen.ts +++ b/UI/MoreScreen.ts @@ -13,9 +13,11 @@ import {SubtleButton} from "./Base/SubtleButton"; export class MoreScreen extends UIElement { private currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>; + private currentLayout: string; - constructor(currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>) { + constructor(currentLayout: string, currentLocation: UIEventSource<{ zoom: number, lat: number, lon: number }>) { super(currentLocation); + this.currentLayout = currentLayout; this.currentLocation = currentLocation; } @@ -28,22 +30,21 @@ export class MoreScreen extends UIElement { if (layout.hideFromOverview) { continue } + if (layout.name === this.currentLayout) { + continue; + } const linkText = `https://pietervdvn.github.io/MapComplete/${layout.name}.html?z=${this.currentLocation.data.zoom}&lat=${this.currentLocation.data.lat}&lon=${this.currentLocation.data.lon}` const link = new SubtleButton(layout.icon, new Combine([ - ``, - "
", "", Translations.W(layout.title), "", "
", Translations.W(layout.description), - "
", - "
" - ])); + ]), {url: linkText, newTab: false}); els.push(link) } diff --git a/UI/PendingChanges.ts b/UI/PendingChanges.ts index cda6214..384f7c7 100644 --- a/UI/PendingChanges.ts +++ b/UI/PendingChanges.ts @@ -1,6 +1,6 @@ import {UIElement} from "./UIElement"; import {UIEventSource} from "./UIEventSource"; -import {Changes} from "../Logic/Changes"; +import {Changes} from "../Logic/Osm/Changes"; export class PendingChanges extends UIElement { private _pendingChangesCount: UIEventSource; diff --git a/UI/QuestionPicker.ts b/UI/QuestionPicker.ts deleted file mode 100644 index 82baac4..0000000 --- a/UI/QuestionPicker.ts +++ /dev/null @@ -1,47 +0,0 @@ -import {UIElement} from "./UIElement"; -import {Question} from "../Logic/Question"; -import {UIEventSource} from "./UIEventSource"; - -export class QuestionPicker extends UIElement { - private readonly _questions: Question[]; - private readonly tags: any; - private source: UIEventSource; - - constructor(questions: Question[], - tags: UIEventSource) { - super(tags); - this._questions = questions; - this.tags = tags.data; - this.source = tags; - } - - - protected InnerRender(): string { - - let t = this.tags; - let highestPriority = Number.MIN_VALUE; - let highestQ: Question; - for (const q of this._questions) { - - if (!q.Applicable(t)) { - continue; - } - - const priority = q.question.severity; - if (priority > highestPriority) { - highestPriority = priority; - highestQ = q; - } - } - - - if (highestQ === undefined) { - return "Er zijn geen vragen meer!"; - } - - return "
" + - highestQ.CreateHtml(this.source).Render() + - "
"; - } - -} \ No newline at end of file diff --git a/UI/SearchAndGo.ts b/UI/SearchAndGo.ts index 8e51f93..0e0a574 100644 --- a/UI/SearchAndGo.ts +++ b/UI/SearchAndGo.ts @@ -1,13 +1,13 @@ -import {UIElement} from "./UIElement"; -import {TextField} from "./Input/TextField"; -import {UIEventSource} from "./UIEventSource"; -import {FixedUiElement} from "./Base/FixedUiElement"; -import {Geocoding} from "../Logic/Geocoding"; -import {Basemap} from "../Logic/Basemap"; -import {VariableUiElement} from "./Base/VariableUIElement"; -import Translation from "./i18n/Translation"; import Locale from "./i18n/Locale"; +import {UIEventSource} from "./UIEventSource"; +import {UIElement} from "./UIElement"; +import Translation from "./i18n/Translation"; +import {VariableUiElement} from "./Base/VariableUIElement"; +import {FixedUiElement} from "./Base/FixedUiElement"; +import {TextField} from "./Input/TextField"; +import {Geocoding} from "../Logic/Osm/Geocoding"; import Translations from "./i18n/Translations"; +import {Basemap} from "../Logic/Leaflet/Basemap"; export class SearchAndGo extends UIElement { diff --git a/UI/SimpleAddUI.ts b/UI/SimpleAddUI.ts index 77ff1d7..ecb24af 100644 --- a/UI/SimpleAddUI.ts +++ b/UI/SimpleAddUI.ts @@ -2,12 +2,23 @@ import {UIElement} from "./UIElement"; import {UIEventSource} from "./UIEventSource"; import {Tag} from "../Logic/TagsFilter"; import {FilteredLayer} from "../Logic/FilteredLayer"; -import {Changes} from "../Logic/Changes"; import {FixedUiElement} from "./Base/FixedUiElement"; import {Button} from "./Base/Button"; -import {UserDetails} from "../Logic/OsmConnection"; import Translations from "./i18n/Translations"; import Combine from "./Base/Combine"; +import {SubtleButton} from "./Base/SubtleButton"; +import {VerticalCombine} from "./Base/VerticalCombine"; +import Locale from "./i18n/Locale"; +import {Changes} from "../Logic/Osm/Changes"; +import {UserDetails} from "../Logic/Osm/OsmConnection"; + +export interface Preset { + description: string | UIElement, + name: string | UIElement, + icon: string, + tags: Tag[], + layerToAddTo: FilteredLayer +} /** * Asks to add a feature at the last clicked location, at least if zoom is sufficient @@ -17,19 +28,27 @@ export class SimpleAddUI extends UIElement { private _addButtons: UIElement[]; private _lastClickLocation: UIEventSource<{ lat: number; lon: number }>; private _changes: Changes; - private _selectedElement: UIEventSource<{feature: any}>; + private _selectedElement: UIEventSource<{ feature: any }>; private _dataIsLoading: UIEventSource; private _userDetails: UIEventSource; + private _confirmPreset: UIEventSource + = new UIEventSource(undefined); + private confirmButton: UIElement = undefined; + private cancelButton: UIElement; + private goToInboxButton: UIElement = new SubtleButton("./assets/envelope.svg", + Translations.t.general.goToInbox, {url:"https://www.openstreetmap.org/messages/inbox", newTab: false}); + constructor(zoomlevel: UIEventSource<{ zoom: number }>, lastClickLocation: UIEventSource<{ lat: number, lon: number }>, changes: Changes, - selectedElement: UIEventSource<{feature: any}>, + selectedElement: UIEventSource<{ feature: any }>, dataIsLoading: UIEventSource, userDetails: UIEventSource, - addButtons: { name: UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[], + addButtons: { description: string | UIElement, name: string | UIElement; icon: string; tags: Tag[]; layerToAddTo: FilteredLayer }[], ) { super(zoomlevel); + this.ListenTo(Locale.language); this._zoomlevel = zoomlevel; this._lastClickLocation = lastClickLocation; this._changes = changes; @@ -39,18 +58,47 @@ export class SimpleAddUI extends UIElement { this.ListenTo(userDetails); this.ListenTo(dataIsLoading); this._addButtons = []; + this.ListenTo(this._confirmPreset); + this.clss = "add-ui" + const self = this; for (const option of addButtons) { //