Better bookcase quests

This commit is contained in:
Pieter Vander Vennet 2020-07-12 23:19:05 +02:00
parent 1ef2459d54
commit 49cab66a72
11 changed files with 247 additions and 63 deletions

View file

@ -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;
}

View file

@ -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 };

View file

@ -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 <b>{ref}</b>"
}
}).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: "<a href='{website}' target='_blank'>Meer informatie over dit boekenruilkastje</a>",
template: "$$$",
placeholder:"website"
},
priority: 5
template: "$$$",
placeholder: "website"
}
}),
new TagRenderingOptions({
freeform: {
key: "description",
renderTemplate: "<b>Beschrijving door de uitbater</b><br>{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: <br /> <p>{description}</p>"}),
]
;*/
/* 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 {

View file

@ -0,0 +1,18 @@
import {Layout} from "../Layout";
export class All extends Layout{
constructor() {
super(
"all",
"All quest layers",
[],
15,
51.2,
3.2,
"<h3>All quests of MapComplete</h3>" +
"This is a mixed bag. Some quests might be hard or for experts to answer only",
"Please log in",
""
);
}
}

View file

@ -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<any>, 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<any>,
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();
}
}

View file

@ -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";

View file

@ -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<any>, 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[] = [];

View file

@ -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<any>, changes: Changes): TagDependantUIElement;
}
export abstract class TagDependantUIElement extends UIElement {
abstract IsKnown(): boolean;
abstract IsQuestioning(): boolean;
abstract Priority() : number;
}

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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");