| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  | import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" | 
					
						
							|  |  |  | import BaseUIElement from "../BaseUIElement" | 
					
						
							| 
									
										
										
										
											2022-06-05 02:24:14 +02:00
										 |  |  | import { Stores, UIEventSource } from "../../Logic/UIEventSource" | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  | import { DefaultGuiState } from "../DefaultGuiState" | 
					
						
							|  |  |  | import { SubtleButton } from "../Base/SubtleButton" | 
					
						
							|  |  |  | import Img from "../Base/Img" | 
					
						
							|  |  |  | import { FixedUiElement } from "../Base/FixedUiElement" | 
					
						
							|  |  |  | import Combine from "../Base/Combine" | 
					
						
							|  |  |  | import Link from "../Base/Link" | 
					
						
							|  |  |  | import { SubstitutedTranslation } from "../SubstitutedTranslation" | 
					
						
							|  |  |  | import { Utils } from "../../Utils" | 
					
						
							|  |  |  | import Minimap from "../Base/Minimap" | 
					
						
							|  |  |  | import ShowDataLayer from "../ShowDataLayer/ShowDataLayer" | 
					
						
							|  |  |  | import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource" | 
					
						
							|  |  |  | import { VariableUiElement } from "../Base/VariableUIElement" | 
					
						
							|  |  |  | import Loading from "../Base/Loading" | 
					
						
							|  |  |  | import { OsmConnection } from "../../Logic/Osm/OsmConnection" | 
					
						
							|  |  |  | import Translations from "../i18n/Translations" | 
					
						
							| 
									
										
										
										
											2021-12-13 02:05:34 +01:00
										 |  |  | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | 
					
						
							|  |  |  | import { Changes } from "../../Logic/Osm/Changes" | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  | import { UIElement } from "../UIElement" | 
					
						
							|  |  |  | import FilteredLayer from "../../Models/FilteredLayer" | 
					
						
							|  |  |  | import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | 
					
						
							|  |  |  | import Lazy from "../Base/Lazy" | 
					
						
							| 
									
										
										
										
											2022-06-22 20:30:48 +02:00
										 |  |  | import List from "../Base/List" | 
					
						
							| 
									
										
										
										
											2022-11-02 14:44:06 +01:00
										 |  |  | import { SpecialVisualization } from "../SpecialVisualization" | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | export interface AutoAction extends SpecialVisualization { | 
					
						
							|  |  |  |     supportsAutoAction: boolean | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-13 02:05:34 +01:00
										 |  |  |     applyActionOn( | 
					
						
							|  |  |  |         state: { | 
					
						
							|  |  |  |             layoutToUse: LayoutConfig | 
					
						
							|  |  |  |             changes: Changes | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |         tagSource: UIEventSource<any>, | 
					
						
							|  |  |  |         argument: string[] | 
					
						
							|  |  |  |     ): Promise<void> | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  | class ApplyButton extends UIElement { | 
					
						
							|  |  |  |     private readonly icon: string | 
					
						
							|  |  |  |     private readonly text: string | 
					
						
							|  |  |  |     private readonly targetTagRendering: string | 
					
						
							|  |  |  |     private readonly target_layer_id: string | 
					
						
							|  |  |  |     private readonly state: FeaturePipelineState | 
					
						
							|  |  |  |     private readonly target_feature_ids: string[] | 
					
						
							|  |  |  |     private readonly buttonState = new UIEventSource< | 
					
						
							|  |  |  |         "idle" | "running" | "done" | { error: string } | 
					
						
							|  |  |  |     >("idle") | 
					
						
							|  |  |  |     private readonly layer: FilteredLayer | 
					
						
							|  |  |  |     private readonly tagRenderingConfig: TagRenderingConfig | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |     constructor( | 
					
						
							|  |  |  |         state: FeaturePipelineState, | 
					
						
							|  |  |  |         target_feature_ids: string[], | 
					
						
							|  |  |  |         options: { | 
					
						
							|  |  |  |             target_layer_id: string | 
					
						
							|  |  |  |             targetTagRendering: string | 
					
						
							|  |  |  |             text: string | 
					
						
							|  |  |  |             icon: string | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |         super() | 
					
						
							|  |  |  |         this.state = state | 
					
						
							|  |  |  |         this.target_feature_ids = target_feature_ids | 
					
						
							|  |  |  |         this.target_layer_id = options.target_layer_id | 
					
						
							|  |  |  |         this.targetTagRendering = options.targetTagRendering | 
					
						
							|  |  |  |         this.text = options.text | 
					
						
							|  |  |  |         this.icon = options.icon | 
					
						
							|  |  |  |         this.layer = this.state.filteredLayers.data.find( | 
					
						
							|  |  |  |             (l) => l.layerDef.id === this.target_layer_id | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |         ) | 
					
						
							|  |  |  |         this.tagRenderingConfig = this.layer.layerDef.tagRenderings.find( | 
					
						
							|  |  |  |             (tr) => tr.id === this.targetTagRendering | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     protected InnerRender(): string | BaseUIElement { | 
					
						
							|  |  |  |         if (this.target_feature_ids.length === 0) { | 
					
						
							|  |  |  |             return new FixedUiElement("No elements found to perform action") | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (this.tagRenderingConfig === undefined) { | 
					
						
							|  |  |  |             return new FixedUiElement( | 
					
						
							|  |  |  |                 "Target tagrendering " + this.targetTagRendering + " not found" | 
					
						
							|  |  |  |             ).SetClass("alert") | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         const self = this | 
					
						
							|  |  |  |         const button = new SubtleButton(new Img(this.icon), this.text).onClick(() => { | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |             this.buttonState.setData("running") | 
					
						
							|  |  |  |             window.setTimeout(() => { | 
					
						
							|  |  |  |                 self.Run() | 
					
						
							|  |  |  |             }, 50) | 
					
						
							|  |  |  |         }) | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         const explanation = new Combine([ | 
					
						
							|  |  |  |             "The following objects will be updated: ", | 
					
						
							|  |  |  |             ...this.target_feature_ids.map( | 
					
						
							|  |  |  |                 (id) => new Combine([new Link(id, "https:/  /openstreetmap.org/" + id, true), ", "]) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |             ), | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |         ]).SetClass("subtle") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const previewMap = Minimap.createMiniMap({ | 
					
						
							|  |  |  |             allowMoving: false, | 
					
						
							|  |  |  |             background: this.state.backgroundLayer, | 
					
						
							|  |  |  |             addLayerControl: true, | 
					
						
							|  |  |  |         }).SetClass("h-48") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const features = this.target_feature_ids.map((id) => | 
					
						
							|  |  |  |             this.state.allElements.ContainingFeatures.get(id) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         new ShowDataLayer({ | 
					
						
							|  |  |  |             leafletMap: previewMap.leafletMap, | 
					
						
							|  |  |  |             zoomToFeatures: true, | 
					
						
							| 
									
										
										
										
											2022-06-05 02:24:14 +02:00
										 |  |  |             features: StaticFeatureSource.fromGeojson(features), | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             state: this.state, | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |             layerToShow: this.layer.layerDef, | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |         }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return new VariableUiElement( | 
					
						
							|  |  |  |             this.buttonState.map((st) => { | 
					
						
							|  |  |  |                 if (st === "idle") { | 
					
						
							|  |  |  |                     return new Combine([button, previewMap, explanation]) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (st === "done") { | 
					
						
							|  |  |  |                     return new FixedUiElement("All done!").SetClass("thanks") | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 if (st === "running") { | 
					
						
							|  |  |  |                     return new Loading("Applying changes...") | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 const error = st.error | 
					
						
							|  |  |  |                 return new Combine([ | 
					
						
							|  |  |  |                     new FixedUiElement("Something went wrong...").SetClass("alert"), | 
					
						
							|  |  |  |                     new FixedUiElement(error).SetClass("subtle"), | 
					
						
							|  |  |  |                 ]).SetClass("flex flex-col") | 
					
						
							|  |  |  |             }) | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |     private async Run() { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             console.log("Applying auto-action on " + this.target_feature_ids.length + " features") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for (const targetFeatureId of this.target_feature_ids) { | 
					
						
							|  |  |  |                 const featureTags = this.state.allElements.getEventSourceById(targetFeatureId) | 
					
						
							|  |  |  |                 const rendering = this.tagRenderingConfig.GetRenderValue(featureTags.data).txt | 
					
						
							|  |  |  |                 const specialRenderings = Utils.NoNull( | 
					
						
							|  |  |  |                     SubstitutedTranslation.ExtractSpecialComponents(rendering).map((x) => x.special) | 
					
						
							|  |  |  |                 ).filter((v) => v.func["supportsAutoAction"] === true) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if (specialRenderings.length == 0) { | 
					
						
							|  |  |  |                     console.warn( | 
					
						
							|  |  |  |                         "AutoApply: feature " + | 
					
						
							|  |  |  |                             targetFeatureId + | 
					
						
							|  |  |  |                             " got a rendering without supported auto actions:", | 
					
						
							|  |  |  |                         rendering | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 for (const specialRendering of specialRenderings) { | 
					
						
							|  |  |  |                     const action = <AutoAction>specialRendering.func | 
					
						
							|  |  |  |                     await action.applyActionOn(this.state, featureTags, specialRendering.args) | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             console.log("Flushing changes...") | 
					
						
							|  |  |  |             await this.state.changes.flushChanges("Auto button") | 
					
						
							|  |  |  |             this.buttonState.setData("done") | 
					
						
							|  |  |  |         } catch (e) { | 
					
						
							|  |  |  |             console.error("Error while running autoApply: ", e) | 
					
						
							|  |  |  |             this.buttonState.setData({ error: e }) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  | export default class AutoApplyButton implements SpecialVisualization { | 
					
						
							| 
									
										
										
										
											2022-06-22 20:30:48 +02:00
										 |  |  |     public readonly docs: BaseUIElement | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |     public readonly funcName: string = "auto_apply" | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |     public readonly args: { | 
					
						
							|  |  |  |         name: string | 
					
						
							|  |  |  |         defaultValue?: string | 
					
						
							|  |  |  |         doc: string | 
					
						
							|  |  |  |         required?: boolean | 
					
						
							|  |  |  |     }[] = [ | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         { | 
					
						
							|  |  |  |             name: "target_layer", | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |             doc: "The layer that the target features will reside in", | 
					
						
							|  |  |  |             required: true, | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             name: "target_feature_ids", | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |             doc: "The key, of which the value contains a list of ids", | 
					
						
							|  |  |  |             required: true, | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             name: "tag_rendering_id", | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |             doc: "The ID of the tagRendering containing the autoAction. This tagrendering will be calculated. The embedded actions will be executed", | 
					
						
							|  |  |  |             required: true, | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             name: "text", | 
					
						
							| 
									
										
										
										
											2022-03-29 00:20:10 +02:00
										 |  |  |             doc: "The text to show on the button", | 
					
						
							|  |  |  |             required: true, | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         }, | 
					
						
							|  |  |  |         { | 
					
						
							|  |  |  |             name: "icon", | 
					
						
							|  |  |  |             doc: "The icon to show on the button", | 
					
						
							|  |  |  |             defaultValue: "./assets/svg/robot.svg", | 
					
						
							|  |  |  |         }, | 
					
						
							|  |  |  |     ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     constructor(allSpecialVisualisations: SpecialVisualization[]) { | 
					
						
							|  |  |  |         this.docs = AutoApplyButton.generateDocs( | 
					
						
							|  |  |  |             allSpecialVisualisations | 
					
						
							|  |  |  |                 .filter((sv) => sv["supportsAutoAction"] === true) | 
					
						
							|  |  |  |                 .map((sv) => sv.funcName) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |     private static generateDocs(supportedActions: string[]) { | 
					
						
							| 
									
										
										
										
											2022-06-22 20:30:48 +02:00
										 |  |  |         return new Combine([ | 
					
						
							|  |  |  |             "A button to run many actions for many features at once.", | 
					
						
							|  |  |  |             "To effectively use this button, you'll need some ingredients:", | 
					
						
							|  |  |  |             new List([ | 
					
						
							|  |  |  |                 "A target layer with features for which an action is defined in a tag rendering. The following special visualisations support an autoAction: " + | 
					
						
							|  |  |  |                     supportedActions.join(", "), | 
					
						
							|  |  |  |                 "A host feature to place the auto-action on. This can be a big outline (such as a city). Another good option for this is the layer ", | 
					
						
							|  |  |  |                 new Link("current_view", "./BuiltinLayers.md#current_view"), | 
					
						
							|  |  |  |                 "Then, use a calculated tag on the host feature to determine the overlapping object ids", | 
					
						
							|  |  |  |                 "At last, add this component", | 
					
						
							|  |  |  |             ]), | 
					
						
							|  |  |  |         ]) | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |     constr( | 
					
						
							|  |  |  |         state: FeaturePipelineState, | 
					
						
							|  |  |  |         tagSource: UIEventSource<any>, | 
					
						
							|  |  |  |         argument: string[], | 
					
						
							|  |  |  |         guistate: DefaultGuiState | 
					
						
							|  |  |  |     ): BaseUIElement { | 
					
						
							|  |  |  |         try { | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             if ( | 
					
						
							|  |  |  |                 !state.layoutToUse.official && | 
					
						
							|  |  |  |                 !( | 
					
						
							|  |  |  |                     state.featureSwitchIsTesting.data || | 
					
						
							|  |  |  |                     state.osmConnection._oauth_config.url === | 
					
						
							|  |  |  |                         OsmConnection.oauth_configs["osm-test"].url | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             ) { | 
					
						
							|  |  |  |                 const t = Translations.t.general.add.import | 
					
						
							|  |  |  |                 return new Combine([ | 
					
						
							|  |  |  |                     new FixedUiElement( | 
					
						
							|  |  |  |                         "The auto-apply button is only available in official themes (or in testing mode)" | 
					
						
							|  |  |  |                     ).SetClass("alert"), | 
					
						
							|  |  |  |                     t.howToTest, | 
					
						
							|  |  |  |                 ]) | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2022-01-26 21:40:38 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             const target_layer_id = argument[0] | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |             const targetTagRendering = argument[2] | 
					
						
							|  |  |  |             const text = argument[3] | 
					
						
							|  |  |  |             const icon = argument[4] | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             const options = { | 
					
						
							|  |  |  |                 target_layer_id, | 
					
						
							|  |  |  |                 targetTagRendering, | 
					
						
							|  |  |  |                 text, | 
					
						
							|  |  |  |                 icon, | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             return new Lazy(() => { | 
					
						
							|  |  |  |                 const to_parse = new UIEventSource(undefined) | 
					
						
							|  |  |  |                 // Very ugly hack: read the value every 500ms
 | 
					
						
							| 
									
										
										
										
											2022-06-05 02:24:14 +02:00
										 |  |  |                 Stores.Chronic(500, () => to_parse.data === undefined).addCallback(() => { | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |                     const applicable = tagSource.data[argument[1]] | 
					
						
							|  |  |  |                     to_parse.setData(applicable) | 
					
						
							|  |  |  |                 }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 const loading = new Loading("Gathering which elements support auto-apply... ") | 
					
						
							|  |  |  |                 return new VariableUiElement( | 
					
						
							|  |  |  |                     to_parse.map((ids) => { | 
					
						
							| 
									
										
										
										
											2022-02-11 03:57:39 +01:00
										 |  |  |                         if (ids === undefined) { | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |                             return loading | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |                         return new ApplyButton(state, JSON.parse(ids), options) | 
					
						
							|  |  |  |                     }) | 
					
						
							| 
									
										
										
										
											2022-09-08 21:40:48 +02:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2022-02-10 23:16:14 +01:00
										 |  |  |             }) | 
					
						
							| 
									
										
										
										
											2021-12-12 02:59:24 +01:00
										 |  |  |         } catch (e) { | 
					
						
							|  |  |  |             return new FixedUiElement( | 
					
						
							|  |  |  |                 "Could not generate a auto_apply-button for key " + argument[0] + " due to " + e | 
					
						
							|  |  |  |             ).SetClass("alert") | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     getLayerDependencies(args: string[]): string[] { | 
					
						
							|  |  |  |         return [args[0]] | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |