From 49cab66a72ab99729f15bb2577a732317b4b5f82 Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Sun, 12 Jul 2020 23:19:05 +0200 Subject: [PATCH] Better bookcase quests --- Customizations/AllKnownLayouts.ts | 6 +- Customizations/LayerDefinition.ts | 3 +- Customizations/Layers/Bookcases.ts | 133 +++++++++++++++++-------- Customizations/Layouts/All.ts | 18 ++++ Customizations/OnlyShowIf.ts | 79 +++++++++++++++ Customizations/Questions/AccessTag.ts | 2 +- Customizations/TagRendering.ts | 21 +++- Customizations/UIElementConstructor.ts | 20 ++++ Logic/TagsFilter.ts | 4 +- UI/FeatureInfoBox.ts | 23 ++--- index.ts | 1 + 11 files changed, 247 insertions(+), 63 deletions(-) create mode 100644 Customizations/Layouts/All.ts create mode 100644 Customizations/OnlyShowIf.ts create mode 100644 Customizations/UIElementConstructor.ts diff --git a/Customizations/AllKnownLayouts.ts b/Customizations/AllKnownLayouts.ts index c3008f8..68173c8 100644 --- a/Customizations/AllKnownLayouts.ts +++ b/Customizations/AllKnownLayouts.ts @@ -4,16 +4,19 @@ import {GRB} from "./Layouts/GRB"; import {Statues} from "./Layouts/Statues"; import {Bookcases} from "./Layouts/Bookcases"; import Cyclofix from "./Layouts/Cyclofix"; +import {All} from "./Layouts/All"; export class AllKnownLayouts { public static allSets: any = AllKnownLayouts.AllLayouts(); private static AllLayouts() { + const all = new All(); const layouts = [ new Groen(), new GRB(), new Cyclofix(), - new Bookcases() + new Bookcases(), + all /*new Toilets(), new Statues(), */ @@ -21,6 +24,7 @@ export class AllKnownLayouts { const allSets = {}; for (const layout of layouts) { allSets[layout.name] = layout; + all.layers = all.layers.concat(layout.layers); } return allSets; } diff --git a/Customizations/LayerDefinition.ts b/Customizations/LayerDefinition.ts index 945f299..03822e3 100644 --- a/Customizations/LayerDefinition.ts +++ b/Customizations/LayerDefinition.ts @@ -7,6 +7,7 @@ import {FilteredLayer} from "../Logic/FilteredLayer"; import {Changes} from "../Logic/Changes"; import {UserDetails} from "../Logic/OsmConnection"; import {TagRenderingOptions} from "./TagRendering"; +import {TagDependantUIElementConstructor} from "./UIElementConstructor"; export class LayerDefinition { @@ -27,7 +28,7 @@ export class LayerDefinition { /** * These are the questions/shown attributes in the popup */ - elementsToShow: TagRenderingOptions[]; + elementsToShow: TagDependantUIElementConstructor[]; style: (tags: any) => { color: string, icon: any }; diff --git a/Customizations/Layers/Bookcases.ts b/Customizations/Layers/Bookcases.ts index 04cda67..0c17586 100644 --- a/Customizations/Layers/Bookcases.ts +++ b/Customizations/Layers/Bookcases.ts @@ -1,6 +1,6 @@ import {LayerDefinition} from "../LayerDefinition"; import L from "leaflet"; -import {Tag} from "../../Logic/TagsFilter"; +import {And, Regex, Tag} from "../../Logic/TagsFilter"; import {QuestionDefinition} from "../../Logic/Question"; import {TagRenderingOptions} from "../TagRendering"; import {NameInline} from "../Questions/NameInline"; @@ -21,7 +21,6 @@ export class Bookcases extends LayerDefinition { this.elementsToShow = [ new TagRenderingOptions({ - priority: 13, question: "Heeft dit boekenruilkastje een naam?", freeform: { key: "name", @@ -44,66 +43,114 @@ export class Bookcases extends LayerDefinition { key: "capacity", placeholder: "aantal" }, - priority: 15 } ), new TagRenderingOptions({ question: "Wat voor soort boeken heeft dit boekenruilkastje?", - mappings:[ - {k: new Tag("books","children"), txt: "Voornamelijk kinderboeken"}, - {k: new Tag("books","adults"), txt: "Voornamelijk boeken voor volwassenen"}, - {k: new Tag("books","children;adults"), txt: "Zowel kinderboeken als boeken voor volwassenen"} + mappings: [ + {k: new Tag("books", "children"), txt: "Voornamelijk kinderboeken"}, + {k: new Tag("books", "adults"), txt: "Voornamelijk boeken voor volwassenen"}, + {k: new Tag("books", "children;adults"), txt: "Zowel kinderboeken als boeken voor volwassenen"} ], - priority: 14 }), - + new TagRenderingOptions({ - question: "", - freeform:{ + question: "Staat dit boekenruilkastje binnen of buiten?", + mappings: [ + {k: new Tag("indoor", "yes"), txt: "Dit boekenruilkastje staat binnen"}, + {k: new Tag("indoor", "no"), txt: "Dit boekenruilkastje staat buiten"}, + {k: new Tag("indoor", ""), txt: "Dit boekenruilkastje staat buiten"} + ] + }), + + new TagRenderingOptions({ + question: "Is dit boekenruilkastje vrij toegankelijk?", + mappings: [ + {k: new Tag("access", "yes"), txt: "Ja, vrij toegankelijk"}, + {k: new Tag("access", "customers"), txt: "Enkel voor klanten"}, + ] + }).OnlyShowIf(new Tag("indoor", "yes")), + new TagRenderingOptions({ + question: "Wie (welke organisatie) beheert dit boekenruilkastje?", + freeform: { + key: "opeartor", + renderTemplate: "Dit boekenruilkastje wordt beheerd door {operator}", + template: "Dit boekenruilkastje wordt beheerd door $$$" + } + }), + + new TagRenderingOptions({ + question: "Zijn er openingsuren voor dit boekenruilkastje?", + mappings: [ + {k: new Tag("opening_hours", "24/7"), txt: "Dag en nacht toegankelijk"}, + {k: new Tag("opening_hours", ""), txt: "Dag en nacht toegankelijk"}, + {k: new Tag("opening_hours", "sunrise-sunset"), txt: "Van zonsopgang tot zonsondergang"}, + ], + freeform: { + key: "opening_hours", + renderTemplate: "De openingsuren zijn {opening_hours}", + template: "De openingsuren zijn $$$" + } + }), + + new TagRenderingOptions({ + question: "Is dit boekenruilkastje deel van een netwerk?", + freeform: { + key: "brand", + renderTemplate: "Deel van het netwerk {brand}", + template: "Deel van het netwerk $$$" + }, + mappings: [{ + k: new And([new Tag("brand", "Little Free Library"), new Tag("nobrand", "")]), + txt: "Little Free Library" + }, + { + k: new And([new Tag("brand", ""), new Tag("nobrand", "yes")]), + txt: "Maakt geen deel uit van een groter netwerk" + }] + }).OnlyShowIf(new And( + [new Tag("brand", "!(Little Free Library)"), + new Tag("ref", "")])), + + new TagRenderingOptions({ + question: "Wat is het LFL-referentienummer van dit boekenruilkastje?", + freeform: { + key: "ref", + template: "Het refernetienummer is $$$", + renderTemplate: "Gekend als Little Free Library {ref}" + } + }).OnlyShowIf(new Tag("brand", "Little Free Library")), + + new TagRenderingOptions({ + question: "Wanneer werd dit boekenruilkastje geinstalleerd?", + priority: -1, + freeform: { key: "start_date", renderTemplate: "Geplaatst op {start_date}", template: "Geplaatst op $$$" } }), - + new TagRenderingOptions({ question: "Is er een website waar we er meer informatie is over dit boekenruilkastje?", - freeform:{ - key:"website", + freeform: { + key: "website", renderTemplate: "Meer informatie over dit boekenruilkastje", - template: "$$$", - placeholder:"website" - - }, - priority: 5 + template: "$$$", + placeholder: "website" + } }), - - - + new TagRenderingOptions({ + freeform: { + key: "description", + renderTemplate: "Beschrijving door de uitbater
{description}", + template: "$$$", + } + }) + ]; - /* - this.elementsToShow = [ - - - - new TagMappingOptions({key: "operator", template: "Onder de hoede van {operator}"}), - new TagMappingOptions({key: "brand", template: "Deel van het netwerk {brand}"}), - new TagMappingOptions({key: "ref", template: "Referentienummer {ref}"}), - - new TagMappingOptions({key: "description", template: "Extra beschrijving:

{description}

"}), - ] - ;*/ - - /* this.questions = [ - QuestionDefinition.textQuestion("Heeft dit boekenkastje een peter, meter of voogd?", "operator", 10), - // QuestionDefinition.textQuestion("Wie kunnen we (per email) contacteren voor dit boekenruilkastje?", "email", 5), - - - ] - ; - */ this.style = function (tags) { return { diff --git a/Customizations/Layouts/All.ts b/Customizations/Layouts/All.ts new file mode 100644 index 0000000..a415c2e --- /dev/null +++ b/Customizations/Layouts/All.ts @@ -0,0 +1,18 @@ +import {Layout} from "../Layout"; + +export class All extends Layout{ + constructor() { + super( + "all", + "All quest layers", + [], + 15, + 51.2, + 3.2, + "

All quests of MapComplete

" + + "This is a mixed bag. Some quests might be hard or for experts to answer only", + "Please log in", + "" + ); + } +} \ No newline at end of file diff --git a/Customizations/OnlyShowIf.ts b/Customizations/OnlyShowIf.ts new file mode 100644 index 0000000..1e8d59f --- /dev/null +++ b/Customizations/OnlyShowIf.ts @@ -0,0 +1,79 @@ +/** + * Wrapper around another TagDependandElement, which only shows if the filters match + */ +import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; +import {TagsFilter, TagUtils} from "../Logic/TagsFilter"; +import {UIElement} from "../UI/UIElement"; +import {UIEventSource} from "../UI/UIEventSource"; +import {Changes} from "../Logic/Changes"; + + +export class OnlyShowIfConstructor implements TagDependantUIElementConstructor{ + private _tagsFilter: TagsFilter; + private _embedded: TagDependantUIElementConstructor; + + constructor(tagsFilter : TagsFilter, embedded: TagDependantUIElementConstructor) { + this._tagsFilter = tagsFilter; + this._embedded = embedded; + } + + construct(tags: UIEventSource, changes: Changes): TagDependantUIElement { + return new OnlyShowIf(tags, + this._embedded.construct(tags, changes), + this._tagsFilter); + } + + +} + +class OnlyShowIf extends UIElement implements TagDependantUIElement { + private _embedded: TagDependantUIElement; + private _filter: TagsFilter; + + constructor( + tags: UIEventSource, + embedded: TagDependantUIElement, filter: TagsFilter) { + super(tags); + this._filter = filter; + this._embedded = embedded; + + } + + private Matches() : boolean{ + return this._filter.matches(TagUtils.proprtiesToKV(this._source.data)); + } + + protected InnerRender(): string { + if (this.Matches()) { + return this._embedded.Render(); + } else { + return ""; + } + } + + Priority(): number { + return this._embedded.Priority(); + } + + IsKnown(): boolean { + if(!this.Matches()){ + return false; + } + return this._embedded.IsKnown(); + } + + IsQuestioning(): boolean { + if(!this.Matches()){ + return false; + } + return this._embedded.IsQuestioning(); + } + + Activate(): void { + this._embedded.Activate(); + } + + Update(): void { + this._embedded.Update(); + } +} \ No newline at end of file diff --git a/Customizations/Questions/AccessTag.ts b/Customizations/Questions/AccessTag.ts index 8e67a12..06a5fe0 100644 --- a/Customizations/Questions/AccessTag.ts +++ b/Customizations/Questions/AccessTag.ts @@ -1,4 +1,4 @@ -import {TagRendering, TagRenderingOptions} from "../TagRendering"; +import {TagRenderingOptions} from "../TagRendering"; import {UIEventSource} from "../../UI/UIEventSource"; import {Changes} from "../../Logic/Changes"; import {And, Tag} from "../../Logic/TagsFilter"; diff --git a/Customizations/TagRendering.ts b/Customizations/TagRendering.ts index d299e9d..9f80166 100644 --- a/Customizations/TagRendering.ts +++ b/Customizations/TagRendering.ts @@ -9,8 +9,10 @@ import {TextField} from "../UI/Base/TextField"; import {UIInputElement} from "../UI/Base/UIInputElement"; import {UIRadioButtonWithOther} from "../UI/Base/UIRadioButtonWithOther"; import {VariableUiElement} from "../UI/Base/VariableUIElement"; +import {TagDependantUIElement, TagDependantUIElementConstructor} from "./UIElementConstructor"; +import {OnlyShowIfConstructor} from "./OnlyShowIf"; -export class TagRenderingOptions { +export class TagRenderingOptions implements TagDependantUIElementConstructor { /** * Notes: by not giving a 'question', one disables the question form alltogether @@ -66,6 +68,10 @@ export class TagRenderingOptions { }) { this.options = options; } + + OnlyShowIf(tagsFilter: TagsFilter) : TagDependantUIElementConstructor{ + return new OnlyShowIfConstructor(tagsFilter, this); + } IsQuestioning(tags: any): boolean { @@ -87,13 +93,20 @@ export class TagRenderingOptions { } + construct(tags: UIEventSource, changes: Changes): TagDependantUIElement { + return new TagRendering(tags, changes, this.options); + } + } -export class TagRendering extends UIElement { +class TagRendering extends UIElement implements TagDependantUIElement { - public elementPriority: number; + private _priority: number; + Priority(): number { + return this._priority; + } private _question: string; private _primer: string; @@ -142,12 +155,12 @@ export class TagRendering extends UIElement { this.ListenTo(this._editMode); this._question = options.question; + this._priority = options.priority ?? 0; this._primer = options.primer ?? ""; this._tagsPreprocessor = options.tagsPreprocessor; this._mapping = []; this._renderMapping = []; this._freeform = options.freeform; - this.elementPriority = options.priority ?? 0; // Prepare the choices for the Radio buttons const choices: UIElement[] = []; diff --git a/Customizations/UIElementConstructor.ts b/Customizations/UIElementConstructor.ts new file mode 100644 index 0000000..8c0758e --- /dev/null +++ b/Customizations/UIElementConstructor.ts @@ -0,0 +1,20 @@ +import {UIEventSource} from "../UI/UIEventSource"; +import {Changes} from "../Logic/Changes"; +import {UIElement} from "../UI/UIElement"; + + +export interface TagDependantUIElementConstructor { + + construct(tags: UIEventSource, changes: Changes): TagDependantUIElement; + +} + +export abstract class TagDependantUIElement extends UIElement { + + abstract IsKnown(): boolean; + + abstract IsQuestioning(): boolean; + + abstract Priority() : number; + +} \ No newline at end of file diff --git a/Logic/TagsFilter.ts b/Logic/TagsFilter.ts index fc3254d..bf039a5 100644 --- a/Logic/TagsFilter.ts +++ b/Logic/TagsFilter.ts @@ -22,8 +22,10 @@ export class Regex implements TagsFilter { // Any is allowed return true; } + - return tag.v.match(this._r).length > 0; + const matchCount =tag.v.match(this._r)?.length; + return (matchCount ?? 0) > 0; } } return false; diff --git a/UI/FeatureInfoBox.ts b/UI/FeatureInfoBox.ts index fbd42ac..b358417 100644 --- a/UI/FeatureInfoBox.ts +++ b/UI/FeatureInfoBox.ts @@ -6,10 +6,11 @@ import {ImageCarousel} from "./Image/ImageCarousel"; import {Changes} from "../Logic/Changes"; import {UserDetails} from "../Logic/OsmConnection"; import {VerticalCombine} from "./Base/VerticalCombine"; -import {TagRendering, TagRenderingOptions} from "../Customizations/TagRendering"; +import {TagRenderingOptions} from "../Customizations/TagRendering"; import {OsmLink} from "../Customizations/Questions/OsmLink"; import {WikipediaLink} from "../Customizations/Questions/WikipediaLink"; import {And} from "../Logic/TagsFilter"; +import {TagDependantUIElement} from "../Customizations/UIElementConstructor"; export class FeatureInfoBox extends UIElement { @@ -27,7 +28,7 @@ export class FeatureInfoBox extends UIElement { private _imageElement: ImageCarousel; private _pictureUploader: UIElement; private _wikipedialink: UIElement; - private _infoboxes: TagRendering[]; + private _infoboxes: TagDependantUIElement[]; constructor( @@ -48,10 +49,8 @@ export class FeatureInfoBox extends UIElement { this._infoboxes = []; for (const tagRenderingOption of elementsToShow) { - if (tagRenderingOption.options === undefined) { - throw "Tagrendering.options not defined" - } - this._infoboxes.push(new TagRendering(this._tagsES, this._changes, tagRenderingOption.options)) + this._infoboxes.push( + tagRenderingOption.construct(this._tagsES, this._changes)); } title = title ?? new TagRenderingOptions( @@ -60,10 +59,10 @@ export class FeatureInfoBox extends UIElement { } ) - this._title = new TagRendering(this._tagsES, this._changes, title.options); + this._title = new TagRenderingOptions(title.options).construct(this._tagsES, this._changes); - this._osmLink = new TagRendering(this._tagsES, this._changes, new OsmLink().options); - this._wikipedialink = new TagRendering(this._tagsES, this._changes, new WikipediaLink().options); + this._osmLink =new OsmLink().construct(this._tagsES, this._changes); + this._wikipedialink = new WikipediaLink().construct(this._tagsES, this._changes); this._pictureUploader = new OsmImageUploadHandler(tagsES, userDetails, preferedPictureLicense, changes, this._imageElement.slideshow).getUI(); @@ -73,7 +72,7 @@ export class FeatureInfoBox extends UIElement { const info = []; - const questions = []; + const questions : TagDependantUIElement[] = []; for (const infobox of this._infoboxes) { if (infobox.IsKnown()) { @@ -92,9 +91,9 @@ export class FeatureInfoBox extends UIElement { let score = -1000; for (const question of questions) { - if (mostImportantQuestion === undefined || question.priority > score) { + if (mostImportantQuestion === undefined || question.Priority() > score) { mostImportantQuestion = question; - score = question.priority; + score = question.Priority(); } } diff --git a/index.ts b/index.ts index 66a2bc6..45fd058 100644 --- a/index.ts +++ b/index.ts @@ -27,6 +27,7 @@ import {AllKnownLayouts} from "./Customizations/AllKnownLayouts"; // --------------------- Read the URL parameters ----------------- +// @ts-ignore if(location.href.startsWith("http://buurtnatuur.be")){ // Reload the https version. This is important for the 'locate me' button window.location.replace("https://buurtnatuur.be");