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 { OsmConnection } from "../Osm/OsmConnection" | ||||||
| import { MangroveIdentity } from "../Web/MangroveReviews" | import { MangroveIdentity } from "../Web/MangroveReviews" | ||||||
| import { Store, Stores, UIEventSource } from "../UIEventSource" | import { Store, Stores, UIEventSource } from "../UIEventSource" | ||||||
| import Locale from "../../UI/i18n/Locale" |  | ||||||
| import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource" | import StaticFeatureSource from "../FeatureSource/Sources/StaticFeatureSource" | ||||||
| import { FeatureSource } from "../FeatureSource/FeatureSource" | import { FeatureSource } from "../FeatureSource/FeatureSource" | ||||||
| import { Feature } from "geojson" | 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, |  * 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. |      * The number of seconds that the GPS-locations are stored in memory. | ||||||
|      * Time in seconds |      * Time in seconds | ||||||
|      */ |      */ | ||||||
|     public gpsLocationHistoryRetentionTime = new UIEventSource( |     public readonly gpsLocationHistoryRetentionTime = new UIEventSource( | ||||||
|         7 * 24 * 60 * 60, |         7 * 24 * 60 * 60, | ||||||
|         "gps_location_retention" |         "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 |         this.osmConnection = osmConnection | ||||||
|         { |         { | ||||||
|             const translationMode: UIEventSource<undefined | "true" | "false" | "mobile" | string> = |             const translationMode: UIEventSource<undefined | "true" | "false" | "mobile" | string> = | ||||||
|                 this.osmConnection.GetPreference("translation-mode") |                 this.osmConnection.GetPreference("translation-mode", "false") | ||||||
|             translationMode.addCallbackAndRunD((mode) => { |             translationMode.addCallbackAndRunD((mode) => { | ||||||
|                 mode = mode.toLowerCase() |                 mode = mode.toLowerCase() | ||||||
|                 if (mode === "true" || mode === "yes") { |                 if (mode === "true" || mode === "yes") { | ||||||
|  | @ -73,6 +94,8 @@ export default class UserRelatedState { | ||||||
|         this.installedUserThemes = this.InitInstalledUserThemes() |         this.installedUserThemes = this.InitInstalledUserThemes() | ||||||
| 
 | 
 | ||||||
|         this.homeLocation = this.initHomeLocation() |         this.homeLocation = this.initHomeLocation() | ||||||
|  | 
 | ||||||
|  |         this.preferencesAsTags = this.initAmendedPrefs(layout) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public GetUnofficialTheme(id: string): |     public GetUnofficialTheme(id: string): | ||||||
|  | @ -211,4 +234,127 @@ export default class UserRelatedState { | ||||||
|         }) |         }) | ||||||
|         return new StaticFeatureSource(feature) |         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 { |         } else { | ||||||
|             properties = fieldstate |             properties = fieldstate | ||||||
|         } |         } | ||||||
|         console.log("Building tagsspec with properties", properties) |  | ||||||
|         const missingKeys = option.fields |         const missingKeys = option.fields | ||||||
|             .map((f) => f.name) |             .map((f) => f.name) | ||||||
|             .filter((key) => properties[key] === undefined) |             .filter((key) => properties[key] === undefined) | ||||||
|  | @ -182,7 +181,6 @@ export default class FilteredLayer { | ||||||
|                 // We calculate the fields
 |                 // We calculate the fields
 | ||||||
|                 const fieldProperties = FilteredLayer.stringToFieldProperties(<string>state.data) |                 const fieldProperties = FilteredLayer.stringToFieldProperties(<string>state.data) | ||||||
|                 const asTags = FilteredLayer.fieldsToTags(filter.options[0], fieldProperties) |                 const asTags = FilteredLayer.fieldsToTags(filter.options[0], fieldProperties) | ||||||
|                 console.log("Current field properties:", state.data, fieldProperties, asTags) |  | ||||||
|                 if (asTags) { |                 if (asTags) { | ||||||
|                     needed.push(asTags) |                     needed.push(asTags) | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ export class MenuState { | ||||||
|     public readonly highlightedLayerInFilters: UIEventSource<string> = new UIEventSource<string>( |     public readonly highlightedLayerInFilters: UIEventSource<string> = new UIEventSource<string>( | ||||||
|         undefined |         undefined | ||||||
|     ) |     ) | ||||||
|  |     public highlightedUserSetting: UIEventSource<string> = new UIEventSource<string>(undefined) | ||||||
|     constructor() { |     constructor() { | ||||||
|         this.themeViewTabIndex = new UIEventSource(0) |         this.themeViewTabIndex = new UIEventSource(0) | ||||||
|         this.themeViewTab = this.themeViewTabIndex.sync( |         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() { |     public closeAll() { | ||||||
|         this.menuIsOpened.setData(false) |         this.menuIsOpened.setData(false) | ||||||
|         this.themeIsOpened.setData(false) |         this.themeIsOpened.setData(false) | ||||||
|  |  | ||||||
|  | @ -805,7 +805,13 @@ export class RewriteSpecial extends DesugaringStep<TagRenderingConfigJson> { | ||||||
|             const param = special[arg.name] |             const param = special[arg.name] | ||||||
|             if (param === undefined) { |             if (param === undefined) { | ||||||
|                 errors.push( |                 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, |             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.selectedElement = new UIEventSource<Feature | undefined>(undefined, "Selected element") | ||||||
|         this.selectedLayer = new UIEventSource<LayerConfig>(undefined, "Selected layer") |         this.selectedLayer = new UIEventSource<LayerConfig>(undefined, "Selected layer") | ||||||
|         this.geolocation = new GeoLocationHandler( |         this.geolocation = new GeoLocationHandler( | ||||||
|  |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
|   const offlineModes: Partial<Record<OsmServiceState, Translation>> = { |   const offlineModes: Partial<Record<OsmServiceState, Translation>> = { | ||||||
|     offline: t.loginFailedOfflineMode, |     offline: t.loginFailedOfflineMode, | ||||||
|     unreachable: t.loginFailedUnreachableMode, |     unreachable: t.loginFailedUnreachableMode, | ||||||
|  |     unknown: t.loginFailedUnreachableMode, | ||||||
|     readonly: t.loginFailedReadonlyMode |     readonly: t.loginFailedReadonlyMode | ||||||
|   }; |   }; | ||||||
|   const apiState = state.osmConnection.apiIsOnline; |   const apiState = state.osmConnection.apiIsOnline; | ||||||
|  |  | ||||||
|  | @ -7,15 +7,17 @@ | ||||||
|   import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"; |   import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte"; | ||||||
|   import { onDestroy } from "svelte"; |   import { onDestroy } from "svelte"; | ||||||
| 
 | 
 | ||||||
|   export let selectedElement: Feature; |   export let state: SpecialVisualizationState; | ||||||
|   export let layer: LayerConfig; |   export let layer: LayerConfig; | ||||||
|  |   export let selectedElement: Feature; | ||||||
|   export let tags: UIEventSource<Record<string, string>>; |   export let tags: UIEventSource<Record<string, string>>; | ||||||
|  |   export let highlightedRendering: UIEventSource<string> = undefined; | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
|   let _tags: Record<string, string>; |   let _tags: Record<string, string>; | ||||||
|   onDestroy(tags.addCallbackAndRun(tags => { |   onDestroy(tags.addCallbackAndRun(tags => { | ||||||
|     _tags = tags; |     _tags = tags; | ||||||
|   })); |   })); | ||||||
|   export let state: SpecialVisualizationState; |  | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| <div> | <div> | ||||||
|  | @ -40,7 +42,7 @@ | ||||||
|     {#each layer.tagRenderings as config (config.id)} |     {#each layer.tagRenderings as config (config.id)} | ||||||
|       {#if config.condition === undefined || config.condition.matchesProperties(_tags)} |       {#if config.condition === undefined || config.condition.matchesProperties(_tags)} | ||||||
|         {#if config.IsKnown(_tags)} |         {#if config.IsKnown(_tags)} | ||||||
|           <TagRenderingEditable {tags} {config} {state} {selectedElement} {layer}></TagRenderingEditable> |           <TagRenderingEditable {tags} {config} {state} {selectedElement} {layer} {highlightedRendering}></TagRenderingEditable> | ||||||
|         {/if} |         {/if} | ||||||
|       {/if} |       {/if} | ||||||
|     {/each} |     {/each} | ||||||
|  |  | ||||||
|  | @ -1,4 +1,3 @@ | ||||||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen" |  | ||||||
| import Translations from "../i18n/Translations" | import Translations from "../i18n/Translations" | ||||||
| import { OsmConnection } from "../../Logic/Osm/OsmConnection" | import { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||||
| import Combine from "../Base/Combine" | import Combine from "../Base/Combine" | ||||||
|  | @ -8,25 +7,13 @@ import { VariableUiElement } from "../Base/VariableUIElement" | ||||||
| import Img from "../Base/Img" | import Img from "../Base/Img" | ||||||
| import { FixedUiElement } from "../Base/FixedUiElement" | import { FixedUiElement } from "../Base/FixedUiElement" | ||||||
| import Link from "../Base/Link" | import Link from "../Base/Link" | ||||||
| import { Store, UIEventSource } from "../../Logic/UIEventSource" | import { UIEventSource } from "../../Logic/UIEventSource" | ||||||
| import Loc from "../../Models/Loc" | import Loc from "../../Models/Loc" | ||||||
| import BaseUIElement from "../BaseUIElement" | import BaseUIElement from "../BaseUIElement" | ||||||
| import Showdown from "showdown" | import Showdown from "showdown" | ||||||
| import LanguagePicker from "../LanguagePicker" | import LanguagePicker from "../LanguagePicker" | ||||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||||
| import Constants from "../../Models/Constants" | 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 { | export class ImportViewerLinks extends VariableUiElement { | ||||||
|     constructor(osmConnection: OsmConnection) { |     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 { | class UserInformationMainPanel extends VariableUiElement { | ||||||
|     private readonly settings: UIEventSource<Record<string, BaseUIElement>> |     private readonly settings: UIEventSource<Record<string, BaseUIElement>> | ||||||
|     private readonly userInfoFocusedQuestion?: UIEventSource<string> |     private readonly userInfoFocusedQuestion?: UIEventSource<string> | ||||||
|  | @ -104,210 +46,9 @@ class UserInformationMainPanel extends VariableUiElement { | ||||||
|         isOpened: UIEventSource<boolean>, |         isOpened: UIEventSource<boolean>, | ||||||
|         userInfoFocusedQuestion?: UIEventSource<string> |         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 settings = new UIEventSource<Record<string, BaseUIElement>>({}) | ||||||
|         const usersettingsConfig = new LayerConfig(usersettings, "userinformationpanel") |  | ||||||
| 
 | 
 | ||||||
|         const amendedPrefs = new UIEventSource<any>({ _theme: layout?.id }) |         super() | ||||||
|         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") |  | ||||||
|         this.settings = settings |         this.settings = settings | ||||||
|         this.userInfoFocusedQuestion = userInfoFocusedQuestion |         this.userInfoFocusedQuestion = userInfoFocusedQuestion | ||||||
|         const self = this |         const self = this | ||||||
|  | @ -325,50 +66,3 @@ class UserInformationMainPanel extends VariableUiElement { | ||||||
|         this.settings.data[focusedId]?.ScrollIntoView() |         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 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( |         const extraLink = Toggle.If( | ||||||
|             state.featureSwitchExtraLinkEnabled, |             state.featureSwitchExtraLinkEnabled, | ||||||
|             () => new ExtraLinkButton(state, state.layoutToUse.extraLink) |             () => new ExtraLinkButton(state, state.layoutToUse.extraLink) | ||||||
|  | @ -200,7 +176,7 @@ export default class DefaultGUI { | ||||||
|         const copyright = new MapControlButton(Svg.copyright_svg()).onClick(() => |         const copyright = new MapControlButton(Svg.copyright_svg()).onClick(() => | ||||||
|             guiState.copyrightViewIsOpened.setData(true) |             guiState.copyrightViewIsOpened.setData(true) | ||||||
|         ) |         ) | ||||||
|         new Combine([welcomeMessageMapControl, userInfoMapControl, copyright, extraLink]) |         new Combine([welcomeMessageMapControl, copyright, extraLink]) | ||||||
|             .SetClass("flex flex-col") |             .SetClass("flex flex-col") | ||||||
|             .AttachTo("top-left") |             .AttachTo("top-left") | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,32 +15,51 @@ | ||||||
|   export let tags: UIEventSource<Record<string, string>>; |   export let tags: UIEventSource<Record<string, string>>; | ||||||
|   export let selectedElement: Feature; |   export let selectedElement: Feature; | ||||||
|   export let state: SpecialVisualizationState; |   export let state: SpecialVisualizationState; | ||||||
|   export let layer: LayerConfig |   export let layer: LayerConfig; | ||||||
| 
 | 
 | ||||||
|   export let showQuestionIfUnknown : boolean= false |   export let highlightedRendering: UIEventSource<string> = undefined; | ||||||
|   let editMode = false |   export let showQuestionIfUnknown: boolean = false; | ||||||
|  |   let editMode = false; | ||||||
|   onDestroy(tags.addCallbackAndRunD(tags => { |   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> | </script> | ||||||
| 
 | 
 | ||||||
| {#if config.question} | <div bind:this={htmlElem}> | ||||||
|   {#if editMode} |   {#if config.question} | ||||||
|     <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer} > |     {#if editMode} | ||||||
|       <button slot="cancel" on:click={() => {editMode = false}}> |       <TagRenderingQuestion {config} {tags} {selectedElement} {state} {layer}> | ||||||
|         <Tr t={Translations.t.general.cancel}/> |         <button slot="cancel" on:click={() => {editMode = false}}> | ||||||
|       </button> |           <Tr t={Translations.t.general.cancel} /> | ||||||
|     </TagRenderingQuestion> |         </button> | ||||||
|   {:else} |       </TagRenderingQuestion> | ||||||
|     <div class="flex justify-between"> |     {:else} | ||||||
|       <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> |       <div class="flex justify-between"> | ||||||
|       <button on:click={() => {editMode = true}} class="w-6 h-6 rounded-full subtle-background p-1"> |         <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||||
|         <PencilAltIcon></PencilAltIcon> |         <button on:click={() => {editMode = true}} class="shrink-0 w-6 h-6 rounded-full subtle-background p-1"> | ||||||
|       </button> |           <PencilAltIcon></PencilAltIcon> | ||||||
|     </div> |         </button> | ||||||
|  |       </div> | ||||||
|  |     {/if} | ||||||
|  |   {:else } | ||||||
|  |     <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> | ||||||
|   {/if} |   {/if} | ||||||
| {:else } | </div> | ||||||
|   <TagRenderingAnswer {config} {tags} {selectedElement} {state} {layer} /> |  | ||||||
| {/if} |  | ||||||
|  |  | ||||||
|  | @ -10,7 +10,6 @@ | ||||||
|   import { TagsFilter } from "../../../Logic/Tags/TagsFilter"; |   import { TagsFilter } from "../../../Logic/Tags/TagsFilter"; | ||||||
|   import FreeformInput from "./FreeformInput.svelte"; |   import FreeformInput from "./FreeformInput.svelte"; | ||||||
|   import Translations from "../../i18n/Translations.js"; |   import Translations from "../../i18n/Translations.js"; | ||||||
|   import FromHtml from "../../Base/FromHtml.svelte"; |  | ||||||
|   import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"; |   import ChangeTagAction from "../../../Logic/Osm/Actions/ChangeTagAction"; | ||||||
|   import { createEventDispatcher } from "svelte"; |   import { createEventDispatcher } from "svelte"; | ||||||
|   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; |   import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"; | ||||||
|  | @ -63,6 +62,24 @@ | ||||||
|   }>(); |   }>(); | ||||||
| 
 | 
 | ||||||
|   function onSave() { |   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 }); |     dispatch("saved", { config, applied: selectedTags }); | ||||||
|     const change = new ChangeTagAction( |     const change = new ChangeTagAction( | ||||||
|       tags.data.id, |       tags.data.id, | ||||||
|  | @ -76,6 +93,7 @@ | ||||||
|     freeformInput.setData(undefined); |     freeformInput.setData(undefined); | ||||||
|     selectedMapping = 0; |     selectedMapping = 0; | ||||||
|     selectedTags = undefined; |     selectedTags = undefined; | ||||||
|  | 
 | ||||||
|     change.CreateChangeDescriptions().then(changes => |     change.CreateChangeDescriptions().then(changes => | ||||||
|       state.changes.applyChanges(changes) |       state.changes.applyChanges(changes) | ||||||
|     ).catch(console.error); |     ).catch(console.error); | ||||||
|  | @ -93,12 +111,14 @@ | ||||||
|         </span> |         </span> | ||||||
|         <span class="alert">{config.id}</span> |         <span class="alert">{config.id}</span> | ||||||
|       </div> |       </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> | ||||||
| 
 | 
 | ||||||
|     {#if config.questionhint} |     {#if config.questionhint} | ||||||
|       <div class="subtle"> |       <div class="subtle"> | ||||||
|         <SpecialTranslation t={config.questionhint} {tags} {state} {layer} feature={selectedElement}></SpecialTranslation> |         <SpecialTranslation t={config.questionhint} {tags} {state} {layer} | ||||||
|  |                             feature={selectedElement}></SpecialTranslation> | ||||||
|       </div> |       </div> | ||||||
|     {/if} |     {/if} | ||||||
| 
 | 
 | ||||||
|  | @ -152,7 +172,7 @@ | ||||||
|       </div> |       </div> | ||||||
|     {/if} |     {/if} | ||||||
| 
 | 
 | ||||||
|   <TagHint osmConnection={state.osmConnection} tags={selectedTags}></TagHint> |     <TagHint osmConnection={state.osmConnection} tags={selectedTags}></TagHint> | ||||||
|     <div> |     <div> | ||||||
|       <!-- TagRenderingQuestion-buttons --> |       <!-- TagRenderingQuestion-buttons --> | ||||||
|       <slot name="cancel"></slot> |       <slot name="cancel"></slot> | ||||||
|  | @ -163,7 +183,7 @@ | ||||||
|       {:else } |       {:else } | ||||||
|         <div class="w-6 h-6"> |         <div class="w-6 h-6"> | ||||||
|           <!-- Invalid value; show an inactive button or something like that--> |           <!-- Invalid value; show an inactive button or something like that--> | ||||||
|         <ExclamationIcon></ExclamationIcon> |           <ExclamationIcon></ExclamationIcon> | ||||||
|         </div> |         </div> | ||||||
|       {/if} |       {/if} | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -56,16 +56,27 @@ import Maproulette from "../Logic/Maproulette" | ||||||
| import SvelteUIElement from "./Base/SvelteUIElement" | import SvelteUIElement from "./Base/SvelteUIElement" | ||||||
| import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | import { BBoxFeatureSourceForLayer } from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" | ||||||
| import QuestionViz from "./Popup/QuestionViz" | import QuestionViz from "./Popup/QuestionViz" | ||||||
| import SimpleAddUI from "./BigComponents/SimpleAddUI" |  | ||||||
| import { Feature } from "geojson" | import { Feature } from "geojson" | ||||||
| import { GeoOperations } from "../Logic/GeoOperations" | import { GeoOperations } from "../Logic/GeoOperations" | ||||||
| import CreateNewNote from "./Popup/CreateNewNote.svelte" | import CreateNewNote from "./Popup/CreateNewNote.svelte" | ||||||
| import { svelte } from "@sveltejs/vite-plugin-svelte" |  | ||||||
| import AddNewPoint from "./Popup/AddNewPoint/AddNewPoint.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 { | export default class SpecialVisualizations { | ||||||
|     public static specialVisualizations: SpecialVisualization[] = SpecialVisualizations.initList() |     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. |      * 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 ?? "") |                 const args = knownSpecial.args.map((arg) => arg.defaultValue ?? "") | ||||||
|                 if (argument.length > 0) { |                 if (argument.length > 0) { | ||||||
|                     const realArgs = argument.split(",").map((str) => |                     const realArgs = argument.split(",").map((str) => this.undoEncoding(str)) | ||||||
|                         str |  | ||||||
|                             .trim() |  | ||||||
|                             .replace(/&LPARENS/g, "(") |  | ||||||
|                             .replace(/&RPARENS/g, ")") |  | ||||||
|                             .replace(/&LBRACE/g, "{") |  | ||||||
|                             .replace(/&RBRACE/g, "}") |  | ||||||
|                             .replace(/&COMMA/g, ",") |  | ||||||
|                     ) |  | ||||||
|                     for (let i = 0; i < realArgs.length; i++) { |                     for (let i = 0; i < realArgs.length; i++) { | ||||||
|                         if (args.length <= i) { |                         if (args.length <= i) { | ||||||
|                             args.push(realArgs[i]) |                             args.push(realArgs[i]) | ||||||
|  | @ -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 HistogramViz(), | ||||||
|             new StealViz(), |             new StealViz(), | ||||||
|             new MinimapViz(), |             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()}?'", |                 docs: "Shows the title of the popup. Useful for some cases, e.g. 'What is phone number of {title()}?'", | ||||||
|                 example: |                 example: | ||||||
|                     "`What is the phone number of {title()}`, which might automatically become `What is the phone number of XYZ`.", |                     "`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( |                     new VariableUiElement( | ||||||
|                         tagsSource.map((tags) => { |                         tagsSource.map((tags) => { | ||||||
|                             const layer = state.layout.getMatchingLayer(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", |                 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", |                 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 |                             tagsSource.data.id | ||||||
|                         ) |                         ) | ||||||
|                         return viz.func |                         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) |                             ?.SetStyle(proto.style) | ||||||
|                     } catch (e) { |                     } catch (e) { | ||||||
|                         console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e) |                         console.error("SPECIALRENDERING FAILED for", tagsSource.data?.id, e) | ||||||
|  |  | ||||||
|  | @ -2,13 +2,12 @@ | ||||||
|   import { Store, UIEventSource } from "../Logic/UIEventSource"; |   import { Store, UIEventSource } from "../Logic/UIEventSource"; | ||||||
|   import { Map as MlMap } from "maplibre-gl"; |   import { Map as MlMap } from "maplibre-gl"; | ||||||
|   import MaplibreMap from "./Map/MaplibreMap.svelte"; |   import MaplibreMap from "./Map/MaplibreMap.svelte"; | ||||||
|   import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"; |  | ||||||
|   import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; |   import FeatureSwitchState from "../Logic/State/FeatureSwitchState"; | ||||||
|   import MapControlButton from "./Base/MapControlButton.svelte"; |   import MapControlButton from "./Base/MapControlButton.svelte"; | ||||||
|   import ToSvelte from "./Base/ToSvelte.svelte"; |   import ToSvelte from "./Base/ToSvelte.svelte"; | ||||||
|   import Svg from "../Svg"; |   import Svg from "../Svg"; | ||||||
|   import If from "./Base/If.svelte"; |   import If from "./Base/If.svelte"; | ||||||
|   import { GeolocationControl } from "./BigComponents/GeolocationControl.js"; |   import { GeolocationControl } from "./BigComponents/GeolocationControl"; | ||||||
|   import type { Feature } from "geojson"; |   import type { Feature } from "geojson"; | ||||||
|   import SelectedElementView from "./BigComponents/SelectedElementView.svelte"; |   import SelectedElementView from "./BigComponents/SelectedElementView.svelte"; | ||||||
|   import LayerConfig from "../Models/ThemeConfig/LayerConfig"; |   import LayerConfig from "../Models/ThemeConfig/LayerConfig"; | ||||||
|  | @ -17,19 +16,21 @@ | ||||||
|   import ThemeViewState from "../Models/ThemeViewState"; |   import ThemeViewState from "../Models/ThemeViewState"; | ||||||
|   import type { MapProperties } from "../Models/MapProperties"; |   import type { MapProperties } from "../Models/MapProperties"; | ||||||
|   import Geosearch from "./BigComponents/Geosearch.svelte"; |   import Geosearch from "./BigComponents/Geosearch.svelte"; | ||||||
|   import { Tab, TabGroup, TabList, TabPanel, TabPanels } from "@rgossiaux/svelte-headlessui"; |  | ||||||
|   import Translations from "./i18n/Translations"; |   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 Tr from "./Base/Tr.svelte"; | ||||||
|   import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"; |   import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"; | ||||||
|   import FloatOver from "./Base/FloatOver.svelte"; |   import FloatOver from "./Base/FloatOver.svelte"; | ||||||
|   import PrivacyPolicy from "./BigComponents/PrivacyPolicy.js"; |   import PrivacyPolicy from "./BigComponents/PrivacyPolicy"; | ||||||
|   import { Utils } from "../Utils.js"; |   import { Utils } from "../Utils"; | ||||||
|   import Constants from "../Models/Constants"; |   import Constants from "../Models/Constants"; | ||||||
|   import TabbedGroup from "./Base/TabbedGroup.svelte"; |   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; |   export let state: ThemeViewState; | ||||||
|   const state = new ThemeViewState(layout); |   let layout = state.layout; | ||||||
| 
 | 
 | ||||||
|   let selectedElementTags: Store<UIEventSource<Record<string, string>>> = |   let selectedElementTags: Store<UIEventSource<Record<string, string>>> = | ||||||
|     state.selectedElement.mapD((f) => { |     state.selectedElement.mapD((f) => { | ||||||
|  | @ -43,6 +44,7 @@ | ||||||
|   let mapproperties: MapProperties = state.mapProperties; |   let mapproperties: MapProperties = state.mapProperties; | ||||||
|   let featureSwitches: FeatureSwitchState = state.featureSwitches; |   let featureSwitches: FeatureSwitchState = state.featureSwitches; | ||||||
|   let availableLayers = state.availableLayers; |   let availableLayers = state.availableLayers; | ||||||
|  |   let userdetails = state.osmConnection.userDetails; | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -97,7 +99,7 @@ | ||||||
| {#if $selectedElement !== undefined && $selectedLayer !== undefined} | {#if $selectedElement !== undefined && $selectedLayer !== undefined} | ||||||
|   <FloatOver on:close={() => {selectedElement.setData(undefined)}}> |   <FloatOver on:close={() => {selectedElement.setData(undefined)}}> | ||||||
|     <SelectedElementView layer={$selectedLayer} selectedElement={$selectedElement} |     <SelectedElementView layer={$selectedLayer} selectedElement={$selectedElement} | ||||||
|                          tags={$selectedElementTags} state={state}></SelectedElementView> |                          tags={$selectedElementTags} state={state} /> | ||||||
|   </FloatOver> |   </FloatOver> | ||||||
| 
 | 
 | ||||||
| {/if} | {/if} | ||||||
|  | @ -106,7 +108,7 @@ | ||||||
|   <!-- Theme page --> |   <!-- Theme page --> | ||||||
|   <FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}> |   <FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}> | ||||||
|     <TabbedGroup tab={state.guistate.themeViewTabIndex}> |     <TabbedGroup tab={state.guistate.themeViewTabIndex}> | ||||||
|           <Tr slot="title0" t={layout.title} /> |       <Tr slot="title0" t={layout.title} /> | ||||||
| 
 | 
 | ||||||
|       <div slot="content0"> |       <div slot="content0"> | ||||||
| 
 | 
 | ||||||
|  | @ -130,17 +132,18 @@ | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|       </div> |       </div> | ||||||
|        | 
 | ||||||
|       <div slot="title1" class="flex"> |       <div class="flex" slot="title1"> | ||||||
|         <If condition={state.featureSwitches.featureSwitchFilter}> |         <If condition={state.featureSwitches.featureSwitchFilter}> | ||||||
|         <img class="w-4 h-4" src="./assets/svg/filter.svg"> |           <img class="w-4 h-4" src="./assets/svg/filter.svg"> | ||||||
|         <Tr t={Translations.t.general.menu.filter} /> |           <Tr t={Translations.t.general.menu.filter} /> | ||||||
|         </If> |         </If> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div slot="content1" class="flex flex-col"> |       <div class="flex flex-col" slot="content1"> | ||||||
|         {#each layout.layers as layer} |         {#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} |         {/each} | ||||||
|         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> |         <If condition={state.featureSwitches.featureSwitchBackgroundSelection}> | ||||||
|           <RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker> |           <RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker> | ||||||
|  | @ -154,50 +157,56 @@ | ||||||
| <If condition={state.guistate.menuIsOpened}> | <If condition={state.guistate.menuIsOpened}> | ||||||
|   <!-- Menu page --> |   <!-- Menu page --> | ||||||
|   <FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}> |   <FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}> | ||||||
|     <TabGroup on:change={(e) => {state.guistate.menuViewTabIndex.setData(e.detail)} }> |     <TabbedGroup tab={state.guistate.menuViewTabIndex}> | ||||||
|       <TabList> |       <div class="flex" slot="title0"> | ||||||
|         <Tab class={({selected}) => selected ? "tab-selected" : "tab-unselected"}> |         <Tr t={Translations.t.general.aboutMapcompleteTitle}></Tr> | ||||||
|           <div class="flex"> |       </div> | ||||||
|             <Tr t={Translations.t.general.aboutMapcompleteTitle}></Tr> | 
 | ||||||
|           </div> |       <div class="flex flex-col" slot="content0"> | ||||||
|         </Tab> |         <Tr t={Translations.t.general.aboutMapcomplete.Subs({ | ||||||
|         <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"> |  | ||||||
|           <Tr t={Translations.t.general.aboutMapcomplete.Subs({ |  | ||||||
|                     osmcha_link: Utils.OsmChaLinkFor(7), |                     osmcha_link: Utils.OsmChaLinkFor(7), | ||||||
|                 })}></Tr> |                 })}></Tr> | ||||||
| 
 | 
 | ||||||
|           {Constants.vNumber} |         {Constants.vNumber} | ||||||
|         </TabPanel> |       </div> | ||||||
|         <TabPanel>User settings</TabPanel> |  | ||||||
|         <TabPanel> |  | ||||||
|           <CommunityIndexView location={state.mapProperties.location}></CommunityIndexView> |  | ||||||
| 
 | 
 | ||||||
|         </TabPanel> |       <If condition={state.osmConnection.isLoggedIn} slot="title1"> | ||||||
|         <TabPanel> |         <div class="flex"> | ||||||
|           <ToSvelte construct={() => new PrivacyPolicy()}></ToSvelte> |           <CogIcon class="w-6 h-6" /> | ||||||
|         </TabPanel> |           <Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} /> | ||||||
|       </TabPanels> |         </div> | ||||||
|     </TabGroup> |       </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> |   </FloatOver> | ||||||
| </If> | </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", |     "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" |     "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", |   "source": "special", | ||||||
|   "calculatedTags": [ |   "calculatedTags": [ | ||||||
|     "_mastodon_candidate_md=feat.properties._description.match(/\\[[^\\]]*\\]\\((.*(mastodon|en.osm.town).*)\\).*/)?.at(1)", |     "_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" |     "_mastodon_candidate=feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a" | ||||||
|   ], |   ], | ||||||
|   "tagRenderings": [ |   "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", |       "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", |       "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", | ||||||
|  | @ -328,4 +393,4 @@ | ||||||
|     } |     } | ||||||
|   ], |   ], | ||||||
|   "mapRendering": null |   "mapRendering": null | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -859,10 +859,6 @@ video { | ||||||
|   margin-bottom: 0.75rem; |   margin-bottom: 0.75rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .mt-1 { |  | ||||||
|   margin-top: 0.25rem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .mr-2 { | .mr-2 { | ||||||
|   margin-right: 0.5rem; |   margin-right: 0.5rem; | ||||||
| } | } | ||||||
|  | @ -931,6 +927,10 @@ video { | ||||||
|   margin-top: 2rem; |   margin-top: 2rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .mt-1 { | ||||||
|  |   margin-top: 0.25rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .ml-3 { | .ml-3 { | ||||||
|   margin-left: 0.75rem; |   margin-left: 0.75rem; | ||||||
| } | } | ||||||
|  | @ -1063,14 +1063,14 @@ video { | ||||||
|   height: 2.75rem; |   height: 2.75rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .h-96 { |  | ||||||
|   height: 24rem; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .h-64 { | .h-64 { | ||||||
|   height: 16rem; |   height: 16rem; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .h-96 { | ||||||
|  |   height: 24rem; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .h-0 { | .h-0 { | ||||||
|   height: 0px; |   height: 0px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -594,9 +594,7 @@ | ||||||
|     }, |     }, | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Obre la teva safata d'entrada", |         "gotoInbox": "Obre la teva safata d'entrada", | ||||||
|         "gotoSettings": "Aneu a la vostra configuració a OpenStreetMap.org", |         "gotoSettings": "Aneu a la vostra configuració a OpenStreetMap.org" | ||||||
|         "moveToHome": "Mou el mapa a la vostra ubicació de casa", |  | ||||||
|         "welcome": "Benvingut/da {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -265,10 +265,7 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Otevřít poštu", |         "gotoInbox": "Otevřít poštu", | ||||||
|         "gotoSettings": "Přejít do vašich nastavení na OpenStreetMap.org", |         "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", |         "noDescription": "Na svém profilu zatím nemáte popis", | ||||||
|         "noDescriptionCallToAction": "Přidat popis profilu", |         "noDescriptionCallToAction": "Přidat popis profilu" | ||||||
|         "titleNotLoggedIn": "Vítejte", |  | ||||||
|         "welcome": "Vítejte, {name}" |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -934,12 +934,8 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Posteingang öffnen", |         "gotoInbox": "Posteingang öffnen", | ||||||
|         "gotoSettings": "Einstellungen auf OpenStreetMap.org ö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", |         "noDescription": "Sie haben noch keine Profilbeschreibung", | ||||||
|         "noDescriptionCallToAction": "Profilbeschreibung hinzufügen", |         "noDescriptionCallToAction": "Profilbeschreibung hinzufügen" | ||||||
|         "titleNotLoggedIn": "Willkommen", |  | ||||||
|         "welcome": "Willkommen {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -958,12 +958,9 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Open your inbox", |         "gotoInbox": "Open your inbox", | ||||||
|         "gotoSettings": "Go to your settings on OpenStreetMap.org", |         "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", |         "noDescription": "You don't have a description on your profile yet", | ||||||
|         "noDescriptionCallToAction": "Add a profile description", |         "noDescriptionCallToAction": "Add a profile description", | ||||||
|         "titleNotLoggedIn": "Welcome", |         "notLoggedIn": "You have logged out" | ||||||
|         "welcome": "Welcome {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -713,9 +713,7 @@ | ||||||
|         "missing": "{count} cadenas sin traducir", |         "missing": "{count} cadenas sin traducir", | ||||||
|         "notImmediate": "Las traducciones no se actualizan directamente. Habitualmente esto lleva unos días" |         "notImmediate": "Las traducciones no se actualizan directamente. Habitualmente esto lleva unos días" | ||||||
|     }, |     }, | ||||||
|     "userinfo": { |     "userinfo": {}, | ||||||
|         "welcome": "Bienvenido {name}" |  | ||||||
|     }, |  | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|             "description": "Un color o código hexadecimal" |             "description": "Un color o código hexadecimal" | ||||||
|  |  | ||||||
|  | @ -488,9 +488,7 @@ | ||||||
|     }, |     }, | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Ouvrir sa boite de réception", |         "gotoInbox": "Ouvrir sa boite de réception", | ||||||
|         "gotoSettings": "Paramètres sur OpenStreetMap.org", |         "gotoSettings": "Paramètres sur OpenStreetMap.org" | ||||||
|         "moveToHome": "Déplacez la carte vers votre emplacement", |  | ||||||
|         "welcome": "Bienvenue {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "email": { |         "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": { |             "picture-license": { | ||||||
|                 "mappings": { |                 "mappings": { | ||||||
|                     "0": { |                     "0": { | ||||||
|  | @ -8617,6 +8635,9 @@ | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         }, | ||||||
|  |         "title": { | ||||||
|  |             "render": "Settings" | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     "veterinary": { |     "veterinary": { | ||||||
|  |  | ||||||
|  | @ -8265,6 +8265,17 @@ | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|  |             "inbox": { | ||||||
|  |                 "mappings": { | ||||||
|  |                     "0": { | ||||||
|  |                         "then": { | ||||||
|  |                             "special": { | ||||||
|  |                                 "text": "Ga naar je inbox" | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|             "picture-license": { |             "picture-license": { | ||||||
|                 "mappings": { |                 "mappings": { | ||||||
|                     "0": { |                     "0": { | ||||||
|  | @ -8314,6 +8325,9 @@ | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |         }, | ||||||
|  |         "title": { | ||||||
|  |             "render": "Instellingen" | ||||||
|         } |         } | ||||||
|     }, |     }, | ||||||
|     "veterinary": { |     "veterinary": { | ||||||
|  |  | ||||||
|  | @ -692,11 +692,8 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Åpne innboksen din", |         "gotoInbox": "Åpne innboksen din", | ||||||
|         "gotoSettings": "Gå til innstillingene dine på OpenStreetMap.org", |         "gotoSettings": "Gå til innstillingene dine på OpenStreetMap.org", | ||||||
|         "newMessages": "du har nye meldinger", |  | ||||||
|         "noDescription": "Du har ikke noen profilbeskrivelse enda", |         "noDescription": "Du har ikke noen profilbeskrivelse enda", | ||||||
|         "noDescriptionCallToAction": "Legg til profilbeskrivelse", |         "noDescriptionCallToAction": "Legg til profilbeskrivelse" | ||||||
|         "titleNotLoggedIn": "Velkommen", |  | ||||||
|         "welcome": "Velkommen {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -938,12 +938,8 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Open je inbox", |         "gotoInbox": "Open je inbox", | ||||||
|         "gotoSettings": "Ga naar je instellingen op OpenStreetMap.org", |         "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", |         "noDescription": "Je hebt nog geen beschrijving op je profiel", | ||||||
|         "noDescriptionCallToAction": "Voeg een profielbeschrijving toe", |         "noDescriptionCallToAction": "Voeg een profielbeschrijving toe" | ||||||
|         "titleNotLoggedIn": "Welkom", |  | ||||||
|         "welcome": "Welkom {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -192,11 +192,8 @@ | ||||||
|     "userinfo": { |     "userinfo": { | ||||||
|         "gotoInbox": "Otwórz swoją skrzynkę odbiorczą", |         "gotoInbox": "Otwórz swoją skrzynkę odbiorczą", | ||||||
|         "gotoSettings": "Przejdź do swoich ustawień na OpenStreetMap.org", |         "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", |         "noDescription": "Nie masz jeszcze opisu w swoim profilu", | ||||||
|         "noDescriptionCallToAction": "Dodaj opis profilu", |         "noDescriptionCallToAction": "Dodaj opis profilu" | ||||||
|         "welcome": "Witaj {name}" |  | ||||||
|     }, |     }, | ||||||
|     "validation": { |     "validation": { | ||||||
|         "color": { |         "color": { | ||||||
|  |  | ||||||
|  | @ -216,10 +216,7 @@ | ||||||
|     "translations": { |     "translations": { | ||||||
|         "activateButton": "Помогите перевести MapComplete" |         "activateButton": "Помогите перевести MapComplete" | ||||||
|     }, |     }, | ||||||
|     "userinfo": { |     "userinfo": {}, | ||||||
|         "titleNotLoggedIn": "Добро пожаловать", |  | ||||||
|         "welcome": "Добро пожаловать, {name}" |  | ||||||
|     }, |  | ||||||
|     "validation": { |     "validation": { | ||||||
|         "nat": { |         "nat": { | ||||||
|             "notANumber": "Введите число" |             "notANumber": "Введите число" | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -7,17 +7,24 @@ import ThemeViewState from "./Models/ThemeViewState" | ||||||
| import Combine from "./UI/Base/Combine" | import Combine from "./UI/Base/Combine" | ||||||
| import SpecialVisualizations from "./UI/SpecialVisualizations" | import SpecialVisualizations from "./UI/SpecialVisualizations" | ||||||
| import AddNewPoint from "./UI/Popup/AddNewPoint/AddNewPoint.svelte" | import AddNewPoint from "./UI/Popup/AddNewPoint/AddNewPoint.svelte" | ||||||
|  | import UserProfile from "./UI/BigComponents/UserProfile.svelte" | ||||||
| 
 | 
 | ||||||
| async function main() { | async function main() { | ||||||
|     new FixedUiElement("").AttachTo("extradiv") |     new FixedUiElement("").AttachTo("extradiv") | ||||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 |     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") |     main.AttachTo("maindiv") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| async function testspecial() { | async function testspecial() { | ||||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 |     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||||
|     const state = new ThemeViewState(layout) |     const state = new ThemeViewState(layout) | ||||||
|  | 
 | ||||||
|  |     state.guistate.openUsersettings("picture-license") | ||||||
|     const all = SpecialVisualizations.specialVisualizations.map((s) => |     const all = SpecialVisualizations.specialVisualizations.map((s) => | ||||||
|         SpecialVisualizations.renderExampleOfSpecial(state, s) |         SpecialVisualizations.renderExampleOfSpecial(state, s) | ||||||
|     ) |     ) | ||||||
|  | @ -27,12 +34,7 @@ async function testspecial() { | ||||||
| async function test() { | async function test() { | ||||||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 |     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||||
|     const state = new ThemeViewState(layout) |     const state = new ThemeViewState(layout) | ||||||
|     state.featureSwitches.featureSwitchIsTesting.setData(true) |     new SvelteUIElement(UserProfile, { osmConnection: state.osmConnection }).AttachTo("maindiv") | ||||||
|     new SvelteUIElement(AddNewPoint, { |  | ||||||
|         state, |  | ||||||
|         coordinate: { lon: 3.22001, lat: 51.21576 }, |  | ||||||
|     }).AttachTo("maindiv") |  | ||||||
|     //*/
 |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* | /* | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue