Seperate logic to detect special renderings and to actually render them, add a minimap to all popups (if no minimap is defined seperately)
This commit is contained in:
parent
ab0d510cdd
commit
284102c2f1
6 changed files with 118 additions and 57 deletions
|
@ -7,6 +7,7 @@ import {Utils} from "../../Utils";
|
||||||
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
import {TagUtils} from "../../Logic/Tags/TagUtils";
|
||||||
import {And} from "../../Logic/Tags/And";
|
import {And} from "../../Logic/Tags/And";
|
||||||
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
import {TagsFilter} from "../../Logic/Tags/TagsFilter";
|
||||||
|
import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation";
|
||||||
|
|
||||||
|
|
||||||
/***
|
/***
|
||||||
|
@ -67,14 +68,13 @@ export default class TagRenderingConfig {
|
||||||
if (json.freeform) {
|
if (json.freeform) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.freeform = {
|
this.freeform = {
|
||||||
key: json.freeform.key,
|
key: json.freeform.key,
|
||||||
type: json.freeform.type ?? "string",
|
type: json.freeform.type ?? "string",
|
||||||
addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
|
addExtraTags: json.freeform.addExtraTags?.map((tg, i) =>
|
||||||
FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
|
FromJSON.Tag(tg, `${context}.extratag[${i}]`)) ?? [],
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
if (json.freeform["extraTags"] !== undefined) {
|
if (json.freeform["extraTags"] !== undefined) {
|
||||||
throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})`
|
throw `Freeform.extraTags is defined. This should probably be 'freeform.addExtraTag' (at ${context})`
|
||||||
|
@ -82,9 +82,8 @@ export default class TagRenderingConfig {
|
||||||
if (this.freeform.key === undefined || this.freeform.key === "") {
|
if (this.freeform.key === undefined || this.freeform.key === "") {
|
||||||
throw `Freeform.key is undefined or the empty string - this is not allowed; either fill out something or remove the freeform block alltogether. Error in ${context}`
|
throw `Freeform.key is undefined or the empty string - this is not allowed; either fill out something or remove the freeform block alltogether. Error in ${context}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (ValidatedTextField.AllTypes[this.freeform.type] === undefined) {
|
if (ValidatedTextField.AllTypes[this.freeform.type] === undefined) {
|
||||||
const knownKeys = ValidatedTextField.tpList.map(tp => tp.name).join(", ");
|
const knownKeys = ValidatedTextField.tpList.map(tp => tp.name).join(", ");
|
||||||
throw `Freeform.key ${this.freeform.key} is an invalid type. Known keys are ${knownKeys}`
|
throw `Freeform.key ${this.freeform.key} is an invalid type. Known keys are ${knownKeys}`
|
||||||
|
@ -328,4 +327,25 @@ export default class TagRenderingConfig {
|
||||||
return usedIcons;
|
return usedIcons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this tag rendering has a minimap in some language.
|
||||||
|
* Note: this might be hidden by conditions
|
||||||
|
*/
|
||||||
|
public hasMinimap(): boolean {
|
||||||
|
const translations : Translation[]= Utils.NoNull([this.render, ...(this.mappings ?? []).map(m => m.then)]);
|
||||||
|
for (const translation of translations) {
|
||||||
|
for (const key in translation.translations) {
|
||||||
|
if(!translation.translations.hasOwnProperty(key)){
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const template = translation.translations[key]
|
||||||
|
const parts = SubstitutedTranslation.ExtractSpecialComponents(template)
|
||||||
|
const hasMiniMap = parts.filter(part =>part.special !== undefined ).some(special => special.special.func.funcName === "minimap")
|
||||||
|
if(hasMiniMap){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -12,6 +12,7 @@ import {Tag} from "../../Logic/Tags/Tag";
|
||||||
import Constants from "../../Models/Constants";
|
import Constants from "../../Models/Constants";
|
||||||
import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
|
import SharedTagRenderings from "../../Customizations/SharedTagRenderings";
|
||||||
import BaseUIElement from "../BaseUIElement";
|
import BaseUIElement from "../BaseUIElement";
|
||||||
|
import AllKnownLayers from "../../Customizations/AllKnownLayers";
|
||||||
|
|
||||||
export default class FeatureInfoBox extends ScrollableFullScreen {
|
export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
|
|
||||||
|
@ -64,13 +65,19 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
|
||||||
if (!questionBoxIsUsed) {
|
if (!questionBoxIsUsed) {
|
||||||
renderings.push(questionBox);
|
renderings.push(questionBox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasMinimap = layerConfig.tagRenderings.some(tr => tr.hasMinimap())
|
||||||
|
if(!hasMinimap){
|
||||||
|
renderings.push(new TagRenderingAnswer(tags, SharedTagRenderings.SharedTagRendering.get("minimap")))
|
||||||
|
}
|
||||||
|
|
||||||
if (State.state.osmConnection.userDetails.data.csCount >= Constants.userJourney.historyLinkVisible ||
|
if (State.state.osmConnection.userDetails.data.csCount >= Constants.userJourney.historyLinkVisible ||
|
||||||
State.state.featureSwitchIsDebugging.data == true ||
|
State.state.featureSwitchIsDebugging.data == true ||
|
||||||
State.state.featureSwitchIsTesting.data == true) {
|
State.state.featureSwitchIsTesting.data == true) {
|
||||||
renderings.push(new TagRenderingAnswer( tags, SharedTagRenderings.SharedTagRendering.get("last_edit")))
|
renderings.push(new TagRenderingAnswer( tags, SharedTagRenderings.SharedTagRendering.get("last_edit")))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (State.state.featureSwitchIsDebugging.data) {
|
if (State.state.featureSwitchIsDebugging.data) {
|
||||||
const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, new Tag("id", ""), "");
|
const config: TagRenderingConfig = new TagRenderingConfig({render: "{all_tags()}"}, new Tag("id", ""), "");
|
||||||
renderings.push(new TagRenderingAnswer(tags, config))
|
renderings.push(new TagRenderingAnswer(tags, config))
|
||||||
|
|
|
@ -25,16 +25,18 @@ import Loc from "../Models/Loc";
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
import BaseLayer from "../Models/BaseLayer";
|
import BaseLayer from "../Models/BaseLayer";
|
||||||
|
|
||||||
|
export interface SpecialVisualization{
|
||||||
|
funcName: string,
|
||||||
|
constr: ((state: State, tagSource: UIEventSource<any>, argument: string[]) => BaseUIElement),
|
||||||
|
docs: string,
|
||||||
|
example?: string,
|
||||||
|
args: { name: string, defaultValue?: string, doc: string }[]
|
||||||
|
}
|
||||||
|
|
||||||
export default class SpecialVisualizations {
|
export default class SpecialVisualizations {
|
||||||
|
|
||||||
|
|
||||||
public static specialVisualizations: {
|
public static specialVisualizations: SpecialVisualization[] =
|
||||||
funcName: string,
|
|
||||||
constr: ((state: State, tagSource: UIEventSource<any>, argument: string[]) => BaseUIElement),
|
|
||||||
docs: string,
|
|
||||||
example?: string,
|
|
||||||
args: { name: string, defaultValue?: string, doc: string }[]
|
|
||||||
}[] =
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
funcName: "all_tags",
|
funcName: "all_tags",
|
||||||
|
@ -93,7 +95,7 @@ export default class SpecialVisualizations {
|
||||||
docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div",
|
docs: "A small map showing the selected feature. Note that no styling is applied, wrap this in a div",
|
||||||
args: [
|
args: [
|
||||||
{
|
{
|
||||||
doc: "The zoomlevel: the higher, the more zoomed in with 1 being the entire world and 19 being really close",
|
doc: "The (maximum) zoomlevel: the target zoomlevel after fitting the entire feature. The minimap will fit the entire feature, then zoom out to this zoom level. The higher, the more zoomed in with 1 being the entire world and 19 being really close",
|
||||||
name: "zoomlevel",
|
name: "zoomlevel",
|
||||||
defaultValue: "18"
|
defaultValue: "18"
|
||||||
},
|
},
|
||||||
|
@ -135,17 +137,26 @@ export default class SpecialVisualizations {
|
||||||
zoom = parsed;
|
zoom = parsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const locationSource =new UIEventSource<Loc>({
|
||||||
|
lat: Number(properties._lat),
|
||||||
|
lon: Number(properties._lon),
|
||||||
|
zoom: zoom
|
||||||
|
})
|
||||||
const minimap = SpecialVisualizations.constructMiniMap(
|
const minimap = SpecialVisualizations.constructMiniMap(
|
||||||
{
|
{
|
||||||
background: state.backgroundLayer,
|
background: state.backgroundLayer,
|
||||||
location: new UIEventSource<Loc>({
|
location: locationSource,
|
||||||
lat: Number(properties._lat),
|
|
||||||
lon: Number(properties._lon),
|
|
||||||
zoom: zoom
|
|
||||||
}),
|
|
||||||
allowMoving: false
|
allowMoving: false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
locationSource.addCallback(loc => {
|
||||||
|
if(loc.zoom > zoom){
|
||||||
|
// We zoom back
|
||||||
|
locationSource.data.zoom = zoom;
|
||||||
|
locationSource.ping();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
SpecialVisualizations.constructShowDataLayer(
|
SpecialVisualizations.constructShowDataLayer(
|
||||||
featuresToShow,
|
featuresToShow,
|
||||||
|
@ -362,6 +373,19 @@ export default class SpecialVisualizations {
|
||||||
}
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
private static byName() : Map<string, SpecialVisualization>{
|
||||||
|
const result = new Map<string, SpecialVisualization>();
|
||||||
|
|
||||||
|
for (const specialVisualization of SpecialVisualizations.specialVisualizations) {
|
||||||
|
result.set(specialVisualization.funcName, specialVisualization)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static specialVisualisationsByName: Map<string, SpecialVisualization> = SpecialVisualizations.byName();
|
||||||
|
|
||||||
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();
|
static HelpMessage: BaseUIElement = SpecialVisualizations.GenHelpMessage();
|
||||||
static constructMiniMap: (options?: {
|
static constructMiniMap: (options?: {
|
||||||
background?: UIEventSource<BaseLayer>,
|
background?: UIEventSource<BaseLayer>,
|
||||||
|
|
|
@ -3,8 +3,7 @@ import {Translation} from "./i18n/Translation";
|
||||||
import Locale from "./i18n/Locale";
|
import Locale from "./i18n/Locale";
|
||||||
import State from "../State";
|
import State from "../State";
|
||||||
import {FixedUiElement} from "./Base/FixedUiElement";
|
import {FixedUiElement} from "./Base/FixedUiElement";
|
||||||
import SpecialVisualizations from "./SpecialVisualizations";
|
import SpecialVisualizations, {SpecialVisualization} from "./SpecialVisualizations";
|
||||||
import BaseUIElement from "./BaseUIElement";
|
|
||||||
import {Utils} from "../Utils";
|
import {Utils} from "../Utils";
|
||||||
import {VariableUiElement} from "./Base/VariableUIElement";
|
import {VariableUiElement} from "./Base/VariableUIElement";
|
||||||
import Combine from "./Base/Combine";
|
import Combine from "./Base/Combine";
|
||||||
|
@ -15,13 +14,26 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
translation: Translation,
|
translation: Translation,
|
||||||
tagsSource: UIEventSource<any>) {
|
tagsSource: UIEventSource<any>) {
|
||||||
super(
|
super(
|
||||||
tagsSource.map(tags => {
|
Locale.language.map(language => {
|
||||||
const txt = Utils.SubstituteKeys(translation.txt, tags)
|
const txt = translation.textFor(language)
|
||||||
if (txt === undefined) {
|
if (txt === undefined) {
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
return new Combine(SubstitutedTranslation.EvaluateSpecialComponents(txt, tagsSource))
|
return new Combine(SubstitutedTranslation.ExtractSpecialComponents(txt).map(
|
||||||
}, [Locale.language])
|
proto => {
|
||||||
|
if (proto.fixed !== undefined) {
|
||||||
|
return new VariableUiElement(tagsSource.map(tags => Utils.SubstituteKeys(proto.fixed, tags)));
|
||||||
|
}
|
||||||
|
const viz = proto.special;
|
||||||
|
try {
|
||||||
|
return viz.func.constr(State.state, tagsSource, proto.special.args).SetStyle(proto.special.style);
|
||||||
|
} catch (e) {
|
||||||
|
console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e)
|
||||||
|
return new FixedUiElement(`Could not generate special rendering for ${viz.func}(${viz.args.join(", ")}) ${e}`).SetStyle("alert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
))
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -29,7 +41,13 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static EvaluateSpecialComponents(template: string, tags: UIEventSource<any>): BaseUIElement[] {
|
public static ExtractSpecialComponents(template: string): {
|
||||||
|
fixed?: string, special?: {
|
||||||
|
func: SpecialVisualization,
|
||||||
|
args: string[],
|
||||||
|
style: string
|
||||||
|
}
|
||||||
|
}[] {
|
||||||
|
|
||||||
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
for (const knownSpecial of SpecialVisualizations.specialVisualizations) {
|
||||||
|
|
||||||
|
@ -38,38 +56,29 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
if (matched != null) {
|
if (matched != null) {
|
||||||
|
|
||||||
// We found a special component that should be brought to live
|
// We found a special component that should be brought to live
|
||||||
const partBefore = SubstitutedTranslation.EvaluateSpecialComponents(matched[1], tags);
|
const partBefore = SubstitutedTranslation.ExtractSpecialComponents(matched[1]);
|
||||||
const argument = matched[2].trim();
|
const argument = matched[2].trim();
|
||||||
const style = matched[3]?.substring(1) ?? ""
|
const style = matched[3]?.substring(1) ?? ""
|
||||||
const partAfter = SubstitutedTranslation.EvaluateSpecialComponents(matched[4], tags);
|
const partAfter = SubstitutedTranslation.ExtractSpecialComponents(matched[4]);
|
||||||
try {
|
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
||||||
const args = knownSpecial.args.map(arg => arg.defaultValue ?? "");
|
if (argument.length > 0) {
|
||||||
if (argument.length > 0) {
|
const realArgs = argument.split(",").map(str => str.trim());
|
||||||
const realArgs = argument.split(",").map(str => str.trim());
|
for (let i = 0; i < realArgs.length; i++) {
|
||||||
for (let i = 0; i < realArgs.length; i++) {
|
if (args.length <= i) {
|
||||||
if (args.length <= i) {
|
args.push(realArgs[i]);
|
||||||
args.push(realArgs[i]);
|
} else {
|
||||||
} else {
|
args[i] = realArgs[i];
|
||||||
args[i] = realArgs[i];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let element: BaseUIElement = new FixedUiElement(`Constructing ${knownSpecial}(${args.join(", ")})`)
|
|
||||||
try {
|
|
||||||
element = knownSpecial.constr(State.state, tags, args);
|
|
||||||
element.SetStyle(style)
|
|
||||||
} catch (e) {
|
|
||||||
console.error("SPECIALRENDERING FAILED for", tags.data.id, e)
|
|
||||||
element = new FixedUiElement(`Could not generate special rendering for ${knownSpecial}(${args.join(", ")}) ${e}`).SetClass("alert")
|
|
||||||
}
|
|
||||||
|
|
||||||
return [...partBefore, element, ...partAfter]
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
return [...partBefore, new FixedUiElement(`Failed loading ${knownSpecial.funcName}(${matched[2]}): ${e}`), ...partAfter]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let element;
|
||||||
|
element = {special:{
|
||||||
|
args: args,
|
||||||
|
style: style,
|
||||||
|
func: knownSpecial
|
||||||
|
}}
|
||||||
|
return [...partBefore, element, ...partAfter]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +89,7 @@ export class SubstitutedTranslation extends VariableUiElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// IF we end up here, no changes have to be made - except to remove any resting {}
|
// IF we end up here, no changes have to be made - except to remove any resting {}
|
||||||
return [new FixedUiElement(template.replace(/{.*}/g, ""))];
|
return [{fixed: template}];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -195,4 +195,5 @@ export class Translation extends BaseUIElement {
|
||||||
}
|
}
|
||||||
return allIcons.filter(icon => icon != undefined)
|
return allIcons.filter(icon => icon != undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@
|
||||||
"render": "{reviews()}"
|
"render": "{reviews()}"
|
||||||
},
|
},
|
||||||
"minimap": {
|
"minimap": {
|
||||||
"render": "{minimap(19, id): width:100%; height:6rem; border-radius:999rem; overflow: hidden; pointer-events: none;}"
|
"render": "{minimap(18, id): width:100%; height:8rem; border-radius:2rem; overflow: hidden; pointer-events: none;}"
|
||||||
},
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"question": {
|
"question": {
|
||||||
|
|
Loading…
Add table
Reference in a new issue