forked from MapComplete/MapComplete
		
	Performance improvements
This commit is contained in:
		
							parent
							
								
									49f78d5604
								
							
						
					
					
						commit
						8f8ef690a4
					
				
					 11 changed files with 125 additions and 101 deletions
				
			
		|  | @ -8,13 +8,13 @@ import {TagRenderingConfigJson} from "./TagRenderingConfigJson"; | |||
| import {Translation} from "../../UI/i18n/Translation"; | ||||
| import Img from "../../UI/Base/Img"; | ||||
| import Svg from "../../Svg"; | ||||
| import {SubstitutedTranslation} from "../../UI/SpecialVisualizations"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import Combine from "../../UI/Base/Combine"; | ||||
| import {VariableUiElement} from "../../UI/Base/VariableUIElement"; | ||||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import {FixedUiElement} from "../../UI/Base/FixedUiElement"; | ||||
| import {UIElement} from "../../UI/UIElement"; | ||||
| import {SubstitutedTranslation} from "../../UI/SubstitutedTranslation"; | ||||
| 
 | ||||
| export default class LayerConfig { | ||||
| 
 | ||||
|  |  | |||
|  | @ -336,7 +336,7 @@ export class InitUiElements { | |||
|             } | ||||
|             let features = featuresFreshness.map(ff => ff.feature); | ||||
|             features.forEach(feature => { | ||||
|                 State.state.allElements.addElement(feature); | ||||
|                 State.state.allElements.addOrGetElement(feature); | ||||
|                  | ||||
|                 if(Hash.hash.data === feature.properties.id.replace("/","_")){ | ||||
|                     State.state.selectedElement.setData(feature); | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ export class ElementStorage { | |||
|     } | ||||
| 
 | ||||
|     addElement(element): UIEventSource<any> { | ||||
|         const eventSource = new UIEventSource<any>(element.properties); | ||||
|         const eventSource = new UIEventSource<any>(element.properties, "tags of "+element.properties.id); | ||||
|         this._elements[element.properties.id] = eventSource; | ||||
|         return eventSource; | ||||
|     } | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ export class OsmConnection { | |||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         this.userDetails = new UIEventSource<UserDetails>(new UserDetails()); | ||||
|         this.userDetails = new UIEventSource<UserDetails>(new UserDetails(), "userDetails"); | ||||
|         this.userDetails.data.dryRun = dryRun; | ||||
|         this._dryRun = dryRun; | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ export class UIEventSource<T> { | |||
|             return []; | ||||
|         } | ||||
|         // @ts-ignore
 | ||||
|         window.mcperf = () => { | ||||
|         window.mapcomplete_performance = () => { | ||||
|             console.log(UIEventSource.allSources.length, "uieventsources created"); | ||||
|             const copy = [...UIEventSource.allSources]; | ||||
|             copy.sort((a,b) => b._callbacks.length - a._callbacks.length); | ||||
|  |  | |||
|  | @ -1,10 +1,10 @@ | |||
| import {UIEventSource} from "../../Logic/UIEventSource"; | ||||
| import TagRenderingConfig from "../../Customizations/JSON/TagRenderingConfig"; | ||||
| import {UIElement} from "../UIElement"; | ||||
| import {SubstitutedTranslation} from "../SpecialVisualizations"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import Combine from "../Base/Combine"; | ||||
| import {TagUtils} from "../../Logic/Tags"; | ||||
| import {SubstitutedTranslation} from "../SubstitutedTranslation"; | ||||
| 
 | ||||
| /*** | ||||
|  * Displays the correct value for a known tagrendering | ||||
|  | @ -64,7 +64,7 @@ export default class TagRenderingAnswer extends UIElement { | |||
|          | ||||
|         const tr = this._configuration.GetRenderValue(tags); | ||||
|         if (tr !== undefined) { | ||||
|             this._content = new SubstitutedTranslation(tr, this._tags); | ||||
|             this._content = SubstitutedTranslation.construct(tr, this._tags); | ||||
|             return this._content.Render(); | ||||
|         } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,7 +6,6 @@ import {InputElement} from "../Input/InputElement"; | |||
| import {And, Tag, TagsFilter, TagUtils} from "../../Logic/Tags"; | ||||
| import ValidatedTextField from "../Input/ValidatedTextField"; | ||||
| import {FixedInputElement} from "../Input/FixedInputElement"; | ||||
| import {SubstitutedTranslation} from "../SpecialVisualizations"; | ||||
| import {RadioButton} from "../Input/RadioButton"; | ||||
| import {Utils} from "../../Utils"; | ||||
| import CheckBoxes from "../Input/Checkboxes"; | ||||
|  | @ -19,6 +18,7 @@ import Translations from "../i18n/Translations"; | |||
| import {FixedUiElement} from "../Base/FixedUiElement"; | ||||
| import {Translation} from "../i18n/Translation"; | ||||
| import Constants from "../../Models/Constants"; | ||||
| import {SubstitutedTranslation} from "../SubstitutedTranslation"; | ||||
| 
 | ||||
| /** | ||||
|  * Shows the question element. | ||||
|  | @ -203,7 +203,7 @@ export default class TagRenderingQuestion extends UIElement { | |||
|             return undefined; | ||||
|         } | ||||
|         return new FixedInputElement( | ||||
|             new SubstitutedTranslation(mapping.then, this._tags), | ||||
|             SubstitutedTranslation.construct(mapping.then, this._tags), | ||||
|             mapping.if, | ||||
|             (t0, t1) => t1.isEquivalent(t0)); | ||||
|     } | ||||
|  |  | |||
|  | @ -116,12 +116,12 @@ export default class ShowDataLayer { | |||
|         const popup = L.popup({ | ||||
|             autoPan: true, | ||||
|             closeOnEscapeKey: true, | ||||
|             closeButton: false | ||||
|         }, leafletLayer); | ||||
| 
 | ||||
| 
 | ||||
|         const tags = State.state.allElements.getEventSourceFor(feature); | ||||
|         const uiElement: LazyElement<UIElement> = new LazyElement(() =>new Combine([ new FeatureInfoBox(tags, layer, () => { | ||||
|                 console.log("Closing the popup!") | ||||
|                 State.state.selectedElement.setData(undefined); | ||||
|                 popup.remove(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,9 +5,7 @@ import LiveQueryHandler from "../Logic/Web/LiveQueryHandler"; | |||
| import {ImageCarousel} from "./Image/ImageCarousel"; | ||||
| import Combine from "./Base/Combine"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import Locale from "../UI/i18n/Locale"; | ||||
| import {ImageUploadFlow} from "./Image/ImageUploadFlow"; | ||||
| import {Translation} from "./i18n/Translation"; | ||||
| 
 | ||||
| import ShareButton from "./BigComponents/ShareButton"; | ||||
| import Svg from "../Svg"; | ||||
|  | @ -19,93 +17,6 @@ import OpeningHoursVisualization from "./OpeningHours/OhVisualization"; | |||
| 
 | ||||
| import State from "../State"; | ||||
| 
 | ||||
| export class SubstitutedTranslation extends UIElement { | ||||
|     private readonly tags: UIEventSource<any>; | ||||
|     private readonly translation: Translation; | ||||
|     private content: UIElement[]; | ||||
| 
 | ||||
|     constructor( | ||||
|         translation: Translation, | ||||
|         tags: UIEventSource<any>) { | ||||
|         super(tags); | ||||
|         this.translation = translation; | ||||
|         this.tags = tags; | ||||
|         const self = this; | ||||
|         tags.addCallbackAndRun(() => { | ||||
|             self.content = self.CreateContent(); | ||||
|             self.Update(); | ||||
|         }); | ||||
| 
 | ||||
|         Locale.language.addCallback(() => { | ||||
|             self.content = self.CreateContent(); | ||||
|             self.Update(); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine(this.content).Render(); | ||||
|     } | ||||
| 
 | ||||
|     private CreateContent(): UIElement[] { | ||||
|         let txt = this.translation?.txt; | ||||
|         if (txt === undefined) { | ||||
|             return [] | ||||
|         } | ||||
|         const tags = this.tags.data; | ||||
|         txt = SubstitutedTranslation.SubstituteKeys(txt, tags); | ||||
|         return this.EvaluateSpecialComponents(txt); | ||||
|     } | ||||
| 
 | ||||
|     public static SubstituteKeys(txt: string, tags: any) { | ||||
|         for (const key in tags) { | ||||
|             // Poor mans replace all
 | ||||
|             txt = txt.split("{" + key + "}").join(tags[key]); | ||||
|         } | ||||
|         return txt; | ||||
|     } | ||||
| 
 | ||||
|     private EvaluateSpecialComponents(template: string): UIElement[] { | ||||
| 
 | ||||
|         for (const knownSpecial of SpecialVisualizations.specialVisualizations) { | ||||
| 
 | ||||
|             // Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
 | ||||
|             const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)}(.*)`); | ||||
|             if (matched != null) { | ||||
| 
 | ||||
|                 // We found a special component that should be brought to live
 | ||||
|                 const partBefore = this.EvaluateSpecialComponents(matched[1]); | ||||
|                 const argument = matched[2].trim(); | ||||
|                 const partAfter = this.EvaluateSpecialComponents(matched[3]); | ||||
|                 try { | ||||
|                     const args = knownSpecial.args.map(arg => arg.defaultValue ?? ""); | ||||
|                     if (argument.length > 0) { | ||||
|                         const realArgs = argument.split(",").map(str => str.trim()); | ||||
|                         for (let i = 0; i < realArgs.length; i++) { | ||||
|                             if (args.length <= i) { | ||||
|                                 args.push(realArgs[i]); | ||||
|                             } else { | ||||
|                                 args[i] = realArgs[i]; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
| 
 | ||||
|                     const element = knownSpecial.constr(State.state, this.tags, args); | ||||
|                     return [...partBefore, element, ...partAfter] | ||||
|                 } catch (e) { | ||||
|                     console.error(e); | ||||
|                     return [...partBefore, new FixedUiElement(`Failed loading ${knownSpecial.funcName}(${matched[2]}): ${e}`), ...partAfter] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // IF we end up here, no changes have to be made
 | ||||
|         return [new FixedUiElement(template)]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export default class SpecialVisualizations { | ||||
| 
 | ||||
|     public static specialVisualizations: { | ||||
|  |  | |||
							
								
								
									
										113
									
								
								UI/SubstitutedTranslation.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								UI/SubstitutedTranslation.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,113 @@ | |||
| import {UIElement} from "./UIElement"; | ||||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import {Translation} from "./i18n/Translation"; | ||||
| import Locale from "./i18n/Locale"; | ||||
| import Combine from "./Base/Combine"; | ||||
| import State from "../State"; | ||||
| import {FixedUiElement} from "./Base/FixedUiElement"; | ||||
| import SpecialVisualizations from "./SpecialVisualizations"; | ||||
| 
 | ||||
| export class SubstitutedTranslation extends UIElement { | ||||
|     private static cachedTranslations: Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>> = new Map<Translation, Map<UIEventSource<any>, SubstitutedTranslation>>(); | ||||
|     private readonly tags: UIEventSource<any>; | ||||
|     private readonly translation: Translation; | ||||
|     private content: UIElement[]; | ||||
| 
 | ||||
|     private constructor( | ||||
|         translation: Translation, | ||||
|         tags: UIEventSource<any>) { | ||||
|         super(tags); | ||||
|         this.translation = translation; | ||||
|         this.tags = tags; | ||||
|         const self = this; | ||||
|         tags.addCallbackAndRun(() => { | ||||
|             self.content = self.CreateContent(); | ||||
|             self.Update(); | ||||
|         }); | ||||
| 
 | ||||
|         Locale.language.addCallback(() => { | ||||
|             self.content = self.CreateContent(); | ||||
|             self.Update(); | ||||
|         }); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static construct( | ||||
|         translation: Translation, | ||||
|         tags: UIEventSource<any>): SubstitutedTranslation { | ||||
|         if (!this.cachedTranslations.has(translation)) { | ||||
|             this.cachedTranslations.set(translation, new Map<UIEventSource<any>, SubstitutedTranslation>()); | ||||
|         } | ||||
|         const innerMap = this.cachedTranslations.get(translation); | ||||
| 
 | ||||
|         const cachedTranslation = innerMap.get(tags); | ||||
|         if (cachedTranslation !== undefined) { | ||||
|             return cachedTranslation; | ||||
|         } | ||||
|         const st = new SubstitutedTranslation(translation, tags); | ||||
|         innerMap.set(tags, st); | ||||
|         return st; | ||||
|     } | ||||
| 
 | ||||
|     public static SubstituteKeys(txt: string, tags: any) { | ||||
|         for (const key in tags) { | ||||
|             // Poor mans replace all
 | ||||
|             txt = txt.split("{" + key + "}").join(tags[key]); | ||||
|         } | ||||
|         return txt; | ||||
|     } | ||||
| 
 | ||||
|     InnerRender(): string { | ||||
|         return new Combine(this.content).Render(); | ||||
|     } | ||||
| 
 | ||||
|     private CreateContent(): UIElement[] { | ||||
|         let txt = this.translation?.txt; | ||||
|         if (txt === undefined) { | ||||
|             return [] | ||||
|         } | ||||
|         const tags = this.tags.data; | ||||
|         txt = SubstitutedTranslation.SubstituteKeys(txt, tags); | ||||
|         return this.EvaluateSpecialComponents(txt); | ||||
|     } | ||||
| 
 | ||||
|     private EvaluateSpecialComponents(template: string): UIElement[] { | ||||
| 
 | ||||
|         for (const knownSpecial of SpecialVisualizations.specialVisualizations) { | ||||
| 
 | ||||
|             // Note: the '.*?' in the regex reads as 'any character, but in a non-greedy way'
 | ||||
|             const matched = template.match(`(.*){${knownSpecial.funcName}\\((.*?)\\)}(.*)`); | ||||
|             if (matched != null) { | ||||
| 
 | ||||
|                 // We found a special component that should be brought to live
 | ||||
|                 const partBefore = this.EvaluateSpecialComponents(matched[1]); | ||||
|                 const argument = matched[2].trim(); | ||||
|                 const partAfter = this.EvaluateSpecialComponents(matched[3]); | ||||
|                 try { | ||||
|                     const args = knownSpecial.args.map(arg => arg.defaultValue ?? ""); | ||||
|                     if (argument.length > 0) { | ||||
|                         const realArgs = argument.split(",").map(str => str.trim()); | ||||
|                         for (let i = 0; i < realArgs.length; i++) { | ||||
|                             if (args.length <= i) { | ||||
|                                 args.push(realArgs[i]); | ||||
|                             } else { | ||||
|                                 args[i] = realArgs[i]; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
| 
 | ||||
| 
 | ||||
|                     const element = knownSpecial.constr(State.state, this.tags, args); | ||||
|                     return [...partBefore, element, ...partAfter] | ||||
|                 } catch (e) { | ||||
|                     console.error(e); | ||||
|                     return [...partBefore, new FixedUiElement(`Failed loading ${knownSpecial.funcName}(${matched[2]}): ${e}`), ...partAfter] | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         // IF we end up here, no changes have to be made
 | ||||
|         return [new FixedUiElement(template)]; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -9,10 +9,10 @@ import Translations from "../UI/i18n/Translations"; | |||
| import {UIEventSource} from "../Logic/UIEventSource"; | ||||
| import TagRenderingConfig from "../Customizations/JSON/TagRenderingConfig"; | ||||
| import EditableTagRendering from "../UI/Popup/EditableTagRendering"; | ||||
| import {SubstitutedTranslation} from "../UI/SpecialVisualizations"; | ||||
| import {Translation} from "../UI/i18n/Translation"; | ||||
| import {OH, OpeningHour} from "../UI/OpeningHours/OpeningHours"; | ||||
| import PublicHolidayInput from "../UI/OpeningHours/PublicHolidayInput"; | ||||
| import {SubstitutedTranslation} from "../UI/SubstitutedTranslation"; | ||||
| 
 | ||||
| 
 | ||||
| new T([ | ||||
|  | @ -109,7 +109,7 @@ new T([ | |||
|         equal(undefined, tr.GetRenderValue({"foo": "bar"})); | ||||
|         equal("Has no name", tr.GetRenderValue({"noname": "yes"})?.txt); | ||||
|         equal("Ook een {name}", tr.GetRenderValue({"name": "xyz"})?.txt); | ||||
|         equal("Ook een xyz", new SubstitutedTranslation( tr.GetRenderValue({"name": "xyz"}), | ||||
|         equal("Ook een xyz", SubstitutedTranslation.construct( tr.GetRenderValue({"name": "xyz"}), | ||||
|             new UIEventSource<any>({"name":"xyz"})).InnerRender()); | ||||
|         equal(undefined, tr.GetRenderValue({"foo": "bar"})); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue