forked from MapComplete/MapComplete
		
	Port user settings
This commit is contained in:
		
							parent
							
								
									97aaa8e941
								
							
						
					
					
						commit
						8085079eff
					
				
					 30 changed files with 566 additions and 497 deletions
				
			
		|  | @ -2,10 +2,17 @@ import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | |||
| import { OsmConnection } from "../Osm/OsmConnection" | ||||
| import { MangroveIdentity } from "../Web/MangroveReviews" | ||||
| import { Store, Stores, UIEventSource } from "../UIEventSource" | ||||
| import Locale from "../../UI/i18n/Locale" | ||||
| import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource" | ||||
| import { FeatureSource } from "../FeatureSource/FeatureSource" | ||||
| import { Feature } from "geojson" | ||||
| import { Utils } from "../../Utils" | ||||
| import translators from "../../assets/translators.json" | ||||
| import codeContributors from "../../assets/contributors.json" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson" | ||||
| import usersettings from "../../assets/generated/layers/usersettings.json" | ||||
| import Locale from "../../UI/i18n/Locale" | ||||
| import LinkToWeblate from "../../UI/Base/LinkToWeblate" | ||||
| 
 | ||||
| /** | ||||
|  * The part of the state which keeps track of user-related stuff, e.g. the OSM-connection, | ||||
|  | @ -30,16 +37,30 @@ export default class UserRelatedState { | |||
|      * The number of seconds that the GPS-locations are stored in memory. | ||||
|      * Time in seconds | ||||
|      */ | ||||
|     public gpsLocationHistoryRetentionTime = new UIEventSource( | ||||
|     public readonly gpsLocationHistoryRetentionTime = new UIEventSource( | ||||
|         7 * 24 * 60 * 60, | ||||
|         "gps_location_retention" | ||||
|     ) | ||||
| 
 | ||||
|     constructor(osmConnection: OsmConnection, availableLanguages?: string[]) { | ||||
|     /** | ||||
|      * Preferences as tags exposes many preferences and state properties as record. | ||||
|      * This is used to bridge the internal state with the usersettings.json layerconfig file | ||||
|      */ | ||||
|     public readonly preferencesAsTags: UIEventSource<Record<string, string>> | ||||
|     public static readonly usersettingsConfig = new LayerConfig( | ||||
|         <LayerConfigJson>usersettings, | ||||
|         "userinformationpanel" | ||||
|     ) | ||||
| 
 | ||||
|     constructor( | ||||
|         osmConnection: OsmConnection, | ||||
|         availableLanguages?: string[], | ||||
|         layout?: LayoutConfig | ||||
|     ) { | ||||
|         this.osmConnection = osmConnection | ||||
|         { | ||||
|             const translationMode: UIEventSource<undefined | "true" | "false" | "mobile" | string> = | ||||
|                 this.osmConnection.GetPreference("translation-mode") | ||||
|                 this.osmConnection.GetPreference("translation-mode", "false") | ||||
|             translationMode.addCallbackAndRunD((mode) => { | ||||
|                 mode = mode.toLowerCase() | ||||
|                 if (mode === "true" || mode === "yes") { | ||||
|  | @ -73,6 +94,8 @@ export default class UserRelatedState { | |||
|         this.installedUserThemes = this.InitInstalledUserThemes() | ||||
| 
 | ||||
|         this.homeLocation = this.initHomeLocation() | ||||
| 
 | ||||
|         this.preferencesAsTags = this.initAmendedPrefs(layout) | ||||
|     } | ||||
| 
 | ||||
|     public GetUnofficialTheme(id: string): | ||||
|  | @ -211,4 +234,127 @@ export default class UserRelatedState { | |||
|         }) | ||||
|         return new StaticFeatureSource(feature) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initialize the 'amended preferences'. | ||||
|      * This is inherently a dirty and chaotic method, as it shoves many properties into this EventSourcd | ||||
|      * */ | ||||
|     private initAmendedPrefs(layout?: LayoutConfig): UIEventSource<Record<string, string>> { | ||||
|         const amendedPrefs = new UIEventSource<Record<string, string>>({ | ||||
|             _theme: layout?.id, | ||||
|             _backend: this.osmConnection.Backend(), | ||||
|         }) | ||||
| 
 | ||||
|         const osmConnection = this.osmConnection | ||||
|         osmConnection.preferencesHandler.preferences.addCallback((newPrefs) => { | ||||
|             for (const k in newPrefs) { | ||||
|                 amendedPrefs.data[k] = newPrefs[k] | ||||
|             } | ||||
|             amendedPrefs.ping() | ||||
|         }) | ||||
|         const usersettingsConfig = UserRelatedState.usersettingsConfig | ||||
|         const translationMode = osmConnection.GetPreference("translation-mode") | ||||
|         Locale.language.mapD( | ||||
|             (language) => { | ||||
|                 amendedPrefs.data["_language"] = language | ||||
|                 const trmode = translationMode.data | ||||
|                 if ((trmode === "true" || trmode === "mobile") && layout !== undefined) { | ||||
|                     const missing = layout.missingTranslations() | ||||
|                     const total = missing.total | ||||
| 
 | ||||
|                     const untranslated = missing.untranslated.get(language) ?? [] | ||||
|                     const hasMissingTheme = untranslated.some((k) => k.startsWith("themes:")) | ||||
|                     const missingLayers = Utils.Dedup( | ||||
|                         untranslated | ||||
|                             .filter((k) => k.startsWith("layers:")) | ||||
|                             .map((k) => k.slice("layers:".length).split(".")[0]) | ||||
|                     ) | ||||
| 
 | ||||
|                     const zenLinks: { link: string; id: string }[] = Utils.NoNull([ | ||||
|                         hasMissingTheme | ||||
|                             ? { | ||||
|                                   id: "theme:" + layout.id, | ||||
|                                   link: LinkToWeblate.hrefToWeblateZen( | ||||
|                                       language, | ||||
|                                       "themes", | ||||
|                                       layout.id | ||||
|                                   ), | ||||
|                               } | ||||
|                             : undefined, | ||||
|                         ...missingLayers.map((id) => ({ | ||||
|                             id: "layer:" + id, | ||||
|                             link: LinkToWeblate.hrefToWeblateZen(language, "layers", id), | ||||
|                         })), | ||||
|                     ]) | ||||
|                     const untranslated_count = untranslated.length | ||||
|                     amendedPrefs.data["_translation_total"] = "" + total | ||||
|                     amendedPrefs.data["_translation_translated_count"] = | ||||
|                         "" + (total - untranslated_count) | ||||
|                     amendedPrefs.data["_translation_percentage"] = | ||||
|                         "" + Math.floor((100 * (total - untranslated_count)) / total) | ||||
|                     console.log("Setting zenLinks", zenLinks) | ||||
|                     amendedPrefs.data["_translation_links"] = JSON.stringify(zenLinks) | ||||
|                 } | ||||
|                 amendedPrefs.ping() | ||||
|             }, | ||||
|             [translationMode] | ||||
|         ) | ||||
|         osmConnection.userDetails.addCallback((userDetails) => { | ||||
|             for (const k in userDetails) { | ||||
|                 amendedPrefs.data["_" + k] = "" + userDetails[k] | ||||
|             } | ||||
| 
 | ||||
|             for (const [name, code, _] of usersettingsConfig.calculatedTags) { | ||||
|                 try { | ||||
|                     let result = new Function("feat", "return " + code + ";")({ | ||||
|                         properties: amendedPrefs.data, | ||||
|                     }) | ||||
|                     if (result !== undefined && result !== "" && result !== null) { | ||||
|                         if (typeof result !== "string") { | ||||
|                             result = JSON.stringify(result) | ||||
|                         } | ||||
|                         amendedPrefs.data[name] = result | ||||
|                     } | ||||
|                 } catch (e) { | ||||
|                     console.error( | ||||
|                         "Calculating a tag for userprofile-settings failed for variable", | ||||
|                         name, | ||||
|                         e | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const simplifiedName = userDetails.name.toLowerCase().replace(/\s+/g, "") | ||||
|             const isTranslator = translators.contributors.find( | ||||
|                 (c: { contributor: string; commits: number }) => { | ||||
|                     const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") | ||||
|                     return replaced === simplifiedName | ||||
|                 } | ||||
|             ) | ||||
|             if (isTranslator) { | ||||
|                 amendedPrefs.data["_translation_contributions"] = "" + isTranslator.commits | ||||
|             } | ||||
|             const isCodeContributor = codeContributors.contributors.find( | ||||
|                 (c: { contributor: string; commits: number }) => { | ||||
|                     const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") | ||||
|                     return replaced === simplifiedName | ||||
|                 } | ||||
|             ) | ||||
|             if (isCodeContributor) { | ||||
|                 amendedPrefs.data["_code_contributions"] = "" + isCodeContributor.commits | ||||
|             } | ||||
|             amendedPrefs.ping() | ||||
|         }) | ||||
| 
 | ||||
|         amendedPrefs.addCallbackD((tags) => { | ||||
|             for (const key in tags) { | ||||
|                 if (key.startsWith("_")) { | ||||
|                     continue | ||||
|                 } | ||||
|                 this.osmConnection.GetPreference(key, undefined, { prefix: "" }).setData(tags[key]) | ||||
|             } | ||||
|         }) | ||||
| 
 | ||||
|         return amendedPrefs | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -123,7 +123,6 @@ export default class FilteredLayer { | |||
|         } else { | ||||
|             properties = fieldstate | ||||
|         } | ||||
|         console.log("Building tagsspec with properties", properties) | ||||
|         const missingKeys = option.fields | ||||
|             .map((f) => f.name) | ||||
|             .filter((key) => properties[key] === undefined) | ||||
|  | @ -182,7 +181,6 @@ export default class FilteredLayer { | |||
|                 // We calculate the fields
 | ||||
|                 const fieldProperties = FilteredLayer.stringToFieldProperties(<string>state.data) | ||||
|                 const asTags = FilteredLayer.fieldsToTags(filter.options[0], fieldProperties) | ||||
|                 console.log("Current field properties:", state.data, fieldProperties, asTags) | ||||
|                 if (asTags) { | ||||
|                     needed.push(asTags) | ||||
|                 } | ||||
|  |  | |||
|  | @ -21,6 +21,7 @@ export class MenuState { | |||
|     public readonly highlightedLayerInFilters: UIEventSource<string> = new UIEventSource<string>( | ||||
|         undefined | ||||
|     ) | ||||
|     public highlightedUserSetting: UIEventSource<string> = new UIEventSource<string>(undefined) | ||||
|     constructor() { | ||||
|         this.themeViewTabIndex = new UIEventSource(0) | ||||
|         this.themeViewTab = this.themeViewTabIndex.sync( | ||||
|  | @ -57,6 +58,12 @@ export class MenuState { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public openUsersettings(highlightTagRendering?: string) { | ||||
|         this.menuIsOpened.setData(true) | ||||
|         this.menuViewTab.setData("settings") | ||||
|         this.highlightedUserSetting.setData(highlightTagRendering) | ||||
|     } | ||||
| 
 | ||||
|     public closeAll() { | ||||
|         this.menuIsOpened.setData(false) | ||||
|         this.themeIsOpened.setData(false) | ||||
|  |  | |||
|  | @ -805,7 +805,13 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | |||
|             const param = special[arg.name] | ||||
|             if (param === undefined) { | ||||
|                 errors.push( | ||||
|                     `At ${context}: Obligated parameter '${arg.name}' in special rendering of type ${vis.funcName} not found.\n${arg.doc}` | ||||
|                     `At ${context}: Obligated parameter '${ | ||||
|                         arg.name | ||||
|                     }' in special rendering of type ${ | ||||
|                         vis.funcName | ||||
|                     } not found.\n    The full special rendering specification is: '${JSON.stringify( | ||||
|                         input | ||||
|                     )}'\n    ${arg.name}: ${arg.doc}` | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -101,7 +101,7 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|             ), | ||||
|             osmConfiguration: <"osm" | "osm-test">this.featureSwitches.featureSwitchApiURL.data, | ||||
|         }) | ||||
|         this.userRelatedState = new UserRelatedState(this.osmConnection, layout?.language) | ||||
|         this.userRelatedState = new UserRelatedState(this.osmConnection, layout?.language, layout) | ||||
|         this.selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element") | ||||
|         this.selectedLayer = new UIEventSource<LayerConfig>(undefined, "Selected layer") | ||||
|         this.geolocation = new GeoLocationHandler( | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
|   const offlineModes: Partial<Record<OsmServiceState, Translation>> = { | ||||
|     offline: t.loginFailedOfflineMode, | ||||
|     unreachable: t.loginFailedUnreachableMode, | ||||
|     unknown: t.loginFailedUnreachableMode, | ||||
|     readonly: t.loginFailedReadonlyMode | ||||
|   }; | ||||
|   const apiState = state.osmConnection.apiIsOnline; | ||||
|  |  | |||
|  | @ -7,15 +7,17 @@ | |||
|   import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"; | ||||
|   import { onDestroy } from "svelte"; | ||||
| 
 | ||||
|   export let selectedElement: Feature; | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let layer: LayerConfig; | ||||
|   export let selectedElement: Feature; | ||||
|   export let tags: UIEventSource<Record<string, string>>; | ||||
|   export let highlightedRendering: UIEventSource<string> = undefined; | ||||
| 
 | ||||
| 
 | ||||
|   let _tags: Record<string, string>; | ||||
|   onDestroy(tags.addCallbackAndRun(tags => { | ||||
|     _tags = tags; | ||||
|   })); | ||||
|   export let state: SpecialVisualizationState; | ||||
| </script> | ||||
| 
 | ||||
| <div> | ||||
|  | @ -40,7 +42,7 @@ | |||
|     {#each layer.tagRenderings as config (config.id)} | ||||
|       {#if config.condition === undefined || config.condition.matchesProperties(_tags)} | ||||
|         {#if config.IsKnown(_tags)} | ||||
|           <TagRenderingEditable {tags} {config} {state} {selectedElement} {layer}></TagRenderingEditable> | ||||
|           <TagRenderingEditable {tags} {config} {state} {selectedElement} {layer} {highlightedRendering}></TagRenderingEditable> | ||||
|         {/if} | ||||
|       {/if} | ||||
|     {/each} | ||||
|  |  | |||
|  | @ -1,4 +1,3 @@ | |||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen" | ||||
| import Translations from "../i18n/Translations" | ||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import Combine from "../Base/Combine" | ||||
|  | @ -8,25 +7,13 @@ import { VariableUiElement } from "../Base/VariableUIElement" | |||
| import Img from "../Base/Img" | ||||
| import { FixedUiElement } from "../Base/FixedUiElement" | ||||
| import Link from "../Base/Link" | ||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import Loc from "../../Models/Loc" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Showdown from "showdown" | ||||
| import LanguagePicker from "../LanguagePicker" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import Constants from "../../Models/Constants" | ||||
| import EditableTagRendering from "../Popup/EditableTagRendering" | ||||
| import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" | ||||
| import { SaveButton } from "../Popup/SaveButton" | ||||
| import { TagUtils } from "../../Logic/Tags/TagUtils" | ||||
| import usersettings from "../../assets/generated/layers/usersettings.json" | ||||
| import { LoginToggle } from "../Popup/LoginButton" | ||||
| import LayerConfig from "../../Models/ThemeConfig/LayerConfig" | ||||
| import translators from "../../assets/translators.json" | ||||
| import codeContributors from "../../assets/contributors.json" | ||||
| import Locale from "../i18n/Locale" | ||||
| import { Utils } from "../../Utils" | ||||
| import LinkToWeblate from "../Base/LinkToWeblate" | ||||
| 
 | ||||
| export class ImportViewerLinks extends VariableUiElement { | ||||
|     constructor(osmConnection: OsmConnection) { | ||||
|  | @ -48,51 +35,6 @@ export class ImportViewerLinks extends VariableUiElement { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| class SingleUserSettingsPanel extends EditableTagRendering { | ||||
|     constructor( | ||||
|         config: TagRenderingConfig, | ||||
|         osmConnection: OsmConnection, | ||||
|         amendedPrefs: UIEventSource<any>, | ||||
|         userInfoFocusedQuestion?: UIEventSource<string> | ||||
|     ) { | ||||
|         const editMode = new UIEventSource(false) | ||||
|         // Isolate the preferences. They'll be updated explicitely later on anyway
 | ||||
|         super( | ||||
|             amendedPrefs, | ||||
|             config, | ||||
|             [], | ||||
|             { osmConnection }, | ||||
|             { | ||||
|                 answerElementClasses: "p-2", | ||||
|                 editMode, | ||||
|                 createSaveButton: (store) => | ||||
|                     new SaveButton(amendedPrefs, osmConnection).onClick(() => { | ||||
|                         const selection = TagUtils.FlattenMultiAnswer( | ||||
|                             TagUtils.FlattenAnd(store.data, amendedPrefs.data) | ||||
|                         ).asChange(amendedPrefs.data) | ||||
|                         for (const kv of selection) { | ||||
|                             if (kv.k.startsWith("_")) { | ||||
|                                 continue | ||||
|                             } | ||||
|                             osmConnection.GetPreference(kv.k, "", { prefix: "" }).setData(kv.v) | ||||
|                         } | ||||
| 
 | ||||
|                         editMode.setData(false) | ||||
|                     }), | ||||
|             } | ||||
|         ) | ||||
|         const self = this | ||||
|         this.SetClass("rounded-xl") | ||||
|         userInfoFocusedQuestion.addCallbackAndRun((selected) => { | ||||
|             if (config.id !== selected) { | ||||
|                 self.RemoveClass("glowing-shadow") | ||||
|             } else { | ||||
|                 self.SetClass("glowing-shadow") | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| class UserInformationMainPanel extends VariableUiElement { | ||||
|     private readonly settings: UIEventSource<Record<string, BaseUIElement>> | ||||
|     private readonly userInfoFocusedQuestion?: UIEventSource<string> | ||||
|  | @ -104,210 +46,9 @@ class UserInformationMainPanel extends VariableUiElement { | |||
|         isOpened: UIEventSource<boolean>, | ||||
|         userInfoFocusedQuestion?: UIEventSource<string> | ||||
|     ) { | ||||
|         const t = Translations.t.userinfo | ||||
|         const imgSize = "h-6 w-6" | ||||
|         const ud = osmConnection.userDetails | ||||
|         const settings = new UIEventSource<Record<string, BaseUIElement>>({}) | ||||
|         const usersettingsConfig = new LayerConfig(usersettings, "userinformationpanel") | ||||
| 
 | ||||
|         const amendedPrefs = new UIEventSource<any>({ _theme: layout?.id }) | ||||
|         osmConnection.preferencesHandler.preferences.addCallback((newPrefs) => { | ||||
|             for (const k in newPrefs) { | ||||
|                 amendedPrefs.data[k] = newPrefs[k] | ||||
|             } | ||||
|             amendedPrefs.ping() | ||||
|         }) | ||||
|         const translationMode = osmConnection.GetPreference("translation-mode") | ||||
|         Locale.language.mapD( | ||||
|             (language) => { | ||||
|                 amendedPrefs.data["_language"] = language | ||||
|                 const trmode = translationMode.data | ||||
|                 if (trmode === "true" || trmode === "mobile") { | ||||
|                     const missing = layout.missingTranslations() | ||||
|                     const total = missing.total | ||||
| 
 | ||||
|                     const untranslated = missing.untranslated.get(language) ?? [] | ||||
|                     const hasMissingTheme = untranslated.some((k) => k.startsWith("themes:")) | ||||
|                     const missingLayers = Utils.Dedup( | ||||
|                         untranslated | ||||
|                             .filter((k) => k.startsWith("layers:")) | ||||
|                             .map((k) => k.slice("layers:".length).split(".")[0]) | ||||
|                     ) | ||||
| 
 | ||||
|                     const zenLinks: { link: string; id: string }[] = Utils.NoNull([ | ||||
|                         hasMissingTheme | ||||
|                             ? { | ||||
|                                   id: "theme:" + layout.id, | ||||
|                                   link: LinkToWeblate.hrefToWeblateZen( | ||||
|                                       language, | ||||
|                                       "themes", | ||||
|                                       layout.id | ||||
|                                   ), | ||||
|                               } | ||||
|                             : undefined, | ||||
|                         ...missingLayers.map((id) => ({ | ||||
|                             id: "layer:" + id, | ||||
|                             link: LinkToWeblate.hrefToWeblateZen(language, "layers", id), | ||||
|                         })), | ||||
|                     ]) | ||||
|                     const untranslated_count = untranslated.length | ||||
|                     amendedPrefs.data["_translation_total"] = "" + total | ||||
|                     amendedPrefs.data["_translation_translated_count"] = | ||||
|                         "" + (total - untranslated_count) | ||||
|                     amendedPrefs.data["_translation_percentage"] = | ||||
|                         "" + Math.floor((100 * (total - untranslated_count)) / total) | ||||
|                     console.log("Setting zenLinks", zenLinks) | ||||
|                     amendedPrefs.data["_translation_links"] = JSON.stringify(zenLinks) | ||||
|                 } | ||||
|                 amendedPrefs.ping() | ||||
|             }, | ||||
|             [translationMode] | ||||
|         ) | ||||
|         osmConnection.userDetails.addCallback((userDetails) => { | ||||
|             for (const k in userDetails) { | ||||
|                 amendedPrefs.data["_" + k] = "" + userDetails[k] | ||||
|             } | ||||
| 
 | ||||
|             for (const [name, code, _] of usersettingsConfig.calculatedTags) { | ||||
|                 try { | ||||
|                     let result = new Function("feat", "return " + code + ";")({ | ||||
|                         properties: amendedPrefs.data, | ||||
|                     }) | ||||
|                     if (result !== undefined && result !== "" && result !== null) { | ||||
|                         if (typeof result !== "string") { | ||||
|                             result = JSON.stringify(result) | ||||
|                         } | ||||
|                         amendedPrefs.data[name] = result | ||||
|                     } | ||||
|                 } catch (e) { | ||||
|                     console.error( | ||||
|                         "Calculating a tag for userprofile-settings failed for variable", | ||||
|                         name, | ||||
|                         e | ||||
|                     ) | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             const simplifiedName = userDetails.name.toLowerCase().replace(/\s+/g, "") | ||||
|             const isTranslator = translators.contributors.find( | ||||
|                 (c: { contributor: string; commits: number }) => { | ||||
|                     const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") | ||||
|                     return replaced === simplifiedName | ||||
|                 } | ||||
|             ) | ||||
|             if (isTranslator) { | ||||
|                 amendedPrefs.data["_translation_contributions"] = "" + isTranslator.commits | ||||
|             } | ||||
|             const isCodeContributor = codeContributors.contributors.find( | ||||
|                 (c: { contributor: string; commits: number }) => { | ||||
|                     const replaced = c.contributor.toLowerCase().replace(/\s+/g, "") | ||||
|                     return replaced === simplifiedName | ||||
|                 } | ||||
|             ) | ||||
|             if (isCodeContributor) { | ||||
|                 amendedPrefs.data["_code_contributions"] = "" + isCodeContributor.commits | ||||
|             } | ||||
|             amendedPrefs.ping() | ||||
|         }) | ||||
| 
 | ||||
|         super( | ||||
|             ud.map((ud) => { | ||||
|                 let img: BaseUIElement = Svg.person_ui().SetClass("block") | ||||
|                 if (ud.img !== undefined) { | ||||
|                     img = new Img(ud.img) | ||||
|                 } | ||||
|                 img.SetClass("rounded-full h-12 w-12 m-4") | ||||
| 
 | ||||
|                 let description: BaseUIElement = undefined | ||||
|                 const editLink = osmConnection.Backend() + "/profile/edit" | ||||
|                 if (ud.description) { | ||||
|                     const editButton = new Link( | ||||
|                         Svg.pencil_svg().SetClass("h-4 w-4"), | ||||
|                         editLink, | ||||
|                         true | ||||
|                     ).SetClass( | ||||
|                         "absolute block bg-subtle rounded-full p-2 bottom-2 right-2 w-min self-end" | ||||
|                     ) | ||||
| 
 | ||||
|                     const htmlString = new Showdown.Converter() | ||||
|                         .makeHtml(ud.description) | ||||
|                         .replace(/>/g, ">") | ||||
|                         .replace(/</g, "<") | ||||
|                     description = new Combine([ | ||||
|                         new FixedUiElement(htmlString).SetClass("link-underline"), | ||||
|                         editButton, | ||||
|                     ]).SetClass("relative w-full m-2") | ||||
|                 } else { | ||||
|                     description = new Combine([ | ||||
|                         t.noDescription, | ||||
|                         new SubtleButton(Svg.pencil_svg(), t.noDescriptionCallToAction, { | ||||
|                             imgSize, | ||||
|                             url: editLink, | ||||
|                             newTab: true, | ||||
|                         }), | ||||
|                     ]).SetClass("w-full m-2") | ||||
|                 } | ||||
| 
 | ||||
|                 let panToHome: BaseUIElement | ||||
|                 if (ud.home) { | ||||
|                     panToHome = new SubtleButton(Svg.home_svg(), t.moveToHome, { | ||||
|                         imgSize, | ||||
|                     }).onClick(() => { | ||||
|                         const home = ud?.home | ||||
|                         if (home === undefined) { | ||||
|                             return | ||||
|                         } | ||||
|                         locationControl.setData({ ...home, zoom: 16 }) | ||||
|                         isOpened.setData(false) | ||||
|                     }) | ||||
|                 } | ||||
| 
 | ||||
|                 const settingElements = [] | ||||
|                 for (const c of usersettingsConfig.tagRenderings) { | ||||
|                     const settingsPanel = new SingleUserSettingsPanel( | ||||
|                         c, | ||||
|                         osmConnection, | ||||
|                         amendedPrefs, | ||||
|                         userInfoFocusedQuestion | ||||
|                     ).SetClass("block my-4") | ||||
|                     settings.data[c.id] = settingsPanel | ||||
|                     settingElements.push(settingsPanel) | ||||
|                 } | ||||
|                 settings.ping() | ||||
| 
 | ||||
|                 return new Combine([ | ||||
|                     new Combine([img, description]).SetClass("flex border border-black rounded-md"), | ||||
|                     new LanguagePicker( | ||||
|                         layout.language, | ||||
|                         Translations.t.general.pickLanguage.Clone() | ||||
|                     ), | ||||
|                     ...settingElements, | ||||
|                     new SubtleButton( | ||||
|                         Svg.envelope_svg(), | ||||
|                         new Combine([ | ||||
|                             t.gotoInbox, | ||||
|                             ud.unreadMessages == 0 | ||||
|                                 ? undefined | ||||
|                                 : t.newMessages.SetClass("alert block"), | ||||
|                         ]), | ||||
|                         { imgSize, url: `${ud.backend}/messages/inbox`, newTab: true } | ||||
|                     ), | ||||
|                     new SubtleButton(Svg.gear_svg(), t.gotoSettings, { | ||||
|                         imgSize, | ||||
|                         url: `${ud.backend}/user/${encodeURIComponent(ud.name)}/account`, | ||||
|                         newTab: true, | ||||
|                     }), | ||||
|                     panToHome, | ||||
|                     new ImportViewerLinks(osmConnection), | ||||
|                     new SubtleButton(Svg.logout_svg(), Translations.t.general.logout, { | ||||
|                         imgSize, | ||||
|                     }).onClick(() => { | ||||
|                         osmConnection.LogOut() | ||||
|                     }), | ||||
|                 ]) | ||||
|             }) | ||||
|         ) | ||||
|         this.SetClass("flex flex-col") | ||||
|         super() | ||||
|         this.settings = settings | ||||
|         this.userInfoFocusedQuestion = userInfoFocusedQuestion | ||||
|         const self = this | ||||
|  | @ -325,50 +66,3 @@ class UserInformationMainPanel extends VariableUiElement { | |||
|         this.settings.data[focusedId]?.ScrollIntoView() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export default class UserInformationPanel extends ScrollableFullScreen { | ||||
|     private readonly userPanel: UserInformationMainPanel | ||||
| 
 | ||||
|     constructor( | ||||
|         state: { | ||||
|             readonly layoutToUse: LayoutConfig | ||||
|             readonly osmConnection: OsmConnection | ||||
|             readonly locationControl: UIEventSource<Loc> | ||||
|             readonly featureSwitchUserbadge: Store<boolean> | ||||
|         }, | ||||
|         options?: { | ||||
|             isOpened?: UIEventSource<boolean> | ||||
|             userInfoFocusedQuestion?: UIEventSource<string> | ||||
|         } | ||||
|     ) { | ||||
|         const isOpened = options?.isOpened ?? new UIEventSource<boolean>(false) | ||||
|         const userPanel = new UserInformationMainPanel( | ||||
|             state.osmConnection, | ||||
|             state.locationControl, | ||||
|             state.layoutToUse, | ||||
|             isOpened, | ||||
|             options?.userInfoFocusedQuestion | ||||
|         ) | ||||
|         super( | ||||
|             () => { | ||||
|                 return new VariableUiElement( | ||||
|                     state.osmConnection.userDetails.map((ud) => { | ||||
|                         if (ud.loggedIn === false) { | ||||
|                             return Translations.t.userinfo.titleNotLoggedIn | ||||
|                         } | ||||
|                         return Translations.t.userinfo.welcome.Subs(ud) | ||||
|                     }) | ||||
|                 ) | ||||
|             }, | ||||
|             () => new LoginToggle(userPanel, Translations.t.general.getStartedLogin, state), | ||||
|             "userinfo", | ||||
|             isOpened | ||||
|         ) | ||||
|         this.userPanel = userPanel | ||||
|     } | ||||
| 
 | ||||
|     Activate() { | ||||
|         super.Activate() | ||||
|         this.userPanel?.focusOnSelectedQuestion() | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										48
									
								
								UI/BigComponents/UserProfile.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								UI/BigComponents/UserProfile.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| <script lang="ts"> | ||||
|   import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection"; | ||||
|   import { UIEventSource } from "../../Logic/UIEventSource"; | ||||
|   import { PencilAltIcon, UserCircleIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { onDestroy } from "svelte"; | ||||
|   import Showdown from "showdown"; | ||||
|   import FromHtml from "../Base/FromHtml.svelte"; | ||||
|   import Tr from "../Base/Tr.svelte"; | ||||
|   import Translations from "../i18n/Translations.js"; | ||||
| 
 | ||||
|   /** | ||||
|    * This panel shows information about the logged-in user, showing account name, profile pick, description and an edit-button | ||||
|    */ | ||||
|   export let osmConnection: OsmConnection; | ||||
|   let userdetails: UIEventSource<UserDetails> = osmConnection.userDetails; | ||||
|   let description: string; | ||||
|   onDestroy(userdetails.addCallbackAndRunD(userdetails => { | ||||
|     description = new Showdown.Converter() | ||||
|       .makeHtml(userdetails.description) | ||||
|       ?.replace(/>/g, ">") | ||||
|       ?.replace(/</g, "<"); | ||||
| 
 | ||||
|   })); | ||||
| </script> | ||||
| 
 | ||||
| <div class="flex border border-gray-300 border-dashed m-1 p-1 rounded-md link-underline"> | ||||
|   {#if $userdetails.img} | ||||
|     <img src={$userdetails.img} class="rounded-full w-12 h-12 m-4"> | ||||
|   {:else} | ||||
|     <UserCircleIcon class="w-12 h-12" /> | ||||
|   {/if} | ||||
|   <div class="flex flex-col relative"> | ||||
|     <h3>{$userdetails.name}</h3> | ||||
|     {#if description} | ||||
|       <FromHtml src={description} /> | ||||
|       <a href={osmConnection.Backend() + "/profile/edit"} target="_blank"> | ||||
|         <PencilAltIcon class="p-2 w-6 h-6 subtle-background rounded-full absolute right-1 bottom-1" /> | ||||
|       </a> | ||||
|     {:else} | ||||
|       <Tr t={Translations.t. userinfo.noDescription} /> | ||||
|       <a href={osmConnection.Backend() + "/profile/edit"} target="_blank" class="flex subtle-background items-center"> | ||||
|         <PencilAltIcon slot="image" class="p-2 w-8 h-8" /> | ||||
|         <Tr slot="message" t={Translations.t.userinfo.noDescriptionCallToAction} /> | ||||
|       </a> | ||||
|     {/if} | ||||
| 
 | ||||
|   </div> | ||||
| </div> | ||||
|  | @ -158,30 +158,6 @@ export default class DefaultGUI { | |||
| 
 | ||||
|         const self = this | ||||
| 
 | ||||
|         const userInfoMapControl = Toggle.If(state.featureSwitchUserbadge, () => { | ||||
|             new UserInformationPanel(state, { | ||||
|                 isOpened: guiState.userInfoIsOpened, | ||||
|                 userInfoFocusedQuestion: guiState.userInfoFocusedQuestion, | ||||
|             }) | ||||
| 
 | ||||
|             const mapControl = new MapControlButton( | ||||
|                 new VariableUiElement( | ||||
|                     state.osmConnection.userDetails.map((ud) => { | ||||
|                         if (ud?.img === undefined) { | ||||
|                             return Svg.person_ui().SetClass("mt-1 block") | ||||
|                         } | ||||
|                         return new Img(ud?.img) | ||||
|                     }) | ||||
|                 ).SetClass("block rounded-full overflow-hidden"), | ||||
|                 { | ||||
|                     dontStyle: true, | ||||
|                 } | ||||
|             ).onClick(() => { | ||||
|                 self.guiState.userInfoIsOpened.setData(true) | ||||
|             }) | ||||
| 
 | ||||
|             return new LoginToggle(mapControl, Translations.t.general.loginWithOpenStreetMap, state) | ||||
|         }) | ||||
|         const extraLink = Toggle.If( | ||||
|             state.featureSwitchExtraLinkEnabled, | ||||
|             () => new ExtraLinkButton(state, state.layoutToUse.extraLink) | ||||
|  | @ -200,7 +176,7 @@ export default class DefaultGUI { | |||
|         const copyright = new MapControlButton(Svg.copyright_svg()).onClick(() => | ||||
|             guiState.copyrightViewIsOpened.setData(true) | ||||
|         ) | ||||
|         new Combine([welcomeMessageMapControl, userInfoMapControl, copyright, extraLink]) | ||||
|         new Combine([welcomeMessageMapControl, copyright, extraLink]) | ||||
|             .SetClass("flex flex-col") | ||||
|             .AttachTo("top-left") | ||||
| 
 | ||||
|  |  | |||
|  | @ -15,32 +15,51 @@ | |||
|   export let tags: UIEventSource<Record<string, string>>; | ||||
|   export let selectedElement: Feature; | ||||
|   export let state: SpecialVisualizationState; | ||||
|   export let layer: LayerConfig | ||||
|   export let layer: LayerConfig; | ||||
| 
 | ||||
|   export let showQuestionIfUnknown : boolean= false | ||||
|   let editMode = false | ||||
|   export let highlightedRendering: UIEventSource<string> = undefined; | ||||
|   export let showQuestionIfUnknown: boolean = false; | ||||
|   let editMode = false; | ||||
|   onDestroy(tags.addCallbackAndRunD(tags => { | ||||
|     editMode = showQuestionIfUnknown && !config.IsKnown(tags) | ||||
|     editMode = showQuestionIfUnknown && !config.IsKnown(tags); | ||||
| 
 | ||||
|   })); | ||||
| 
 | ||||
|   let htmlElem: HTMLElement; | ||||
|   if (highlightedRendering) { | ||||
|     onDestroy(highlightedRendering.addCallbackAndRun(highlighted => { | ||||
|       console.log("Highlighted rendering is", highlighted) | ||||
|       if(htmlElem === undefined){ | ||||
|         return | ||||
|       } | ||||
|       if (config.id === highlighted) { | ||||
|         htmlElem.classList.add("glowing-shadow"); | ||||
|       } else { | ||||
|         htmlElem.classList.remove("glowing-shadow"); | ||||
|       } | ||||
|     })); | ||||
|   } | ||||
| 
 | ||||
|   })) | ||||
| 
 | ||||
| </script> | ||||
| 
 | ||||
| {#if config.question} | ||||
| <div bind:this={htmlElem}> | ||||
|   {#if config.question} | ||||
|     {#if editMode} | ||||
|     <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer} > | ||||
|       <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}> | ||||
|         <button slot="cancel" on:click={() => {editMode = false}}> | ||||
|         <Tr t={Translations.t.general.cancel}/> | ||||
|           <Tr t={Translations.t.general.cancel} /> | ||||
|         </button> | ||||
|       </TagRenderingQuestion> | ||||
|     {:else} | ||||
|       <div class="flex justify-between"> | ||||
|         <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||
|       <button on:click={() => {editMode = true}} class="w-6 h-6 rounded-full subtle-background p-1"> | ||||
|         <button on:click={() => {editMode = true}} class="shrink-0 w-6 h-6 rounded-full subtle-background p-1"> | ||||
|           <PencilAltIcon></PencilAltIcon> | ||||
|         </button> | ||||
|       </div> | ||||
|     {/if} | ||||
| {:else } | ||||
|   {:else } | ||||
|     <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||
| {/if} | ||||
|   {/if} | ||||
| </div> | ||||
|  |  | |||
|  | @ -10,7 +10,6 @@ | |||
|   import { TagsFilter } from "../../../Logic/Tags/TagsFilter"; | ||||
|   import FreeformInput from "./FreeformInput.svelte"; | ||||
|   import Translations from "../../i18n/Translations.js"; | ||||
|   import FromHtml from "../../Base/FromHtml.svelte"; | ||||
|   import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"; | ||||
|   import { createEventDispatcher } from "svelte"; | ||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; | ||||
|  | @ -63,6 +62,24 @@ | |||
|   }>(); | ||||
| 
 | ||||
|   function onSave() { | ||||
| 
 | ||||
|     if (layer.source === null) { | ||||
|       /** | ||||
|        * This is a special, priviliged layer. | ||||
|        * We simply apply the tags onto the records | ||||
|        */ | ||||
|       const kv = selectedTags.asChange(tags.data); | ||||
|       for (const { k, v } of kv) { | ||||
|         if (v === undefined) { | ||||
|           delete tags.data[k]; | ||||
|         } else { | ||||
|           tags.data[k] = v; | ||||
|         } | ||||
|       } | ||||
|       tags.ping(); | ||||
|       return; | ||||
|     } | ||||
| 
 | ||||
|     dispatch("saved", { config, applied: selectedTags }); | ||||
|     const change = new ChangeTagAction( | ||||
|       tags.data.id, | ||||
|  | @ -76,6 +93,7 @@ | |||
|     freeformInput.setData(undefined); | ||||
|     selectedMapping = 0; | ||||
|     selectedTags = undefined; | ||||
| 
 | ||||
|     change.CreateChangeDescriptions().then(changes => | ||||
|       state.changes.applyChanges(changes) | ||||
|     ).catch(console.error); | ||||
|  | @ -93,12 +111,14 @@ | |||
|         </span> | ||||
|         <span class="alert">{config.id}</span> | ||||
|       </div> | ||||
|       <SpecialTranslation slot="else" t={config.question} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation> | ||||
|       <SpecialTranslation slot="else" t={config.question} {tags} {state} {layer} | ||||
|                           feature={selectedElement}></SpecialTranslation> | ||||
|     </If> | ||||
| 
 | ||||
|     {#if config.questionhint} | ||||
|       <div class="subtle"> | ||||
|         <SpecialTranslation t={config.questionhint} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation> | ||||
|         <SpecialTranslation t={config.questionhint} {tags} {state} {layer} | ||||
|                             feature={selectedElement}></SpecialTranslation> | ||||
|       </div> | ||||
|     {/if} | ||||
| 
 | ||||
|  |  | |||
|  | @ -56,16 +56,27 @@ import Maproulette from "../Logic/Maproulette" | |||
| import SvelteUIElement from "./Base/SvelteUIElement" | ||||
| import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | ||||
| import QuestionViz from "./Popup/QuestionViz" | ||||
| import SimpleAddUI from "./BigComponents/SimpleAddUI" | ||||
| import { Feature } from "geojson" | ||||
| import { GeoOperations } from "../Logic/GeoOperations" | ||||
| import CreateNewNote from "./Popup/CreateNewNote.svelte" | ||||
| import { svelte } from "@sveltejs/vite-plugin-svelte" | ||||
| import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.svelte" | ||||
| import UserProfile from "./BigComponents/UserProfile.svelte" | ||||
| import LanguagePicker from "./LanguagePicker" | ||||
| import Link from "./Base/Link" | ||||
| 
 | ||||
| export default class SpecialVisualizations { | ||||
|     public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList() | ||||
| 
 | ||||
|     static undoEncoding(str: string) { | ||||
|         return str | ||||
|             .trim() | ||||
|             .replace(/&LPARENS/g, "(") | ||||
|             .replace(/&RPARENS/g, ")") | ||||
|             .replace(/&LBRACE/g, "{") | ||||
|             .replace(/&RBRACE/g, "}") | ||||
|             .replace(/&COMMA/g, ",") | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * | ||||
|      * For a given string, returns a specification what parts are fixed and what parts are special renderings. | ||||
|  | @ -115,15 +126,7 @@ export default class SpecialVisualizations { | |||
|                 ) | ||||
|                 const args = knownSpecial.args.map((arg) => arg.defaultValue ?? "") | ||||
|                 if (argument.length > 0) { | ||||
|                     const realArgs = argument.split(",").map((str) => | ||||
|                         str | ||||
|                             .trim() | ||||
|                             .replace(/&LPARENS/g, "(") | ||||
|                             .replace(/&RPARENS/g, ")") | ||||
|                             .replace(/&LBRACE/g, "{") | ||||
|                             .replace(/&RBRACE/g, "}") | ||||
|                             .replace(/&COMMA/g, ",") | ||||
|                     ) | ||||
|                     const realArgs = argument.split(",").map((str) => this.undoEncoding(str)) | ||||
|                     for (let i = 0; i < realArgs.length; i++) { | ||||
|                         if (args.length <= i) { | ||||
|                             args.push(realArgs[i]) | ||||
|  | @ -273,6 +276,39 @@ export default class SpecialVisualizations { | |||
|                     }) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "user_profile", | ||||
|                 args: [], | ||||
|                 docs: "A component showing information about the currently logged in user (username, profile description, profile picture + link to edit them). Mostly meant to be used in the 'user-settings'", | ||||
|                 constr(state: SpecialVisualizationState): BaseUIElement { | ||||
|                     return new SvelteUIElement(UserProfile, { | ||||
|                         osmConnection: state.osmConnection, | ||||
|                     }) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "language_picker", | ||||
|                 args: [], | ||||
|                 docs: "A component to set the language of the user interface", | ||||
|                 constr(state: SpecialVisualizationState): BaseUIElement { | ||||
|                     return new LanguagePicker( | ||||
|                         state.layout.language, | ||||
|                         Translations.t.general.pickLanguage.Clone() | ||||
|                     ) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "logout", | ||||
|                 args: [], | ||||
|                 docs: "Shows a button where the user can log out", | ||||
|                 constr(state: SpecialVisualizationState): BaseUIElement { | ||||
|                     return new SubtleButton(Svg.logout_ui(), Translations.t.general.logout, { | ||||
|                         imgSize: "w-6 h-6", | ||||
|                     }).onClick(() => { | ||||
|                         state.osmConnection.LogOut() | ||||
|                     }) | ||||
|                 }, | ||||
|             }, | ||||
|             new HistogramViz(), | ||||
|             new StealViz(), | ||||
|             new MinimapViz(), | ||||
|  | @ -717,7 +753,7 @@ export default class SpecialVisualizations { | |||
|                 docs: "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'", | ||||
|                 example: | ||||
|                     "`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.", | ||||
|                 constr: (state, tagsSource, args, feature) => | ||||
|                 constr: (state, tagsSource) => | ||||
|                     new VariableUiElement( | ||||
|                         tagsSource.map((tags) => { | ||||
|                             const layer = state.layout.getMatchingLayer(tags) | ||||
|  | @ -933,6 +969,40 @@ export default class SpecialVisualizations { | |||
|                     ) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "link", | ||||
|                 docs: "Construct a link. By using the 'special' visualisation notation, translation should be easier", | ||||
|                 args: [ | ||||
|                     { | ||||
|                         name: "text", | ||||
|                         doc: "Text to be shown", | ||||
|                         required: true, | ||||
|                     }, | ||||
|                     { | ||||
|                         name: "href", | ||||
|                         doc: "The URL to link to", | ||||
|                         required: true, | ||||
|                     }, | ||||
|                 ], | ||||
|                 constr( | ||||
|                     state: SpecialVisualizationState, | ||||
|                     tagSource: UIEventSource<Record<string, string>>, | ||||
|                     args: string[], | ||||
|                     feature: Feature | ||||
|                 ): BaseUIElement { | ||||
|                     const [text, href] = args | ||||
|                     return new VariableUiElement( | ||||
|                         tagSource.map( | ||||
|                             (tags) => | ||||
|                                 new Link( | ||||
|                                     Utils.SubstituteKeys(text, tags), | ||||
|                                     Utils.SubstituteKeys(href, tags), | ||||
|                                     true | ||||
|                                 ) | ||||
|                         ) | ||||
|                     ) | ||||
|                 }, | ||||
|             }, | ||||
|             { | ||||
|                 funcName: "multi", | ||||
|                 docs: "Given an embedded tagRendering (read only) and a key, will read the keyname as a JSON-list. Every element of this list will be considered as tags and rendered with the tagRendering", | ||||
|  |  | |||
|  | @ -87,7 +87,7 @@ export class SubstitutedTranslation extends VariableUiElement { | |||
|                             tagsSource.data.id | ||||
|                         ) | ||||
|                         return viz.func | ||||
|                             .constr(state, tagsSource, proto.args, feature, undefined) | ||||
|                             .constr(state, tagsSource, proto.args.map(t => SpecialVisualizations.undoEncoding(t)), feature, undefined) | ||||
|                             ?.SetStyle(proto.style) | ||||
|                     } catch (e) { | ||||
|                         console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e) | ||||
|  |  | |||
|  | @ -2,13 +2,12 @@ | |||
|   import { Store, UIEventSource } from "../Logic/UIEventSource"; | ||||
|   import { Map as MlMap } from "maplibre-gl"; | ||||
|   import MaplibreMap from "./Map/MaplibreMap.svelte"; | ||||
|   import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; | ||||
|   import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; | ||||
|   import MapControlButton from "./Base/MapControlButton.svelte"; | ||||
|   import ToSvelte from "./Base/ToSvelte.svelte"; | ||||
|   import Svg from "../Svg"; | ||||
|   import If from "./Base/If.svelte"; | ||||
|   import { GeolocationControl } from "./BigComponents/GeolocationControl.js"; | ||||
|   import { GeolocationControl } from "./BigComponents/GeolocationControl"; | ||||
|   import type { Feature } from "geojson"; | ||||
|   import SelectedElementView from "./BigComponents/SelectedElementView.svelte"; | ||||
|   import LayerConfig from "../Models/ThemeConfig/LayerConfig"; | ||||
|  | @ -17,19 +16,21 @@ | |||
|   import ThemeViewState from "../Models/ThemeViewState"; | ||||
|   import type { MapProperties } from "../Models/MapProperties"; | ||||
|   import Geosearch from "./BigComponents/Geosearch.svelte"; | ||||
|   import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"; | ||||
|   import Translations from "./i18n/Translations"; | ||||
|   import { CogIcon, MenuIcon, EyeIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import { CogIcon, EyeIcon, MenuIcon } from "@rgossiaux/svelte-heroicons/solid"; | ||||
|   import Tr from "./Base/Tr.svelte"; | ||||
|   import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"; | ||||
|   import FloatOver from "./Base/FloatOver.svelte"; | ||||
|   import PrivacyPolicy from "./BigComponents/PrivacyPolicy.js"; | ||||
|   import { Utils } from "../Utils.js"; | ||||
|   import PrivacyPolicy from "./BigComponents/PrivacyPolicy"; | ||||
|   import { Utils } from "../Utils"; | ||||
|   import Constants from "../Models/Constants"; | ||||
|   import TabbedGroup from "./Base/TabbedGroup.svelte"; | ||||
|   import UserRelatedState from "../Logic/State/UserRelatedState"; | ||||
|   import LoginToggle from "./Base/LoginToggle.svelte"; | ||||
|   import LoginButton from "./Base/LoginButton.svelte"; | ||||
| 
 | ||||
|   export let layout: LayoutConfig; | ||||
|   const state = new ThemeViewState(layout); | ||||
|   export let state: ThemeViewState; | ||||
|   let layout = state.layout; | ||||
| 
 | ||||
|   let selectedElementTags: Store<UIEventSource<Record<string, string>>> = | ||||
|     state.selectedElement.mapD((f) => { | ||||
|  | @ -43,6 +44,7 @@ | |||
|   let mapproperties: MapProperties = state.mapProperties; | ||||
|   let featureSwitches: FeatureSwitchState = state.featureSwitches; | ||||
|   let availableLayers = state.availableLayers; | ||||
|   let userdetails = state.osmConnection.userDetails; | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
|  | @ -97,7 +99,7 @@ | |||
| {#if $selectedElement !== undefined && $selectedLayer !== undefined} | ||||
|   <FloatOver on:close={() => {selectedElement.setData(undefined)}}> | ||||
|     <SelectedElementView layer={$selectedLayer} selectedElement={$selectedElement} | ||||
|                          tags={$selectedElementTags} state={state}></SelectedElementView> | ||||
|                          tags={$selectedElementTags} state={state} /> | ||||
|   </FloatOver> | ||||
| 
 | ||||
| {/if} | ||||
|  | @ -131,16 +133,17 @@ | |||
| 
 | ||||
|       </div> | ||||
| 
 | ||||
|       <div slot="title1" class="flex"> | ||||
|       <div class="flex" slot="title1"> | ||||
|         <If condition={state.featureSwitches.featureSwitchFilter}> | ||||
|           <img class="w-4 h-4" src="./assets/svg/filter.svg"> | ||||
|           <Tr t={Translations.t.general.menu.filter} /> | ||||
|         </If> | ||||
|       </div> | ||||
| 
 | ||||
|       <div slot="content1" class="flex flex-col"> | ||||
|       <div class="flex flex-col" slot="content1"> | ||||
|         {#each layout.layers as layer} | ||||
|           <Filterview zoomlevel={state.mapProperties.zoom} filteredLayer={state.layerState.filteredLayers.get(layer.id)} highlightedLayer={state.guistate.highlightedLayerInFilters}></Filterview> | ||||
|           <Filterview zoomlevel={state.mapProperties.zoom} filteredLayer={state.layerState.filteredLayers.get(layer.id)} | ||||
|                       highlightedLayer={state.guistate.highlightedLayerInFilters}></Filterview> | ||||
|         {/each} | ||||
|         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> | ||||
|           <RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker> | ||||
|  | @ -154,50 +157,56 @@ | |||
| <If condition={state.guistate.menuIsOpened}> | ||||
|   <!-- Menu page --> | ||||
|   <FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}> | ||||
|     <TabGroup on:change={(e) => {state.guistate.menuViewTabIndex.setData(e.detail)} }> | ||||
|       <TabList> | ||||
|         <Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}> | ||||
|           <div class="flex"> | ||||
|     <TabbedGroup tab={state.guistate.menuViewTabIndex}> | ||||
|       <div class="flex" slot="title0"> | ||||
|         <Tr t={Translations.t.general.aboutMapcompleteTitle}></Tr> | ||||
|       </div> | ||||
|         </Tab> | ||||
|         <Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}> | ||||
|           <div class="flex"> | ||||
|             <CogIcon class="w-6 h-6"/> | ||||
|             Settings | ||||
|           </div> | ||||
|         </Tab> | ||||
|         <Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}> | ||||
|           <div class="flex"> | ||||
|             <img class="w-6" src="./assets/svg/community.svg"> | ||||
|             Get in touch with others | ||||
|           </div> | ||||
|         </Tab> | ||||
|         <Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}> | ||||
|           <div class="flex"> | ||||
|             <EyeIcon class="w-6"/> | ||||
|             <Tr t={Translations.t.privacy.title}></Tr> | ||||
|           </div> | ||||
|         </Tab> | ||||
|       </TabList> | ||||
|       <TabPanels > | ||||
|         <TabPanel class="flex flex-col"> | ||||
| 
 | ||||
|       <div class="flex flex-col" slot="content0"> | ||||
|         <Tr t={Translations.t.general.aboutMapcomplete.Subs({ | ||||
|                     osmcha_link: Utils.OsmChaLinkFor(7), | ||||
|                 })}></Tr> | ||||
| 
 | ||||
|         {Constants.vNumber} | ||||
|         </TabPanel> | ||||
|         <TabPanel>User settings</TabPanel> | ||||
|         <TabPanel> | ||||
|           <CommunityIndexView location={state.mapProperties.location}></CommunityIndexView> | ||||
|       </div> | ||||
| 
 | ||||
|         </TabPanel> | ||||
|         <TabPanel> | ||||
|           <ToSvelte construct={() => new PrivacyPolicy()}></ToSvelte> | ||||
|         </TabPanel> | ||||
|       </TabPanels> | ||||
|     </TabGroup> | ||||
|       <If condition={state.osmConnection.isLoggedIn} slot="title1"> | ||||
|         <div class="flex"> | ||||
|           <CogIcon class="w-6 h-6" /> | ||||
|           <Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} /> | ||||
|         </div> | ||||
|       </If> | ||||
| 
 | ||||
|       <div slot="content1"> | ||||
|         <!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it --> | ||||
|         <LoginToggle {state}> | ||||
|           <div slot="not-logged-in"> | ||||
|             <Tr class="alert" t={Translations.t.userinfo.notLoggedIn}/> | ||||
|             <LoginButton osmConnection={state.osmConnection}></LoginButton> | ||||
|           </div> | ||||
|           <SelectedElementView | ||||
|             layer={UserRelatedState.usersettingsConfig} | ||||
|             selectedElement={({ | ||||
|         type:"Feature",properties: {}, geometry: {type:"Point", coordinates: [0,0]} | ||||
|         })} {state} | ||||
|             tags={state.userRelatedState.preferencesAsTags}  | ||||
|           highlightedRendering={state.guistate.highlightedUserSetting} | ||||
|           /> | ||||
|         </LoginToggle> | ||||
|       </div> | ||||
| 
 | ||||
|       <div class="flex" slot="title2"> | ||||
|         <img class="w-6" src="./assets/svg/community.svg"> | ||||
|         Get in touch with others | ||||
|       </div> | ||||
|       <CommunityIndexView location={state.mapProperties.location} slot="content2"></CommunityIndexView> | ||||
| 
 | ||||
|       <div class="flex" slot="title3"> | ||||
|         <EyeIcon class="w-6" /> | ||||
|         <Tr t={Translations.t.privacy.title}></Tr> | ||||
|       </div> | ||||
|       <ToSvelte construct={() => new PrivacyPolicy()} slot="content3"></ToSvelte> | ||||
|     </TabbedGroup> | ||||
|   </FloatOver> | ||||
| </If> | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,12 @@ | |||
|     "de": "Eine spezielle Ebene, die nicht für die Darstellung auf einer Karte gedacht ist, sondern für die Festlegung von Benutzereinstellungen verwendet wird", | ||||
|     "nl": "Een speciale lag die niet getoond wordt op de kaart, maar die de instellingen van de gebruiker weergeeft" | ||||
|   }, | ||||
|   "title": null, | ||||
|   "title": { | ||||
|     "render": { | ||||
|       "en": "Settings", | ||||
|       "nl": "Instellingen" | ||||
|     } | ||||
|   }, | ||||
|   "source": "special", | ||||
|   "calculatedTags": [ | ||||
|     "_mastodon_candidate_md=feat.properties._description.match(/\\[[^\\]]*\\]\\((.*(mastodon|en.osm.town).*)\\).*/)?.at(1)", | ||||
|  | @ -15,6 +20,66 @@ | |||
|     "_mastodon_candidate=feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a" | ||||
|   ], | ||||
|   "tagRenderings": [ | ||||
|     { | ||||
|       "id": "profile", | ||||
|       "render": { | ||||
|         "*": "{user_profile()}" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "language_picker", | ||||
|       "render": { | ||||
|         "*": "{language_picker()}" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "inbox", | ||||
|       "mappings": [ | ||||
|         { | ||||
|           "if": "_unreadMessages=0", | ||||
|           "then": { | ||||
|             "special": { | ||||
|               "type": "link", | ||||
|               "href":  "{_backend}/messages/inbox", | ||||
|               "text": { | ||||
|                 "en": "Open your inbox", | ||||
|                 "nl": "Ga naar je inbox" | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         { | ||||
|           "if": "_unreadMessages>0", | ||||
|           "then": { | ||||
|             "special": { | ||||
|               "type": "link", | ||||
|               "text": { | ||||
|                 "en": "<b class='alert'>You have {_unreadMessages}</b><br/>Open your inbox" | ||||
|               }, | ||||
|               "href": "{_backend}/messages/inbox" | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       ] | ||||
|     }, | ||||
|     { | ||||
|       "id": "settings-link", | ||||
|       "render": { | ||||
|         "special": { | ||||
|           "type": "link", | ||||
|           "text": { | ||||
|             "en": "Open your settings on OpenStreetMap.org" | ||||
|           }, | ||||
|           "href": "{_backend}/account/edit" | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "logout", | ||||
|       "render": { | ||||
|         "*": "{logout()}" | ||||
|       } | ||||
|     }, | ||||
|     { | ||||
|       "id": "picture-license", | ||||
|       "description": "This question is not meant to be placed on an OpenStreetMap-element; however it is used in the user information panel to ask which license the user wants", | ||||
|  |  | |||
|  | @ -859,10 +859,6 @@ video { | |||
|   margin-bottom: 0.75rem; | ||||
| } | ||||
| 
 | ||||
| .mt-1 { | ||||
|   margin-top: 0.25rem; | ||||
| } | ||||
| 
 | ||||
| .mr-2 { | ||||
|   margin-right: 0.5rem; | ||||
| } | ||||
|  | @ -931,6 +927,10 @@ video { | |||
|   margin-top: 2rem; | ||||
| } | ||||
| 
 | ||||
| .mt-1 { | ||||
|   margin-top: 0.25rem; | ||||
| } | ||||
| 
 | ||||
| .ml-3 { | ||||
|   margin-left: 0.75rem; | ||||
| } | ||||
|  | @ -1063,14 +1063,14 @@ video { | |||
|   height: 2.75rem; | ||||
| } | ||||
| 
 | ||||
| .h-96 { | ||||
|   height: 24rem; | ||||
| } | ||||
| 
 | ||||
| .h-64 { | ||||
|   height: 16rem; | ||||
| } | ||||
| 
 | ||||
| .h-96 { | ||||
|   height: 24rem; | ||||
| } | ||||
| 
 | ||||
| .h-0 { | ||||
|   height: 0px; | ||||
| } | ||||
|  |  | |||
|  | @ -594,9 +594,7 @@ | |||
|     }, | ||||
|     "userinfo": { | ||||
|         "gotoInbox": "Obre la teva safata d'entrada", | ||||
|         "gotoSettings": "Aneu a la vostra configuració a OpenStreetMap.org", | ||||
|         "moveToHome": "Mou el mapa a la vostra ubicació de casa", | ||||
|         "welcome": "Benvingut/da {name}" | ||||
|         "gotoSettings": "Aneu a la vostra configuració a OpenStreetMap.org" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -265,10 +265,7 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Otevřít poštu", | ||||
|         "gotoSettings": "Přejít do vašich nastavení na OpenStreetMap.org", | ||||
|         "moveToHome": "Přesunout mapu na vaší domovskou polohu", | ||||
|         "noDescription": "Na svém profilu zatím nemáte popis", | ||||
|         "noDescriptionCallToAction": "Přidat popis profilu", | ||||
|         "titleNotLoggedIn": "Vítejte", | ||||
|         "welcome": "Vítejte, {name}" | ||||
|         "noDescriptionCallToAction": "Přidat popis profilu" | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -934,12 +934,8 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Posteingang öffnen", | ||||
|         "gotoSettings": "Einstellungen auf OpenStreetMap.org öffnen", | ||||
|         "moveToHome": "Verschieben Sie die Karte an Ihren Heimatstandort", | ||||
|         "newMessages": "Sie haben neue Nachrichten", | ||||
|         "noDescription": "Sie haben noch keine Profilbeschreibung", | ||||
|         "noDescriptionCallToAction": "Profilbeschreibung hinzufügen", | ||||
|         "titleNotLoggedIn": "Willkommen", | ||||
|         "welcome": "Willkommen {name}" | ||||
|         "noDescriptionCallToAction": "Profilbeschreibung hinzufügen" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -958,12 +958,9 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Open your inbox", | ||||
|         "gotoSettings": "Go to your settings on OpenStreetMap.org", | ||||
|         "moveToHome": "Move the map to your home location", | ||||
|         "newMessages": "you have new messages", | ||||
|         "noDescription": "You don't have a description on your profile yet", | ||||
|         "noDescriptionCallToAction": "Add a profile description", | ||||
|         "titleNotLoggedIn": "Welcome", | ||||
|         "welcome": "Welcome {name}" | ||||
|         "notLoggedIn": "You have logged out" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -713,9 +713,7 @@ | |||
|         "missing": "{count} cadenas sin traducir", | ||||
|         "notImmediate": "Las traducciones no se actualizan directamente. Habitualmente esto lleva unos días" | ||||
|     }, | ||||
|     "userinfo": { | ||||
|         "welcome": "Bienvenido {name}" | ||||
|     }, | ||||
|     "userinfo": {}, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|             "description": "Un color o código hexadecimal" | ||||
|  |  | |||
|  | @ -488,9 +488,7 @@ | |||
|     }, | ||||
|     "userinfo": { | ||||
|         "gotoInbox": "Ouvrir sa boite de réception", | ||||
|         "gotoSettings": "Paramètres sur OpenStreetMap.org", | ||||
|         "moveToHome": "Déplacez la carte vers votre emplacement", | ||||
|         "welcome": "Bienvenue {name}" | ||||
|         "gotoSettings": "Paramètres sur OpenStreetMap.org" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "email": { | ||||
|  |  | |||
|  | @ -8540,6 +8540,24 @@ | |||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             "inbox": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|                         "then": { | ||||
|                             "special": { | ||||
|                                 "text": "Open your inbox" | ||||
|                             } | ||||
|                         } | ||||
|                     }, | ||||
|                     "1": { | ||||
|                         "then": { | ||||
|                             "special": { | ||||
|                                 "text": "<b class='alert'>You have {_unreadMessages}</b><br/>Open your inbox" | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             "picture-license": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|  | @ -8617,6 +8635,9 @@ | |||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "title": { | ||||
|             "render": "Settings" | ||||
|         } | ||||
|     }, | ||||
|     "veterinary": { | ||||
|  |  | |||
|  | @ -8265,6 +8265,17 @@ | |||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             "inbox": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|                         "then": { | ||||
|                             "special": { | ||||
|                                 "text": "Ga naar je inbox" | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }, | ||||
|             "picture-license": { | ||||
|                 "mappings": { | ||||
|                     "0": { | ||||
|  | @ -8314,6 +8325,9 @@ | |||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         }, | ||||
|         "title": { | ||||
|             "render": "Instellingen" | ||||
|         } | ||||
|     }, | ||||
|     "veterinary": { | ||||
|  |  | |||
|  | @ -692,11 +692,8 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Åpne innboksen din", | ||||
|         "gotoSettings": "Gå til innstillingene dine på OpenStreetMap.org", | ||||
|         "newMessages": "du har nye meldinger", | ||||
|         "noDescription": "Du har ikke noen profilbeskrivelse enda", | ||||
|         "noDescriptionCallToAction": "Legg til profilbeskrivelse", | ||||
|         "titleNotLoggedIn": "Velkommen", | ||||
|         "welcome": "Velkommen {name}" | ||||
|         "noDescriptionCallToAction": "Legg til profilbeskrivelse" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -938,12 +938,8 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Open je inbox", | ||||
|         "gotoSettings": "Ga naar je instellingen op OpenStreetMap.org", | ||||
|         "moveToHome": "Beweeg de kaart naar je thuislocatie", | ||||
|         "newMessages": "je hebt nieuwe berichten", | ||||
|         "noDescription": "Je hebt nog geen beschrijving op je profiel", | ||||
|         "noDescriptionCallToAction": "Voeg een profielbeschrijving toe", | ||||
|         "titleNotLoggedIn": "Welkom", | ||||
|         "welcome": "Welkom {name}" | ||||
|         "noDescriptionCallToAction": "Voeg een profielbeschrijving toe" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -192,11 +192,8 @@ | |||
|     "userinfo": { | ||||
|         "gotoInbox": "Otwórz swoją skrzynkę odbiorczą", | ||||
|         "gotoSettings": "Przejdź do swoich ustawień na OpenStreetMap.org", | ||||
|         "moveToHome": "Przesuń mapę do Twojej lokalizacji domowej", | ||||
|         "newMessages": "masz nowe wiadomości", | ||||
|         "noDescription": "Nie masz jeszcze opisu w swoim profilu", | ||||
|         "noDescriptionCallToAction": "Dodaj opis profilu", | ||||
|         "welcome": "Witaj {name}" | ||||
|         "noDescriptionCallToAction": "Dodaj opis profilu" | ||||
|     }, | ||||
|     "validation": { | ||||
|         "color": { | ||||
|  |  | |||
|  | @ -216,10 +216,7 @@ | |||
|     "translations": { | ||||
|         "activateButton": "Помогите перевести MapComplete" | ||||
|     }, | ||||
|     "userinfo": { | ||||
|         "titleNotLoggedIn": "Добро пожаловать", | ||||
|         "welcome": "Добро пожаловать, {name}" | ||||
|     }, | ||||
|     "userinfo": {}, | ||||
|     "validation": { | ||||
|         "nat": { | ||||
|             "notANumber": "Введите число" | ||||
|  |  | |||
							
								
								
									
										16
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -7,17 +7,24 @@ import ThemeViewState from "./Models/ThemeViewState" | |||
| import Combine from "./UI/Base/Combine" | ||||
| import SpecialVisualizations from "./UI/SpecialVisualizations" | ||||
| import AddNewPoint from "./UI/Popup/AddNewPoint/AddNewPoint.svelte" | ||||
| import UserProfile from "./UI/BigComponents/UserProfile.svelte" | ||||
| 
 | ||||
| async function main() { | ||||
|     new FixedUiElement("").AttachTo("extradiv") | ||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||
|     const main = new SvelteUIElement(ThemeViewGUI, { layout }) | ||||
|     const state = new ThemeViewState(layout) | ||||
| 
 | ||||
|     const main = new SvelteUIElement(ThemeViewGUI, { state }) | ||||
|     state.guistate.menuIsOpened.setData(true) | ||||
|     state.guistate.menuViewTab.setData("settings") | ||||
|     main.AttachTo("maindiv") | ||||
| } | ||||
| 
 | ||||
| async function testspecial() { | ||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||
|     const state = new ThemeViewState(layout) | ||||
| 
 | ||||
|     state.guistate.openUsersettings("picture-license") | ||||
|     const all = SpecialVisualizations.specialVisualizations.map((s) => | ||||
|         SpecialVisualizations.renderExampleOfSpecial(state, s) | ||||
|     ) | ||||
|  | @ -27,12 +34,7 @@ async function testspecial() { | |||
| async function test() { | ||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||
|     const state = new ThemeViewState(layout) | ||||
|     state.featureSwitches.featureSwitchIsTesting.setData(true) | ||||
|     new SvelteUIElement(AddNewPoint, { | ||||
|         state, | ||||
|         coordinate: { lon: 3.22001, lat: 51.21576 }, | ||||
|     }).AttachTo("maindiv") | ||||
|     //*/
 | ||||
|     new SvelteUIElement(UserProfile, { osmConnection: state.osmConnection }).AttachTo("maindiv") | ||||
| } | ||||
| 
 | ||||
| /* | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue