Merge pull request #1223 from pietervdvn/fix/1219

Fix/1219
This commit is contained in:
Pieter Vander Vennet 2023-01-05 01:12:31 +01:00 committed by GitHub
commit a2a4071022
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 276 additions and 134 deletions

View file

@ -45,20 +45,6 @@ export default class SelectedFeatureHandler {
const self = this const self = this
hash.addCallback(() => self.setSelectedElementFromHash()) hash.addCallback(() => self.setSelectedElementFromHash())
state.featurePipeline?.newDataLoadedSignal?.addCallbackAndRunD((_) => {
// New data was loaded. In initial startup, the hash might be set (via the URL) but might not be selected yet
if (hash.data === undefined || SelectedFeatureHandler._no_trigger_on.has(hash.data)) {
// This is an invalid hash anyway
return
}
if (state.selectedElement.data !== undefined) {
// We already have something selected
return
}
self.setSelectedElementFromHash()
})
this.initialLoad() this.initialLoad()
} }

View file

@ -62,7 +62,7 @@ export class OsmConnection {
private readonly _singlePage: boolean private readonly _singlePage: boolean
private isChecking = false private isChecking = false
constructor(options: { constructor(options?: {
dryRun?: UIEventSource<boolean> dryRun?: UIEventSource<boolean>
fakeUser?: false | boolean fakeUser?: false | boolean
oauth_token?: UIEventSource<string> oauth_token?: UIEventSource<string>
@ -71,6 +71,7 @@ export class OsmConnection {
osmConfiguration?: "osm" | "osm-test" osmConfiguration?: "osm" | "osm-test"
attemptLogin?: true | boolean attemptLogin?: true | boolean
}) { }) {
options = options ?? {}
this.fakeUser = options.fakeUser ?? false this.fakeUser = options.fakeUser ?? false
this._singlePage = options.singlePage ?? true this._singlePage = options.singlePage ?? true
this._oauth_config = this._oauth_config =

View file

@ -230,10 +230,12 @@ export abstract class Store<T> {
const newSource = new UIEventSource<T>(this.data) const newSource = new UIEventSource<T>(this.data)
const self = this
this.addCallback((latestData) => { this.addCallback((latestData) => {
window.setTimeout(() => { window.setTimeout(() => {
if (this.data == latestData) { if (self.data == latestData) {
// compare by reference // compare by reference.
// Note that 'latestData' and 'self.data' are both from the same UIEVentSource, but both are dereferenced at a different time
newSource.setData(latestData) newSource.setData(latestData)
} }
}, millisToStabilize) }, millisToStabilize)

View file

@ -414,6 +414,7 @@ export default class MinimapImplementation extends BaseUIElement implements Mini
map.on("contextmenu", function (e) { map.on("contextmenu", function (e) {
// @ts-ignore // @ts-ignore
lastClickLocation?.setData({ lat: e.latlng.lat, lon: e.latlng.lng }) lastClickLocation?.setData({ lat: e.latlng.lat, lon: e.latlng.lng })
map.setZoom(map.getZoom() + 1)
}) })
} }

View file

@ -37,12 +37,13 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
featurePipeline: FeaturePipeline featurePipeline: FeaturePipeline
backgroundLayer: UIEventSource<BaseLayer> backgroundLayer: UIEventSource<BaseLayer>
filteredLayers: UIEventSource<FilteredLayer[]> filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState } & UserRelatedState,
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
) { ) {
const layoutToUse = state.layoutToUse const layoutToUse = state.layoutToUse
super( super(
() => layoutToUse.title.Clone(), () => layoutToUse.title.Clone(),
() => FullWelcomePaneWithTabs.GenerateContents(state, currentTab, isShown), () => FullWelcomePaneWithTabs.GenerateContents(state, currentTab, isShown, guistate),
"welcome", "welcome",
isShown isShown
) )
@ -60,12 +61,13 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
filteredLayers: UIEventSource<FilteredLayer[]> filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState, } & UserRelatedState,
isShown: UIEventSource<boolean>, isShown: UIEventSource<boolean>,
currentTab: UIEventSource<number> currentTab: UIEventSource<number>,
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
): { header: string | BaseUIElement; content: BaseUIElement }[] { ): { header: string | BaseUIElement; content: BaseUIElement }[] {
const tabs: { header: string | BaseUIElement; content: BaseUIElement }[] = [ const tabs: { header: string | BaseUIElement; content: BaseUIElement }[] = [
{ {
header: `<img src='${state.layoutToUse.icon}'>`, header: `<img src='${state.layoutToUse.icon}'>`,
content: new ThemeIntroductionPanel(isShown, currentTab, state), content: new ThemeIntroductionPanel(isShown, currentTab, state, guistate),
}, },
] ]
@ -113,11 +115,12 @@ export default class FullWelcomePaneWithTabs extends ScrollableFullScreen {
filteredLayers: UIEventSource<FilteredLayer[]> filteredLayers: UIEventSource<FilteredLayer[]>
} & UserRelatedState, } & UserRelatedState,
currentTab: UIEventSource<number>, currentTab: UIEventSource<number>,
isShown: UIEventSource<boolean> isShown: UIEventSource<boolean>,
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
) { ) {
const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab) const tabs = FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate)
const tabsWithAboutMc = [ const tabsWithAboutMc = [
...FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab), ...FullWelcomePaneWithTabs.ConstructBaseTabs(state, isShown, currentTab, guistate),
] ]
tabsWithAboutMc.push({ tabsWithAboutMc.push({

View file

@ -9,6 +9,7 @@ import Svg from "../../Svg"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig" import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import FullWelcomePaneWithTabs from "./FullWelcomePaneWithTabs" import FullWelcomePaneWithTabs from "./FullWelcomePaneWithTabs"
import LoggedInUserIndicator from "../LoggedInUserIndicator"
export default class ThemeIntroductionPanel extends Combine { export default class ThemeIntroductionPanel extends Combine {
constructor( constructor(
@ -20,7 +21,8 @@ export default class ThemeIntroductionPanel extends Combine {
featureSwitchUserbadge: UIEventSource<boolean> featureSwitchUserbadge: UIEventSource<boolean>
layoutToUse: LayoutConfig layoutToUse: LayoutConfig
osmConnection: OsmConnection osmConnection: OsmConnection
} },
guistate?: { userInfoIsOpened: UIEventSource<boolean> }
) { ) {
const t = Translations.t.general const t = Translations.t.general
const layout = state.layoutToUse const layout = state.layoutToUse
@ -36,9 +38,18 @@ export default class ThemeIntroductionPanel extends Combine {
}) })
.SetClass("only-on-mobile") .SetClass("only-on-mobile")
const loggedInUserInfo = new LoggedInUserIndicator(state.osmConnection, {
firstLine: Translations.t.general.welcomeBack.Clone(),
})
if (guistate?.userInfoIsOpened) {
loggedInUserInfo.onClick(() => {
guistate.userInfoIsOpened.setData(true)
})
}
const loginStatus = new Toggle( const loginStatus = new Toggle(
new LoginToggle( new LoginToggle(
undefined, loggedInUserInfo,
new Combine([ new Combine([
Translations.t.general.loginWithOpenStreetMap.SetClass("text-xl font-bold"), Translations.t.general.loginWithOpenStreetMap.SetClass("text-xl font-bold"),
Translations.t.general.loginOnlyNeededToEdit.Clone().SetClass("font-bold"), Translations.t.general.loginOnlyNeededToEdit.Clone().SetClass("font-bold"),
@ -60,7 +71,7 @@ export default class ThemeIntroductionPanel extends Combine {
]).SetClass("flex flex-col mt-2"), ]).SetClass("flex flex-col mt-2"),
toTheMap, toTheMap,
loginStatus.SetClass("block"), loginStatus.SetClass("block mt-6 pt-2 md:border-t-2 border-dotted border-gray-400"),
layout.descriptionTail?.Clone().SetClass("block mt-4"), layout.descriptionTail?.Clone().SetClass("block mt-4"),
languagePicker?.SetClass("block mt-4"), languagePicker?.SetClass("block mt-4"),

View file

@ -140,12 +140,17 @@ class UserInformationMainPanel extends Combine {
} }
export default class UserInformationPanel extends ScrollableFullScreen { export default class UserInformationPanel extends ScrollableFullScreen {
constructor(state: { constructor(
layoutToUse: LayoutConfig state: {
osmConnection: OsmConnection layoutToUse: LayoutConfig
locationControl: UIEventSource<Loc> osmConnection: OsmConnection
}) { locationControl: UIEventSource<Loc>
const isOpened = new UIEventSource<boolean>(false) },
options?: {
isOpened?: UIEventSource<boolean>
}
) {
const isOpened = options?.isOpened ?? new UIEventSource<boolean>(false)
super( super(
() => { () => {
return new VariableUiElement( return new VariableUiElement(

View file

@ -33,7 +33,6 @@ import GeoLocationHandler from "../Logic/Actors/GeoLocationHandler"
import { GeoLocationState } from "../Logic/State/GeoLocationState" import { GeoLocationState } from "../Logic/State/GeoLocationState"
import Hotkeys from "./Base/Hotkeys" import Hotkeys from "./Base/Hotkeys"
import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers" import AvailableBaseLayers from "../Logic/Actors/AvailableBaseLayers"
import { Translation } from "./i18n/Translation"
/** /**
* The default MapComplete GUI initializer * The default MapComplete GUI initializer
@ -205,7 +204,9 @@ export default class DefaultGUI {
const self = this const self = this
new Combine([ new Combine([
Toggle.If(state.featureSwitchUserbadge, () => { Toggle.If(state.featureSwitchUserbadge, () => {
const userInfo = new UserInformationPanel(state) const userInfo = new UserInformationPanel(state, {
isOpened: guiState.userInfoIsOpened,
})
const mapControl = new MapControlButton( const mapControl = new MapControlButton(
new VariableUiElement( new VariableUiElement(
@ -219,7 +220,7 @@ export default class DefaultGUI {
{ {
dontStyle: true, dontStyle: true,
} }
).onClick(() => userInfo.Activate()) ).onClick(() => guiState.userInfoIsOpened.setData(true))
return new LoginToggle( return new LoginToggle(
mapControl, mapControl,
@ -292,7 +293,12 @@ export default class DefaultGUI {
private InitWelcomeMessage(): BaseUIElement { private InitWelcomeMessage(): BaseUIElement {
const isOpened = this.guiState.welcomeMessageIsOpened const isOpened = this.guiState.welcomeMessageIsOpened
new FullWelcomePaneWithTabs(isOpened, this.guiState.welcomeMessageOpenedTab, this.state) new FullWelcomePaneWithTabs(
isOpened,
this.guiState.welcomeMessageOpenedTab,
this.state,
this.guiState
)
// ?-Button on Desktop, opens panel with close-X. // ?-Button on Desktop, opens panel with close-X.
const help = new MapControlButton(Svg.help_svg()) const help = new MapControlButton(Svg.help_svg())

View file

@ -9,6 +9,7 @@ export class DefaultGuiState {
public readonly filterViewIsOpened: UIEventSource<boolean> public readonly filterViewIsOpened: UIEventSource<boolean>
public readonly copyrightViewIsOpened: UIEventSource<boolean> public readonly copyrightViewIsOpened: UIEventSource<boolean>
public readonly currentViewControlIsOpened: UIEventSource<boolean> public readonly currentViewControlIsOpened: UIEventSource<boolean>
public readonly userInfoIsOpened: UIEventSource<boolean>
public readonly welcomeMessageOpenedTab: UIEventSource<number> public readonly welcomeMessageOpenedTab: UIEventSource<number>
public readonly allFullScreenStates: UIEventSource<boolean>[] = [] public readonly allFullScreenStates: UIEventSource<boolean>[] = []
@ -43,8 +44,14 @@ export class DefaultGuiState {
this.currentViewControlIsOpened = QueryParameters.GetBooleanQueryParameter( this.currentViewControlIsOpened = QueryParameters.GetBooleanQueryParameter(
"currentview-toggle", "currentview-toggle",
false, false,
"Whether or not the current view box is shown" "Whether or not the current view box is shown (metalayer showing current view, allows to do calculate stats for all in view)"
) )
this.userInfoIsOpened = QueryParameters.GetBooleanQueryParameter(
"userinfo-toggle",
false,
"Whether or not the user info is shown"
)
const states = { const states = {
download: this.downloadControlIsOpened, download: this.downloadControlIsOpened,
filters: this.filterViewIsOpened, filters: this.filterViewIsOpened,
@ -66,7 +73,8 @@ export class DefaultGuiState {
this.filterViewIsOpened, this.filterViewIsOpened,
this.copyrightViewIsOpened, this.copyrightViewIsOpened,
this.welcomeMessageIsOpened, this.welcomeMessageIsOpened,
this.currentViewControlIsOpened this.currentViewControlIsOpened,
this.userInfoIsOpened
) )
for (let i = 0; i < this.allFullScreenStates.length; i++) { for (let i = 0; i < this.allFullScreenStates.length; i++) {

View file

@ -0,0 +1,42 @@
import { VariableUiElement } from "./Base/VariableUIElement"
import { OsmConnection } from "../Logic/Osm/OsmConnection"
import Svg from "../Svg"
import Img from "./Base/Img"
import Combine from "./Base/Combine"
import { FixedUiElement } from "./Base/FixedUiElement"
import BaseUIElement from "./BaseUIElement"
export default class LoggedInUserIndicator extends VariableUiElement {
constructor(
osmConnection: OsmConnection,
options?: {
size?: "small" | "medium" | "large"
firstLine?: BaseUIElement
}
) {
options = options ?? {}
let size = "w-8 h-8 mr-2"
if (options.size == "medium") {
size = "w-16 h-16 mr-4"
} else if (options.size == "large") {
size = "w-32 h-32 mr-6"
}
super(
osmConnection.userDetails.mapD((ud) => {
let img = Svg.person_svg().SetClass(
"rounded-full border border-black overflow-hidden"
)
if (ud.img) {
img = new Img(ud.img)
}
let contents: BaseUIElement = new FixedUiElement(ud.name).SetClass("font-bold")
if (options?.firstLine) {
contents = new Combine([options.firstLine, contents]).SetClass("flex flex-col")
}
return new Combine([img.SetClass("rounded-full " + size), contents]).SetClass(
"flex items-center"
)
})
)
}
}

View file

@ -176,6 +176,13 @@ export default class DeleteWizard extends Toggle {
undefined, undefined,
isShown isShown
) )
const self = this
confirm.addCallbackAndRunD((dialogIsOpened) => {
if (dialogIsOpened) {
self.ScrollIntoView()
}
})
} }
private static constructConfirmButton( private static constructConfirmButton(

View file

@ -284,5 +284,13 @@ export default class MoveWizard extends Toggle {
]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200"), ]).SetClass("flex m-2 p-2 rounded-lg bg-gray-200"),
moveDisallowedReason.map((r) => r === undefined) moveDisallowedReason.map((r) => r === undefined)
) )
const self = this
currentStep.addCallback((state) => {
if (state === "start") {
return
}
self.ScrollIntoView()
})
} }
} }

View file

@ -24,6 +24,7 @@ import BaseLayer from "../../Models/BaseLayer"
import FilteredLayer from "../../Models/FilteredLayer" import FilteredLayer from "../../Models/FilteredLayer"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import { VariableUiElement } from "../Base/VariableUIElement" import { VariableUiElement } from "../Base/VariableUIElement"
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
export default class SplitRoadWizard extends Combine { export default class SplitRoadWizard extends Combine {
// @ts-ignore // @ts-ignore
@ -54,6 +55,7 @@ export default class SplitRoadWizard extends Combine {
changes: Changes changes: Changes
layoutToUse: LayoutConfig layoutToUse: LayoutConfig
allElements: ElementStorage allElements: ElementStorage
selectedElement: UIEventSource<any>
} }
) { ) {
const t = Translations.t.split const t = Translations.t.split
@ -79,9 +81,6 @@ export default class SplitRoadWizard extends Combine {
hasBeenSplit hasBeenSplit
) )
) )
splitButton.onClick(() => {
splitClicked.setData(true)
})
// Only show the splitButton if logged in, else show login prompt // Only show the splitButton if logged in, else show login prompt
const loginBtn = t.loginToSplit const loginBtn = t.loginToSplit
@ -110,6 +109,9 @@ export default class SplitRoadWizard extends Combine {
// We throw away the old map and splitpoints, and create a new map from scratch // We throw away the old map and splitpoints, and create a new map from scratch
splitPoints.setData([]) splitPoints.setData([])
leafletMap.setData(SplitRoadWizard.setupMapComponent(id, splitPoints, state)) leafletMap.setData(SplitRoadWizard.setupMapComponent(id, splitPoints, state))
// Close the popup. The contributor has to select a segment again to make sure they continue editing the correct segment; see #1219
ScrollableFullScreen.collapse()
}) })
saveButton.SetClass("btn btn-primary mr-3") saveButton.SetClass("btn btn-primary mr-3")
@ -147,6 +149,11 @@ export default class SplitRoadWizard extends Combine {
new Toggle(mapView, splitToggle, splitClicked), new Toggle(mapView, splitToggle, splitClicked),
]) ])
this.dialogIsOpened = splitClicked this.dialogIsOpened = splitClicked
const self = this
splitButton.onClick(() => {
splitClicked.setData(true)
self.ScrollIntoView()
})
} }
private static setupMapComponent( private static setupMapComponent(

View file

@ -4,8 +4,10 @@ import { ShowDataLayerOptions } from "./ShowDataLayerOptions"
import { ElementStorage } from "../../Logic/ElementStorage" import { ElementStorage } from "../../Logic/ElementStorage"
import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource" import RenderingMultiPlexerFeatureSource from "../../Logic/FeatureSource/Sources/RenderingMultiPlexerFeatureSource"
import ScrollableFullScreen from "../Base/ScrollableFullScreen" import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import { LeafletMouseEvent } from "leaflet" import { LeafletMouseEvent, PathOptions } from "leaflet"
import Hash from "../../Logic/Web/Hash" import Hash from "../../Logic/Web/Hash"
import { BBox } from "../../Logic/BBox"
import { Utils } from "../../Utils"
/* /*
// import 'leaflet-polylineoffset'; // import 'leaflet-polylineoffset';
We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object. We don't actually import it here. It is imported in the 'MinimapImplementation'-class, which'll result in a patched 'L' object.
@ -47,6 +49,7 @@ export default class ShowDataLayerImplementation {
string, string,
{ feature: any; activateFunc: (event: LeafletMouseEvent) => void } { feature: any; activateFunc: (event: LeafletMouseEvent) => void }
>() >()
private readonly showDataLayerid: number private readonly showDataLayerid: number
private readonly createPopup: ( private readonly createPopup: (
tags: UIEventSource<any>, tags: UIEventSource<any>,
@ -81,7 +84,7 @@ export default class ShowDataLayerImplementation {
} }
const self = this const self = this
options.leafletMap.addCallback((_) => { options.leafletMap.addCallback(() => {
return self.update(options) return self.update(options)
}) })
@ -112,6 +115,10 @@ export default class ShowDataLayerImplementation {
}) })
this._selectedElement?.addCallbackAndRunD((selected) => { this._selectedElement?.addCallbackAndRunD((selected) => {
if (selected === undefined) {
ScrollableFullScreen.collapse()
return
}
self.openPopupOfSelectedElement(selected) self.openPopupOfSelectedElement(selected)
}) })
@ -171,17 +178,8 @@ export default class ShowDataLayerImplementation {
} }
const self = this const self = this
const data = {
type: "FeatureCollection", this.geoLayer = new L.LayerGroup()
features: [],
}
// @ts-ignore
this.geoLayer = L.geoJSON(data, {
style: (feature) => self.createStyleFor(feature),
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
onEachFeature: (feature, leafletLayer) =>
self.postProcessFeature(feature, leafletLayer),
})
const selfLayer = this.geoLayer const selfLayer = this.geoLayer
const allFeats = this._features.features.data const allFeats = this._features.features.data
@ -189,6 +187,31 @@ export default class ShowDataLayerImplementation {
if (feat === undefined) { if (feat === undefined) {
continue continue
} }
// Why not one geojson layer with _all_ features, and attaching a right-click onto every feature individually?
// Because that somehow doesn't work :(
const feature = feat
const geojsonLayer = L.geoJSON(feature, {
style: (feature) => <PathOptions>self.createStyleFor(feature),
pointToLayer: (feature, latLng) => self.pointToLayer(feature, latLng),
onEachFeature: (feature, leafletLayer) =>
self.postProcessFeature(feature, leafletLayer),
})
if (feature.geometry.type === "Point") {
geojsonLayer.on({
contextmenu: (e) => {
const o = self.leafletLayersPerId.get(feature?.properties?.id)
o?.activateFunc(<LeafletMouseEvent>e)
Utils.preventDefaultOnMouseEvent(e.originalEvent)
},
dblclick: (e) => {
const o = self.leafletLayersPerId.get(feature?.properties?.id)
o?.activateFunc(<LeafletMouseEvent>e)
Utils.preventDefaultOnMouseEvent(e.originalEvent)
},
})
}
this.geoLayer.addLayer(geojsonLayer)
try { try {
if (feat.geometry.type === "LineString") { if (feat.geometry.type === "LineString") {
const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates) const coords = L.GeoJSON.coordsToLatLngs(feat.geometry.coordinates)
@ -229,7 +252,7 @@ export default class ShowDataLayerImplementation {
return self.geoLayer !== selfLayer return self.geoLayer !== selfLayer
}) })
} else { } else {
this.geoLayer.addData(feat) geojsonLayer.addData(feat)
} }
} catch (e) { } catch (e) {
console.error( console.error(
@ -242,14 +265,14 @@ export default class ShowDataLayerImplementation {
} }
} }
if (options.zoomToFeatures ?? false) { if ((options.zoomToFeatures ?? false) && allFeats.length > 0) {
if (this.geoLayer.getLayers().length > 0) { let bound = undefined
try { for (const feat of allFeats) {
const bounds = this.geoLayer.getBounds() const fbound = BBox.get(feat)
mp.fitBounds(bounds, { animate: false }) bound = bound?.unionWith(fbound) ?? fbound
} catch (e) { }
console.debug("Invalid bounds", e) if (bound !== undefined) {
} mp.fitBounds(bound?.toLeaflet(), { animate: false })
} }
} }
@ -312,29 +335,7 @@ export default class ShowDataLayerImplementation {
icon: L.divIcon(style), icon: L.divIcon(style),
}) })
} }
private createActivateFunction(feature, key: string, layer: LayerConfig): (event) => void {
/**
* Post processing - basically adding the popup
* @param feature
* @param leafletLayer
* @private
*/
private postProcessFeature(feature, leafletLayer: L.Evented) {
const layer: LayerConfig = this._layerToShow
if (layer.title === undefined || !this._enablePopups) {
// No popup action defined -> Don't do anything
// or probably a map in the popup - no popups needed!
return
}
const key = feature.properties.id
if (this.leafletLayersPerId.has(key)) {
const activate = this.leafletLayersPerId.get(key)
leafletLayer.addEventListener("click", activate.activateFunc)
if (Hash.hash.data === key) {
activate.activateFunc(null)
}
return
}
let infobox: ScrollableFullScreen = undefined let infobox: ScrollableFullScreen = undefined
const self = this const self = this
@ -354,17 +355,36 @@ export default class ShowDataLayerImplementation {
self._selectedElement.setData( self._selectedElement.setData(
self.allElements.ContainingFeatures.get(feature.id) ?? feature self.allElements.ContainingFeatures.get(feature.id) ?? feature
) )
event?.originalEvent?.preventDefault() }
event?.originalEvent?.stopPropagation() return activate
event?.originalEvent?.stopImmediatePropagation() }
if (event?.originalEvent) { /**
// This is a total workaround, as 'preventDefault' and everything above seems to be not working * Post processing - basically adding the popup
event.originalEvent["dismissed"] = true * @param feature
} * @param leafletLayer
* @private
*/
private postProcessFeature(feature, leafletLayer: L.Evented) {
const layer: LayerConfig = this._layerToShow
if (layer.title === undefined || !this._enablePopups) {
// No popup action defined -> Don't do anything
// or probably a map in the popup - no popups needed!
return
}
const key = feature.properties.id
let activate: (event) => void
if (this.leafletLayersPerId.has(key)) {
activate = this.leafletLayersPerId.get(key).activateFunc
} else {
activate = this.createActivateFunction(feature, key, layer)
} }
leafletLayer.addEventListener("click", activate) // We also have to open on rightclick, doubleclick, ... as users sometimes do this. See #1219
leafletLayer.on({
dblclick: activate,
contextmenu: activate,
click: activate,
})
// Add the feature to the index to open the popup when needed // Add the feature to the index to open the popup when needed
this.leafletLayersPerId.set(key, { this.leafletLayersPerId.set(key, {
feature: feature, feature: feature,

View file

@ -900,7 +900,7 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
url: string, url: string,
maxCacheTimeMs: number, maxCacheTimeMs: number,
headers?: any headers?: any
): Promise<any | { error: string; url: string; statuscode?: number }> { ): Promise<{ content: any } | { error: string; url: string; statuscode?: number }> {
const cached = Utils._download_cache.get(url) const cached = Utils._download_cache.get(url)
if (cached !== undefined) { if (cached !== undefined) {
if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) { if (new Date().getTime() - cached.timestamp <= maxCacheTimeMs) {
@ -1074,6 +1074,16 @@ In the case that MapComplete is pointed to the testing grounds, the edit will be
) )
} }
public static preventDefaultOnMouseEvent(event: any) {
event?.originalEvent?.preventDefault()
event?.originalEvent?.stopPropagation()
event?.originalEvent?.stopImmediatePropagation()
if (event?.originalEvent) {
// This is a total workaround, as 'preventDefault' and everything above seems to be not working
event.originalEvent["dismissed"] = true
}
}
public static OsmChaLinkFor(daysInThePast, theme = undefined): string { public static OsmChaLinkFor(daysInThePast, theme = undefined): string {
const now = new Date() const now = new Date()
const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000) const lastWeek = new Date(now.getTime() - daysInThePast * 24 * 60 * 60 * 1000)

View file

@ -624,6 +624,10 @@ video {
position: relative; position: relative;
} }
.\!relative {
position: relative !important;
}
.sticky { .sticky {
position: -webkit-sticky; position: -webkit-sticky;
position: sticky; position: sticky;
@ -807,18 +811,22 @@ video {
margin-top: 0.25rem; margin-top: 0.25rem;
} }
.mt-4 {
margin-top: 1rem;
}
.ml-3 {
margin-left: 0.75rem;
}
.mr-2 { .mr-2 {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
.mr-4 {
margin-right: 1rem;
}
.mr-6 {
margin-right: 1.5rem;
}
.mt-4 {
margin-top: 1rem;
}
.ml-4 { .ml-4 {
margin-left: 1rem; margin-left: 1rem;
} }
@ -827,10 +835,6 @@ video {
margin-bottom: 6rem; margin-bottom: 6rem;
} }
.mr-4 {
margin-right: 1rem;
}
.mb-2 { .mb-2 {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
@ -839,6 +843,10 @@ video {
margin-left: 0.5rem; margin-left: 0.5rem;
} }
.ml-3 {
margin-left: 0.75rem;
}
.ml-12 { .ml-12 {
margin-left: 3rem; margin-left: 3rem;
} }
@ -960,6 +968,18 @@ video {
height: 16rem; height: 16rem;
} }
.h-8 {
height: 2rem;
}
.h-16 {
height: 4rem;
}
.h-32 {
height: 8rem;
}
.h-10 { .h-10 {
height: 2.5rem; height: 2.5rem;
} }
@ -988,22 +1008,10 @@ video {
height: 1.5rem; height: 1.5rem;
} }
.h-8 {
height: 2rem;
}
.h-32 {
height: 8rem;
}
.h-96 { .h-96 {
height: 24rem; height: 24rem;
} }
.h-16 {
height: 4rem;
}
.h-0 { .h-0 {
height: 0px; height: 0px;
} }
@ -1048,6 +1056,18 @@ video {
width: 1.5rem; width: 1.5rem;
} }
.w-8 {
width: 2rem;
}
.w-16 {
width: 4rem;
}
.w-32 {
width: 8rem;
}
.w-10 { .w-10 {
width: 2.5rem; width: 2.5rem;
} }
@ -1072,10 +1092,6 @@ video {
width: 2.75rem; width: 2.75rem;
} }
.w-8 {
width: 2rem;
}
.w-min { .w-min {
width: -webkit-min-content; width: -webkit-min-content;
width: min-content; width: min-content;
@ -1090,14 +1106,6 @@ video {
width: 24rem; width: 24rem;
} }
.w-32 {
width: 8rem;
}
.w-16 {
width: 4rem;
}
.w-auto { .w-auto {
width: auto; width: auto;
} }
@ -1374,6 +1382,10 @@ video {
border-bottom-width: 1px; border-bottom-width: 1px;
} }
.border-dotted {
border-style: dotted;
}
.border-black { .border-black {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(0 0 0 / var(--tw-border-opacity)); border-color: rgb(0 0 0 / var(--tw-border-opacity));
@ -1864,6 +1876,11 @@ body {
box-sizing: initial !important; box-sizing: initial !important;
} }
.leaflet-marker-icon img {
-webkit-touch-callout: none;
/* prevent callout to copy image, etc when tap to hold */
}
.leaflet-control-attribution { .leaflet-control-attribution {
display: block ruby; display: block ruby;
} }
@ -2738,6 +2755,10 @@ input {
border-radius: 0.75rem; border-radius: 0.75rem;
} }
.md\:border-t-2 {
border-top-width: 2px;
}
.md\:p-1 { .md\:p-1 {
padding: 0.25rem; padding: 0.25rem;
} }

View file

@ -113,6 +113,10 @@ body {
box-sizing: initial !important; box-sizing: initial !important;
} }
.leaflet-marker-icon img {
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
}
.leaflet-control-attribution { .leaflet-control-attribution {
display: block ruby; display: block ruby;
} }

View file

@ -327,7 +327,7 @@
"tuesday": "Tuesday", "tuesday": "Tuesday",
"wednesday": "Wednesday" "wednesday": "Wednesday"
}, },
"welcomeBack": "You are logged in, welcome back!", "welcomeBack": "Welcome back!",
"welcomeExplanation": { "welcomeExplanation": {
"addNew": "Tap the map to add a new POI.", "addNew": "Tap the map to add a new POI.",
"browseMoreMaps": "Discover more maps", "browseMoreMaps": "Discover more maps",