forked from MapComplete/MapComplete
		
	refactoring: Remove more obsoleted code, add copyright tab
This commit is contained in:
		
							parent
							
								
									f8d34648a0
								
							
						
					
					
						commit
						042d400dc4
					
				
					 20 changed files with 44 additions and 342 deletions
				
			
		|  | @ -1,25 +0,0 @@ | |||
| import { UIEventSource } from "../UIEventSource" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import ScrollableFullScreen from "../../UI/Base/ScrollableFullScreen" | ||||
| import BaseUIElement from "../../UI/BaseUIElement" | ||||
| 
 | ||||
| /** | ||||
|  * The stray-click-handler adds a marker to the map if no feature was clicked. | ||||
|  * Shows the given uiToShow-element in the messagebox | ||||
|  * | ||||
|  * Note: the actual implementation is in StrayClickHandlerImplementation | ||||
|  */ | ||||
| export default class StrayClickHandler { | ||||
|     public static construct = ( | ||||
|         state: { | ||||
|             LastClickLocation: UIEventSource<{ lat: number; lon: number }> | ||||
|             selectedElement: UIEventSource<string> | ||||
|             filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|             leafletMap: UIEventSource<any> | ||||
|         }, | ||||
|         uiToShow: ScrollableFullScreen, | ||||
|         iconToShow: BaseUIElement | ||||
|     ) => { | ||||
|         return undefined | ||||
|     } | ||||
| } | ||||
|  | @ -11,13 +11,13 @@ export default class ContributorCount { | |||
|     private lastUpdate: Date = undefined | ||||
| 
 | ||||
|     constructor(state: { | ||||
|         bounds: Store<BBox> | ||||
|         mapProperties: { bounds: Store<BBox> } | ||||
|         dataIsLoading: Store<boolean> | ||||
|         perLayer: ReadonlyMap<string, GeoIndexedStore> | ||||
|     }) { | ||||
|         this.perLayer = state.perLayer | ||||
|         const self = this | ||||
|         state.bounds.mapD( | ||||
|         state.mapProperties.bounds.mapD( | ||||
|             (bbox) => { | ||||
|                 self.update(bbox) | ||||
|             }, | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| import { FeatureSource , FeatureSourceForLayer } from "../FeatureSource" | ||||
| import { FeatureSource, FeatureSourceForLayer } from "../FeatureSource" | ||||
| import { Feature } from "geojson" | ||||
| import { BBox } from "../../BBox" | ||||
| import { GeoOperations } from "../../GeoOperations" | ||||
|  | @ -26,9 +26,12 @@ export default class GeoIndexedStore implements FeatureSource { | |||
|     public GetFeaturesWithin(bbox: BBox): Feature[] { | ||||
|         // TODO optimize
 | ||||
|         const bboxFeature = bbox.asGeoJson({}) | ||||
|         return this.features.data.filter( | ||||
|             (f) => GeoOperations.intersect(f, bboxFeature) !== undefined | ||||
|         ) | ||||
|         return this.features.data.filter((f) => { | ||||
|             if (f.geometry.type === "Point") { | ||||
|                 return bbox.contains(<[number, number]>f.geometry.coordinates) | ||||
|             } | ||||
|             return GeoOperations.intersect(f, bboxFeature) !== undefined | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,17 +0,0 @@ | |||
| import SelectedFeatureHandler from "../Actors/SelectedFeatureHandler" | ||||
| import Hash from "../Web/Hash" | ||||
| import MetaTagRecalculator from "../FeatureSource/Actors/MetaTagRecalculator" | ||||
| 
 | ||||
| export default class FeaturePipelineState { | ||||
|     /** | ||||
|      * The piece of code which fetches data from various sources and shows it on the background map | ||||
|      */ | ||||
|     public readonly featurePipeline: FeaturePipeline | ||||
|     private readonly metatagRecalculator: MetaTagRecalculator | ||||
| 
 | ||||
|     constructor() { | ||||
|         this.metatagRecalculator = new MetaTagRecalculator(this, this.featurePipeline) | ||||
|         this.metatagRecalculator.registerSource(this.currentView) | ||||
|         new SelectedFeatureHandler(Hash.hash, this) | ||||
|     } | ||||
| } | ||||
|  | @ -244,7 +244,7 @@ export class RegexTag extends TagsFilter { | |||
|         return [] | ||||
|     } | ||||
| 
 | ||||
|     asChange(properties: Record<string, string>): { k: string; v: string }[] { | ||||
|     asChange(): { k: string; v: string }[] { | ||||
|         if (this.invert) { | ||||
|             return [] | ||||
|         } | ||||
|  |  | |||
|  | @ -222,6 +222,14 @@ export default class ThemeViewState implements SpecialVisualizationState { | |||
|                 } | ||||
|             } | ||||
|         ) | ||||
|         /* | ||||
|         Hotkeys.RegisterHotkey( | ||||
|             { shift: "O" }, | ||||
|             Translations.t.hotkeyDocumentation.selectMapnik, | ||||
|             () => { | ||||
|                 this.state.backgroundLayer.setData(AvailableBaseLayers.osmCarto) | ||||
|             } | ||||
|         )//*/
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  |  | |||
|  | @ -107,9 +107,6 @@ export default class ScrollableFullScreen { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Destroy() { | ||||
|         this._fullscreencomponent.Destroy() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Actually show this in the 'fullscreen'-div | ||||
|  | @ -163,7 +160,4 @@ export default class ScrollableFullScreen { | |||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     static ActivateCurrent() { | ||||
|         ScrollableFullScreen._currentlyOpen?.Activate() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,9 @@ import BaseUIElement from "../BaseUIElement" | |||
| import { VariableUiElement } from "./VariableUIElement" | ||||
| 
 | ||||
| export class TabbedComponent extends Combine { | ||||
|     /** | ||||
|      * @deprecated | ||||
|      */ | ||||
|     constructor( | ||||
|         elements: { header: BaseUIElement | string; content: BaseUIElement | string }[], | ||||
|         openedTab: UIEventSource<number> | number = 0, | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ export default class CopyrightPanel extends Combine { | |||
| 
 | ||||
|     constructor(state: { | ||||
|         layout: LayoutConfig | ||||
|         bounds: Store<BBox> | ||||
|         mapProperties: { bounds: Store<BBox> } | ||||
|         osmConnection: OsmConnection | ||||
|         dataIsLoading: Store<boolean> | ||||
|         perLayer: ReadonlyMap<string, GeoIndexedStore> | ||||
|  | @ -121,7 +121,7 @@ export default class CopyrightPanel extends Combine { | |||
|         const t = Translations.t.general.attribution | ||||
|         const layoutToUse = state.layout | ||||
| 
 | ||||
|         const iconAttributions: BaseUIElement[] = layoutToUse.usedImages.map( | ||||
|         const iconAttributions: BaseUIElement[] = Utils.Dedup(layoutToUse.usedImages).map( | ||||
|             CopyrightPanel.IconAttribution | ||||
|         ) | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ | |||
|     fieldTypes[field.name + "}"] = field.type; | ||||
|     const src = new UIEventSource<string>(initialState[field.name] ?? ""); | ||||
|     fieldValues[field.name + "}"] = src; | ||||
|     onDestroy(src.addCallback(v => { | ||||
|     onDestroy(src.addCallback(() => { | ||||
|       setFields(); | ||||
|     })); | ||||
|   } | ||||
|  |  | |||
|  | @ -1,131 +0,0 @@ | |||
| import ThemeIntroductionPanel from "./ThemeIntroductionPanel" | ||||
| import Svg from "../../Svg" | ||||
| import Translations from "../i18n/Translations" | ||||
| import ShareScreen from "./ShareScreen" | ||||
| import MoreScreen from "./MoreScreen" | ||||
| import Constants from "../../Models/Constants" | ||||
| import Combine from "../Base/Combine" | ||||
| import { TabbedComponent } from "../Base/TabbedComponent" | ||||
| import { UIEventSource } from "../../Logic/UIEventSource" | ||||
| import UserDetails, { OsmConnection } from "../../Logic/Osm/OsmConnection" | ||||
| import ScrollableFullScreen from "../Base/ScrollableFullScreen" | ||||
| import BaseUIElement from "../BaseUIElement" | ||||
| import Toggle from "../Input/Toggle" | ||||
| import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" | ||||
| import { Utils } from "../../Utils" | ||||
| import UserRelatedState from "../../Logic/State/UserRelatedState" | ||||
| import Loc from "../../Models/Loc" | ||||
| import FilteredLayer from "../../Models/FilteredLayer" | ||||
| import Hotkeys from "../Base/Hotkeys" | ||||
| 
 | ||||
| export default class FullWelcomePaneWithTabs extends ScrollableFullScreen { | ||||
|     public static MoreThemesTabIndex = 1 | ||||
| 
 | ||||
|     constructor( | ||||
|         isShown: UIEventSource<boolean>, | ||||
|         currentTab: UIEventSource<number>, | ||||
|         state: { | ||||
|             layoutToUse: LayoutConfig | ||||
|             osmConnection: OsmConnection | ||||
|             featureSwitchShareScreen: UIEventSource<boolean> | ||||
|             featureSwitchMoreQuests: UIEventSource<boolean> | ||||
|             locationControl: UIEventSource<Loc> | ||||
|             featurePipeline: FeaturePipeline | ||||
|             backgroundLayer: UIEventSource<BaseLayer> | ||||
|             filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|         } & UserRelatedState, | ||||
|         guistate?: { userInfoIsOpened: UIEventSource<boolean> } | ||||
|     ) { | ||||
|         const layoutToUse = state.layoutToUse | ||||
|         super( | ||||
|             () => layoutToUse.title.Clone(), | ||||
|             () => FullWelcomePaneWithTabs.GenerateContents(state, currentTab, isShown, guistate), | ||||
|             "welcome", | ||||
|             isShown | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     private static ConstructBaseTabs( | ||||
|         state: { | ||||
|             layoutToUse: LayoutConfig | ||||
|             osmConnection: OsmConnection | ||||
|             featureSwitchShareScreen: UIEventSource<boolean> | ||||
|             featureSwitchMoreQuests: UIEventSource<boolean> | ||||
|             featurePipeline: FeaturePipeline | ||||
|             locationControl: UIEventSource<Loc> | ||||
|             backgroundLayer: UIEventSource<BaseLayer> | ||||
|             filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|         } & UserRelatedState, | ||||
|         isShown: UIEventSource<boolean>, | ||||
|         currentTab: UIEventSource<number>, | ||||
|         guistate?: { userInfoIsOpened: UIEventSource<boolean> } | ||||
|     ): { header: string | BaseUIElement; content: BaseUIElement }[] { | ||||
|         const tabs: { header: string | BaseUIElement; content: BaseUIElement }[] = [ | ||||
|             { | ||||
|                 header: `<img src='${state.layoutToUse.icon}'>`, | ||||
|                 content: new ThemeIntroductionPanel(isShown, currentTab, state, guistate), | ||||
|             }, | ||||
|         ] | ||||
| 
 | ||||
|         if (state.featureSwitchMoreQuests.data) { | ||||
|             tabs.push({ | ||||
|                 header: Svg.add_img, | ||||
|                 content: new Combine([ | ||||
|                     Translations.t.general.morescreen.intro, | ||||
|                     new MoreScreen(state), | ||||
|                 ]).SetClass("flex flex-col"), | ||||
|             }) | ||||
|         } | ||||
| 
 | ||||
|         if (state.featureSwitchShareScreen.data) { | ||||
|             tabs.push({ header: Svg.share_img, content: new ShareScreen(state) }) | ||||
|         } | ||||
| 
 | ||||
|         return tabs | ||||
|     } | ||||
| 
 | ||||
|     private static GenerateContents( | ||||
|         state: { | ||||
|             layoutToUse: LayoutConfig | ||||
|             osmConnection: OsmConnection | ||||
|             featureSwitchShareScreen: UIEventSource<boolean> | ||||
|             featureSwitchMoreQuests: UIEventSource<boolean> | ||||
|             featurePipeline: FeaturePipeline | ||||
|             locationControl: UIEventSource<Loc> | ||||
|             backgroundLayer: UIEventSource<BaseLayer> | ||||
|             filteredLayers: UIEventSource<FilteredLayer[]> | ||||
|         } & UserRelatedState, | ||||
|         currentTab: UIEventSource<number>, | ||||
|         isShown: UIEventSource<boolean>, | ||||
|         guistate?: { userInfoIsOpened: UIEventSource<boolean> } | ||||
|     ) { | ||||
|         const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate) | ||||
|         const tabsWithAboutMc = [ | ||||
|             ...FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate), | ||||
|         ] | ||||
| 
 | ||||
|         tabsWithAboutMc.push({ | ||||
|             header: Svg.help, | ||||
|             content: new Combine([ | ||||
|                 Translations.t.general.aboutMapcomplete.Subs({ | ||||
|                     osmcha_link: Utils.OsmChaLinkFor(7), | ||||
|                 }), | ||||
|                 "<br/>Version " + Constants.vNumber, | ||||
|                 Hotkeys.generateDocumentationDynamic(), | ||||
|             ]).SetClass("link-underline"), | ||||
|         }) | ||||
| 
 | ||||
|         tabs.forEach((c) => c.content.SetClass("p-4")) | ||||
|         tabsWithAboutMc.forEach((c) => c.content.SetClass("p-4")) | ||||
| 
 | ||||
|         return new Toggle( | ||||
|             new TabbedComponent(tabsWithAboutMc, currentTab), | ||||
|             new TabbedComponent(tabs, currentTab), | ||||
|             state.osmConnection.userDetails.map( | ||||
|                 (userdetails: UserDetails) => | ||||
|                     userdetails.loggedIn && | ||||
|                     userdetails.csCount >= Constants.userJourney.mapCompleteHelpUnlock | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | @ -33,9 +33,3 @@ export interface PresetInfo extends PresetConfig { | |||
|     layerToAddTo: FilteredLayer | ||||
|     boundsFactor?: 0.25 | number | ||||
| } | ||||
| 
 | ||||
| export default class SimpleAddUI extends Combine { | ||||
|     constructor(state: SpecialVisualizationState) { | ||||
|         super([]) | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										143
									
								
								UI/DefaultGUI.ts
									
										
									
									
									
								
							
							
						
						
									
										143
									
								
								UI/DefaultGUI.ts
									
										
									
									
									
								
							|  | @ -1,33 +1,15 @@ | |||
| import FeaturePipelineState from "../Logic/State/FeaturePipelineState" | ||||
| import { Utils } from "../Utils" | ||||
| import { UIEventSource } from "../Logic/UIEventSource" | ||||
| import FullWelcomePaneWithTabs from "./BigComponents/FullWelcomePaneWithTabs" | ||||
| import MapControlButton from "./MapControlButton" | ||||
| import Svg from "../Svg" | ||||
| import Toggle from "./Input/Toggle" | ||||
| import BaseUIElement from "./BaseUIElement" | ||||
| import LeftControls from "./BigComponents/LeftControls" | ||||
| import RightControls from "./BigComponents/RightControls" | ||||
| import CenterMessageBox from "./CenterMessageBox" | ||||
| import ScrollableFullScreen from "./Base/ScrollableFullScreen" | ||||
| import Translations from "./i18n/Translations" | ||||
| import SimpleAddUI from "./BigComponents/SimpleAddUI" | ||||
| import StrayClickHandler from "../Logic/Actors/StrayClickHandler" | ||||
| import { DefaultGuiState } from "./DefaultGuiState" | ||||
| import NewNoteUi from "./Popup/NewNoteUi" | ||||
| import Combine from "./Base/Combine" | ||||
| import FilteredLayer from "../Models/FilteredLayer" | ||||
| import ExtraLinkButton from "./BigComponents/ExtraLinkButton" | ||||
| import { VariableUiElement } from "./Base/VariableUIElement" | ||||
| import Img from "./Base/Img" | ||||
| import UserInformationPanel from "./BigComponents/UserInformation" | ||||
| import { LoginToggle } from "./Popup/LoginButton" | ||||
| import { FixedUiElement } from "./Base/FixedUiElement" | ||||
| import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler" | ||||
| import Hotkeys from "./Base/Hotkeys" | ||||
| import CopyrightPanel from "./BigComponents/CopyrightPanel" | ||||
| import SvelteUIElement from "./Base/SvelteUIElement" | ||||
| import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte" | ||||
| 
 | ||||
| /** | ||||
|  * The default MapComplete GUI initializer | ||||
|  | @ -36,17 +18,14 @@ import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte" | |||
|  */ | ||||
| export default class DefaultGUI { | ||||
|     private readonly guiState: DefaultGuiState | ||||
|     private readonly state: FeaturePipelineState | ||||
|     private readonly geolocationHandler: GeoLocationHandler | undefined | ||||
| 
 | ||||
|     constructor(state: FeaturePipelineState, guiState: DefaultGuiState) { | ||||
|         this.state = state | ||||
|     constructor(guiState: DefaultGuiState) { | ||||
|         this.guiState = guiState | ||||
|     } | ||||
| 
 | ||||
|     public setup() { | ||||
|         this.SetupUIElements() | ||||
|         this.SetupMap() | ||||
| 
 | ||||
|         if ( | ||||
|             this.state.layoutToUse.customCss !== undefined && | ||||
|  | @ -54,131 +33,24 @@ export default class DefaultGUI { | |||
|         ) { | ||||
|             Utils.LoadCustomCss(this.state.layoutToUse.customCss) | ||||
|         } | ||||
| 
 | ||||
|         Hotkeys.RegisterHotkey( | ||||
|             { shift: "O" }, | ||||
|             Translations.t.hotkeyDocumentation.selectMapnik, | ||||
|             () => { | ||||
|                 this.state.backgroundLayer.setData(AvailableBaseLayers.osmCarto) | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     public setupClickDialogOnMap( | ||||
|         filterViewIsOpened: UIEventSource<boolean>, | ||||
|         state: FeaturePipelineState | ||||
|     ) { | ||||
|         const hasPresets = state.layoutToUse.layers.some((layer) => layer.presets.length > 0) | ||||
|         const noteLayer: FilteredLayer = state.filteredLayers.data.filter( | ||||
|             (l) => l.layerDef.id === "note" | ||||
|         )[0] | ||||
|         let addNewNoteDialog: (isShown: UIEventSource<boolean>) => BaseUIElement = undefined | ||||
|         if (noteLayer !== undefined) { | ||||
|             addNewNoteDialog = (isShown) => new NewNoteUi(noteLayer, isShown, state) | ||||
|         } | ||||
| 
 | ||||
|         function setup() { | ||||
|             if (!hasPresets && addNewNoteDialog === undefined) { | ||||
|                 return // nothing to do
 | ||||
|             } | ||||
|             const newPointDialogIsShown = new UIEventSource<boolean>(false) | ||||
|             const addNewPoint = new ScrollableFullScreen( | ||||
|                 () => | ||||
|                     hasPresets | ||||
|                         ? Translations.t.general.add.title | ||||
|                         : Translations.t.notes.createNoteTitle, | ||||
|                 ({ resetScrollSignal }) => { | ||||
|                     let addNew = undefined | ||||
|                     if (hasPresets) { | ||||
|                         addNew = new SimpleAddUI( | ||||
|                             newPointDialogIsShown, | ||||
|                             resetScrollSignal, | ||||
|                             filterViewIsOpened, | ||||
|                             state | ||||
|                         ) | ||||
|                     } | ||||
|                     let addNote = undefined | ||||
|                     if (noteLayer !== undefined) { | ||||
|                         addNote = addNewNoteDialog(newPointDialogIsShown) | ||||
|                     } | ||||
|                     return new Combine([addNew, addNote]).SetClass("flex flex-col font-lg text-lg") | ||||
|                 }, | ||||
|                 "new", | ||||
|                 newPointDialogIsShown | ||||
|             ) | ||||
| 
 | ||||
|             let noteMarker = undefined | ||||
|             if (!hasPresets && addNewNoteDialog !== undefined) { | ||||
|                 noteMarker = new Combine([ | ||||
|                     Svg.note_svg().SetClass("absolute bottom-0").SetStyle("height: 40px"), | ||||
|                     Svg.addSmall_svg() | ||||
|                         .SetClass("absolute w-6 animate-pulse") | ||||
|                         .SetStyle("right: 10px; bottom: -8px;"), | ||||
|                 ]) | ||||
|                     .SetClass("block relative h-full") | ||||
|                     .SetStyle("left: calc( 50% - 15px )") // This is a bit hacky, yes I know!
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (noteLayer !== undefined) { | ||||
|             setup() | ||||
|         } else { | ||||
|             state.featureSwitchAddNew.addCallbackAndRunD((addNewAllowed) => { | ||||
|                 if (addNewAllowed) { | ||||
|                     setup() | ||||
|                     return true | ||||
|                 } | ||||
|             }) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private SetupMap() { | ||||
|         if (Utils.runningFromConsole) { | ||||
|             return | ||||
|         } | ||||
|         const state = this.state | ||||
|         const guiState = this.guiState | ||||
| 
 | ||||
|         this.setupClickDialogOnMap(guiState.filterViewIsOpened, state) | ||||
| 
 | ||||
|         const selectedElement: FilteredLayer = state.filteredLayers.data.filter( | ||||
|             (l) => l.layerDef.id === "selected_element" | ||||
|         )[0] | ||||
|         new ShowDataLayer({ | ||||
|             leafletMap: state.leafletMap, | ||||
|             layerToShow: selectedElement.layerDef, | ||||
|             features: state.selectedElementsLayer, | ||||
|             state, | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     private SetupUIElements() { | ||||
|         const state = this.state | ||||
|         const guiState = this.guiState | ||||
| 
 | ||||
|         const self = this | ||||
| 
 | ||||
|         const extraLink = Toggle.If( | ||||
|             state.featureSwitchExtraLinkEnabled, | ||||
|             () => new ExtraLinkButton(state, state.layoutToUse.extraLink) | ||||
|         ) | ||||
| 
 | ||||
|         const welcomeMessageMapControl = Toggle.If(state.featureSwitchWelcomeMessage, () => | ||||
|             self.InitWelcomeMessage() | ||||
|         ) | ||||
| 
 | ||||
|         new ScrollableFullScreen( | ||||
|             () => Translations.t.general.attribution.attributionTitle, | ||||
|             () => new CopyrightPanel(state), | ||||
|             "copyright", | ||||
|             guiState.copyrightViewIsOpened | ||||
|         ) | ||||
|         const copyright = new MapControlButton(Svg.copyright_svg()).onClick(() => | ||||
|             guiState.copyrightViewIsOpened.setData(true) | ||||
|         ) | ||||
|         new Combine([welcomeMessageMapControl, copyright, extraLink]) | ||||
|             .SetClass("flex flex-col") | ||||
|             .AttachTo("top-left") | ||||
| 
 | ||||
|         new Combine([extraLink]).SetClass("flex flex-col").AttachTo("top-left") | ||||
| 
 | ||||
|         new Combine([ | ||||
|             new ExtraLinkButton(state, { | ||||
|  | @ -198,13 +70,4 @@ export default class DefaultGUI { | |||
|         new CenterMessageBox(state).AttachTo("centermessage") | ||||
|         document?.getElementById("centermessage")?.classList?.add("pointer-events-none") | ||||
|     } | ||||
| 
 | ||||
|     private InitWelcomeMessage(): BaseUIElement { | ||||
|         return new FullWelcomePaneWithTabs( | ||||
|             new UIEventSource<boolean>(false), | ||||
|             this.guiState.welcomeMessageOpenedTab, | ||||
|             this.state, | ||||
|             this.guiState | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -9,7 +9,6 @@ import { Store } from "../../Logic/UIEventSource" | |||
| 
 | ||||
| export default class SingleReview extends Combine { | ||||
|     constructor(review: Review & { madeByLoggedInUser: Store<boolean> }) { | ||||
|         const d = review | ||||
|         const date = new Date(review.iat * 1000) | ||||
|         const reviewAuthor = | ||||
|             review.metadata.nickname ?? | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ | |||
|   import UserRelatedState from "../Logic/State/UserRelatedState"; | ||||
|   import LoginToggle from "./Base/LoginToggle.svelte"; | ||||
|   import LoginButton from "./Base/LoginButton.svelte"; | ||||
|   import CopyrightPanel from "./BigComponents/CopyrightPanel.js"; | ||||
| 
 | ||||
|   export let state: ThemeViewState; | ||||
|   let layout = state.layout; | ||||
|  | @ -149,6 +150,12 @@ | |||
|           <RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker> | ||||
|         </If> | ||||
|       </div> | ||||
|        | ||||
|       <div slot="title2"> | ||||
|         <Tr t={Translations.t.general.attribution.title}/> | ||||
|       </div> | ||||
|        | ||||
|       <ToSvelte slot="content2" construct={() => new CopyrightPanel(state)}></ToSvelte> | ||||
|     </TabbedGroup> | ||||
|   </FloatOver> | ||||
| </If> | ||||
|  |  | |||
							
								
								
									
										6
									
								
								Utils.ts
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								Utils.ts
									
										
									
									
									
								
							|  | @ -308,6 +308,12 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be | |||
|         return str | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a new array with all elements from 'arr' in such a way that every element will be kept only once | ||||
|      * Elements are returned in the same order as they appear in the lists | ||||
|      * @param arr | ||||
|      * @constructor | ||||
|      */ | ||||
|     public static Dedup(arr: string[]): string[] { | ||||
|         if (arr === undefined) { | ||||
|             return undefined | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ | |||
|           "then": { | ||||
|             "special": { | ||||
|               "type": "link", | ||||
|               "href":  "{_backend}/messages/inbox", | ||||
|               "href": "{_backend}/messages/inbox", | ||||
|               "text": { | ||||
|                 "en": "Open your inbox", | ||||
|                 "nl": "Ga naar je inbox" | ||||
|  | @ -393,4 +393,4 @@ | |||
|     } | ||||
|   ], | ||||
|   "mapRendering": null | ||||
| } | ||||
| } | ||||
|  | @ -2019,4 +2019,4 @@ | |||
|       "pl": "Nazwa sieci to <b>{internet_access:ssid}</b>" | ||||
|     } | ||||
|   } | ||||
| } | ||||
| } | ||||
|  | @ -146,6 +146,7 @@ | |||
|             "openMapillary": "Open Mapillary here", | ||||
|             "openOsmcha": "See latest edits made with {theme}", | ||||
|             "themeBy": "Theme maintained by {author}", | ||||
|             "title": "Copyright and attribution", | ||||
|             "translatedBy": "MapComplete has been translated by {contributors} and <a href='https://github.com/pietervdvn/MapComplete/graphs/contributors' target='_blank'>{hiddenCount} more contributors</a>" | ||||
|         }, | ||||
|         "back": "Back", | ||||
|  |  | |||
							
								
								
									
										3
									
								
								test.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								test.ts
									
										
									
									
									
								
							|  | @ -15,8 +15,6 @@ async function main() { | |||
|     const state = new ThemeViewState(layout) | ||||
| 
 | ||||
|     const main = new SvelteUIElement(ThemeViewGUI, { state }) | ||||
|     state.guistate.menuIsOpened.setData(true) | ||||
|     state.guistate.menuViewTab.setData("settings") | ||||
|     main.AttachTo("maindiv") | ||||
| } | ||||
| 
 | ||||
|  | @ -24,7 +22,6 @@ async function testspecial() { | |||
|     const layout = new LayoutConfig(<any>theme, true) // qp.data === "" ?  : new AllKnownLayoutsLazy().get(qp.data)
 | ||||
|     const state = new ThemeViewState(layout) | ||||
| 
 | ||||
|     state.guistate.openUsersettings("picture-license") | ||||
|     const all = SpecialVisualizations.specialVisualizations.map((s) => | ||||
|         SpecialVisualizations.renderExampleOfSpecial(state, s) | ||||
|     ) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue