From 12fec3f3122d5b7966141163331acfe95734f50a Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Thu, 20 Jun 2024 15:12:51 +0200 Subject: [PATCH] Core: Don't throw away changes if uploading failed, report errors --- .../mapcomplete-changes.json | 90 +++++++++---- langs/en.json | 1 + package.json | 1 + public/css/index-tailwind-output.css | 8 +- src/Logic/Osm/Changes.ts | 13 +- src/Logic/Osm/ChangesetHandler.ts | 16 ++- src/Models/Constants.ts | 1 + src/Models/ThemeViewState.ts | 126 ++++++++++-------- src/UI/Base/LoginToggle.svelte | 9 +- src/UI/SpecialVisualization.ts | 1 + 10 files changed, 170 insertions(+), 96 deletions(-) diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 71a45eb7f..b4818b4eb 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,13 +1,17 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete" + "en": "Changes made with MapComplete", + "de": "Änderungen mit MapComplete" }, "shortDescription": { - "en": "Shows changes made by MapComplete" + "en": "Shows changes made by MapComplete", + "de": "Änderungen von MapComplete anzeigen" }, "description": { - "en": "This maps shows all the changes made with MapComplete" + "en": "This maps shows all the changes made with MapComplete", + "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", + "pl": "Ta mapa pokazuje wszystkie zmiany wprowadzone za pomocą MapComplete" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -18,7 +22,9 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers" + "en": "Changeset centers", + "de": "Zentrum der Änderungssätze", + "zh_Hant": "變更集中心" }, "minzoom": 0, "source": { @@ -28,41 +34,48 @@ }, "title": { "render": { - "en": "Changeset for {theme}" + "en": "Changeset for {theme}", + "de": "Änderungssatz für {theme}" } }, "description": { - "en": "Shows all MapComplete changes" + "en": "Shows all MapComplete changes", + "de": "Alle MapComplete-Änderungen anzeigen" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}" + "en": "Changeset {id}", + "de": "Änderungssatz {id}" } }, { "id": "contributor", "question": { - "en": "What contributor did make this change?" + "en": "What contributor did make this change?", + "de": "Welcher Mitwirkende hat diese Änderung vorgenommen?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}" + "en": "Change made by {user}", + "de": "Änderung vorgenommen von {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?" + "en": "What theme was used to make this change?", + "de": "Welches Thema wurde für die Änderung verwendet?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}" + "en": "Change with theme {theme}", + "de": "Geändert mit Thema {theme}" } }, { @@ -71,19 +84,23 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?" + "en": "What locale (language) was this change made in?", + "de": "In welcher Benutzersprache wurde die Änderung vorgenommen?" }, "render": { - "en": "User locale is {locale}" + "en": "User locale is {locale}", + "de": "Benutzersprache {locale}" } }, { "id": "host", "render": { - "en": "Change with with {host}" + "en": "Change with with {host}", + "de": "Änderung über {host}" }, "question": { - "en": "What host (website) was this change made with?" + "en": "What host (website) was this change made with?", + "de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?" }, "freeform": { "key": "host" @@ -104,10 +121,12 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?" + "en": "What version of MapComplete was used to make this change?", + "de": "Mit welcher MapComplete Version wurde die Änderung vorgenommen?" }, "render": { - "en": "Made with {editor}" + "en": "Made with {editor}", + "de": "Erstellt mit {editor}" }, "freeform": { "key": "editor" @@ -493,7 +512,9 @@ } ], "question": { - "en": "Themename contains {search}" + "en": "Themename contains {search}", + "de": "Themenname enthält {search}", + "pl": "Nazwa tematu zawiera {search}" } } ] @@ -509,7 +530,8 @@ } ], "question": { - "en": "Themename does not contain {search}" + "en": "Themename does not contain {search}", + "de": "Themename enthält not {search}" } } ] @@ -525,7 +547,8 @@ } ], "question": { - "en": "Made by contributor {search}" + "en": "Made by contributor {search}", + "de": "Erstellt vom Mitwirkenden {search}" } } ] @@ -541,7 +564,8 @@ } ], "question": { - "en": "Not made by contributor {search}" + "en": "Not made by contributor {search}", + "de": "Nicht erstellt von Mitwirkendem {search}" } } ] @@ -558,7 +582,8 @@ } ], "question": { - "en": "Made before {search}" + "en": "Made before {search}", + "de": "Erstellt vor {search}" } } ] @@ -575,7 +600,8 @@ } ], "question": { - "en": "Made after {search}" + "en": "Made after {search}", + "de": "Erstellt nach {search}" } } ] @@ -591,7 +617,8 @@ } ], "question": { - "en": "User language (iso-code) {search}" + "en": "User language (iso-code) {search}", + "de": "Benutzersprache (ISO-Code) {search}" } } ] @@ -607,7 +634,8 @@ } ], "question": { - "en": "Made with host {search}" + "en": "Made with host {search}", + "de": "Erstellt mit Host {search}" } } ] @@ -618,7 +646,8 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image" + "en": "Changeset added at least one image", + "de": "Änderungssatz hat mindestens ein Bild hinzugefügt" } } ] @@ -629,7 +658,8 @@ { "osmTags": "theme!=grb", "question": { - "en": "Exclude GRB theme" + "en": "Exclude GRB theme", + "de": "GRB-Thema ausschließen" } } ] @@ -640,7 +670,8 @@ { "osmTags": "theme!=etymology", "question": { - "en": "Exclude etymology theme" + "en": "Exclude etymology theme", + "de": "Etymologie-Thema ausschließen" } } ] @@ -655,7 +686,8 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here" + "en": "More statistics can be found here", + "de": "Weitere Statistiken gibt es hier" } }, { diff --git a/langs/en.json b/langs/en.json index edcc20e7b..88cfb2e3b 100644 --- a/langs/en.json +++ b/langs/en.json @@ -288,6 +288,7 @@ "loadingTheme": "Loading {theme}…", "loginFailed": "Logging in into OpenStreetMap failed", "loginFailedOfflineMode": "OpenStreetMap.org is currently not available due to maintenance. Making edits will be possible soon", + "loginFailedOfflineOther": "OpenStreetMap.org could not be reached", "loginFailedReadonlyMode": "OpenStreetMap.org is currently in readonly mode due to maintenance. Making edits will be possible soon", "loginFailedUnreachableMode": "OpenStreetMap.org is currently not reachable. Are you connected to the internet or do you block third parties? Try again later", "loginOnlyNeededToEdit": "if you want to make changes", diff --git a/package.json b/package.json index 87507f17c..48fa3a646 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "#summary_server": "Should be the endpoint; appending status.json should work", "summary_server": "https://cache.mapcomplete.org/", "geoip_server": "https://ipinfo.mapcomplete.org/", + "error_server": "https://report.mapcomplete.org/report", "disabled:oauth_credentials": { "##": "DEV", "#": "This client-id is registered by 'MapComplete' on https://master.apis.dev.openstreetmap.org/", diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index ff5704e5d..7fe879a30 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -1229,14 +1229,14 @@ video { height: 6rem; } -.h-screen { - height: 100vh; -} - .h-full { height: 100%; } +.h-screen { + height: 100vh; +} + .h-fit { height: -webkit-fit-content; height: -moz-fit-content; diff --git a/src/Logic/Osm/Changes.ts b/src/Logic/Osm/Changes.ts index 58ece3d2e..d59c1047f 100644 --- a/src/Logic/Osm/Changes.ts +++ b/src/Logic/Osm/Changes.ts @@ -43,6 +43,7 @@ export class Changes { private readonly previouslyCreated: OsmObject[] = [] private readonly _leftRightSensitive: boolean private readonly _changesetHandler: ChangesetHandler + private readonly _reportError?: (string: string | Error) => void constructor( state: { @@ -53,7 +54,8 @@ export class Changes { historicalUserLocations?: FeatureSource featureSwitches?: FeatureSwitchState }, - leftRightSensitive: boolean = false + leftRightSensitive: boolean = false, + reportError?: (string: string | Error) => void, ) { this._leftRightSensitive = leftRightSensitive // We keep track of all changes just as well @@ -62,11 +64,13 @@ export class Changes { this._nextId = Math.min(-1, ...(this.pendingChanges.data?.map((pch) => pch.id) ?? [])) this.state = state this.backend = state.osmConnection.Backend() + this._reportError = reportError this._changesetHandler = new ChangesetHandler( state.dryRun, state.osmConnection, state.featurePropertiesStore, - this + this, + e => this._reportError(e) ) this.historicalUserLocations = state.historicalUserLocations @@ -234,6 +238,7 @@ export class Changes { console.log("Changes flushed. Your changeset is " + csNumber) this.errors.setData([]) } catch (e) { + this._reportError(e) this.isUploading.setData(false) this.errors.data.push(e) this.errors.ping() @@ -518,6 +523,9 @@ export class Changes { const osmObj = await downloader.DownloadObjectAsync(id, 0) return { id, osmObj } } catch (e) { + this._reportError( "Could not download OSM-object"+ + id+ + " dropping it from the changes (" + e + ")") console.error( "Could not download OSM-object", id, @@ -685,6 +693,7 @@ export class Changes { } return result } catch (e) { + this._reportError(e) console.error("Could not upload some changes:", e) this.errors.data.push(e) this.errors.ping() diff --git a/src/Logic/Osm/ChangesetHandler.ts b/src/Logic/Osm/ChangesetHandler.ts index 2a6769264..72bd71383 100644 --- a/src/Logic/Osm/ChangesetHandler.ts +++ b/src/Logic/Osm/ChangesetHandler.ts @@ -26,6 +26,7 @@ export class ChangesetHandler { * @private */ private readonly _remappings = new Map() + private readonly _reportError: (e: (string | Error)) => void constructor( dryRun: Store, @@ -34,9 +35,11 @@ export class ChangesetHandler { | FeaturePropertiesStore | { addAlias: (id0: string, id1: string) => void } | undefined, - changes: Changes + changes: Changes, + reportError: (e: string | Error) => void ) { this.osmConnection = osmConnection + this._reportError = reportError this.allElements = allElements this.changes = changes this._dryRun = dryRun @@ -148,8 +151,13 @@ export class ChangesetHandler { await this.UpdateTags(csId, extraMetaTags) } } catch (e) { - console.error("Could not open/upload changeset due to ", e) + if(this._reportError){ + this._reportError(e) + } + console.warn("Could not open/upload changeset due to ", e,"trying t") openChangeset.setData(undefined) + + throw e } } else { // There still exists an open changeset (or at least we hope so) @@ -178,8 +186,12 @@ export class ChangesetHandler { ) await this.UpdateTags(csId, rewrittenTags) } catch (e) { + if(this._reportError){ + this._reportError(e) + } console.warn("Could not upload, changeset is probably closed: ", e) openChangeset.setData(undefined) + throw e } } } diff --git a/src/Models/Constants.ts b/src/Models/Constants.ts index af61a7181..1557cbcea 100644 --- a/src/Models/Constants.ts +++ b/src/Models/Constants.ts @@ -164,6 +164,7 @@ export default class Constants { */ public static VectorTileServer: string | undefined = Constants.config.mvt_layer_server public static GeoIpServer: string | undefined = Constants.config.geoip_server + public static ErrorReportServer: string | undefined = Constants.config.error_server public static readonly maptilerApiKey = "GvoVAJgu46I5rZapJuAy" public static readonly SummaryServer: string = Constants.config.summary_server diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index fea45494b..4443f76a3 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -2,11 +2,7 @@ import LayoutConfig from "./ThemeConfig/LayoutConfig" import { SpecialVisualizationState } from "../UI/SpecialVisualization" import { Changes } from "../Logic/Osm/Changes" import { Store, UIEventSource } from "../Logic/UIEventSource" -import { - FeatureSource, - IndexedFeatureSource, - WritableFeatureSource, -} from "../Logic/FeatureSource/FeatureSource" +import { FeatureSource, IndexedFeatureSource, WritableFeatureSource } from "../Logic/FeatureSource/FeatureSource" import { OsmConnection } from "../Logic/Osm/OsmConnection" import { ExportableMap, MapProperties } from "./MapProperties" import LayerState from "../Logic/State/LayerState" @@ -50,9 +46,7 @@ import BackgroundLayerResetter from "../Logic/Actors/BackgroundLayerResetter" import SaveFeatureSourceToLocalStorage from "../Logic/FeatureSource/Actors/SaveFeatureSourceToLocalStorage" import BBoxFeatureSource from "../Logic/FeatureSource/Sources/TouchesBboxFeatureSource" import ThemeViewStateHashActor from "../Logic/Web/ThemeViewStateHashActor" -import NoElementsInViewDetector, { - FeatureViewState, -} from "../Logic/Actors/NoElementsInViewDetector" +import NoElementsInViewDetector, { FeatureViewState } from "../Logic/Actors/NoElementsInViewDetector" import FilteredLayer from "./FilteredLayer" import { PreferredRasterLayerSelector } from "../Logic/Actors/PreferredRasterLayerSelector" import { ImageUploadManager } from "../Logic/ImageProviders/ImageUploadManager" @@ -158,7 +152,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.featureSwitches = new FeatureSwitchState(layout) this.guistate = new MenuState( this.featureSwitches.featureSwitchWelcomeMessage.data, - layout.id + layout.id, ) this.map = new UIEventSource(undefined) const geolocationState = new GeoLocationState() @@ -174,14 +168,14 @@ export default class ThemeViewState implements SpecialVisualizationState { oauth_token: QueryParameters.GetQueryParameter( "oauth_token", undefined, - "Used to complete the login" + "Used to complete the login", ), }) this.userRelatedState = new UserRelatedState( this.osmConnection, layout, this.featureSwitches, - this.mapProperties + this.mapProperties, ) this.userRelatedState.fixateNorth.addCallbackAndRunD((fixated) => { this.mapProperties.allowRotating.setData(fixated !== "yes") @@ -192,13 +186,13 @@ export default class ThemeViewState implements SpecialVisualizationState { geolocationState, this.selectedElement, this.mapProperties, - this.userRelatedState.gpsLocationHistoryRetentionTime + this.userRelatedState.gpsLocationHistoryRetentionTime, ) this.geolocationControl = new GeolocationControlState(this.geolocation, this.mapProperties) this.availableLayers = AvailableRasterLayers.layersAvailableAt( this.mapProperties.location, - this.osmConnection.isLoggedIn + this.osmConnection.isLoggedIn, ) const self = this @@ -210,7 +204,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const isDisplayed = QueryParameters.GetBooleanQueryParameter( "overlay-" + rasterInfo.id, rasterInfo.defaultState ?? true, - "Wether or not overlayer layer " + rasterInfo.id + " is shown" + "Wether or not overlayer layer " + rasterInfo.id + " is shown", ) const state = { isDisplayed } overlayLayerStates.set(rasterInfo.id, state) @@ -235,7 +229,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.osmConnection.Backend(), (id) => self.layerState.filteredLayers.get(id).isDisplayed, mvtAvailableLayers, - this.fullNodeDatabase + this.fullNodeDatabase, ) let currentViewIndex = 0 @@ -253,7 +247,7 @@ export default class ThemeViewState implements SpecialVisualizationState { id: "current_view_" + currentViewIndex, }), ] - }) + }), ) this.featuresInView = new BBoxFeatureSource(layoutSource, this.mapProperties.bounds) @@ -270,19 +264,20 @@ export default class ThemeViewState implements SpecialVisualizationState { historicalUserLocations: this.geolocation.historicalUserLocations, featureSwitches: this.featureSwitches, }, - layout?.isLeftRightSensitive() ?? false + layout?.isLeftRightSensitive() ?? false, + e => this.reportError(e), ) this.historicalUserLocations = this.geolocation.historicalUserLocations this.newFeatures = new NewGeometryFromChangesFeatureSource( this.changes, layoutSource, - this.featureProperties + this.featureProperties, ) layoutSource.addSource(this.newFeatures) const perLayer = new PerLayerFeatureSourceSplitter( Array.from(this.layerState.filteredLayers.values()).filter( - (l) => l.layerDef?.source !== null + (l) => l.layerDef?.source !== null, ), new ChangeGeometryApplicator(this.indexedFeatures, this.changes), { @@ -293,10 +288,10 @@ export default class ThemeViewState implements SpecialVisualizationState { "Got ", features.length, "leftover features, such as", - features[0].properties + features[0].properties, ) }, - } + }, ) this.perLayer = perLayer.perLayer } @@ -335,12 +330,12 @@ export default class ThemeViewState implements SpecialVisualizationState { this.lastClickObject = new LastClickFeatureSource( this.layout, - this.mapProperties.lastClickLocation + this.mapProperties.lastClickLocation, ) this.osmObjectDownloader = new OsmObjectDownloader( this.osmConnection.Backend(), - this.changes + this.changes, ) this.perLayerFiltered = this.showNormalDataOn(this.map) @@ -351,7 +346,7 @@ export default class ThemeViewState implements SpecialVisualizationState { currentZoom: this.mapProperties.zoom, layerState: this.layerState, bounds: this.visualFeedbackViewportBounds, - } + }, ) this.hasDataInView = new NoElementsInViewDetector(this).hasFeatureInView this.imageUploadManager = new ImageUploadManager( @@ -359,7 +354,7 @@ export default class ThemeViewState implements SpecialVisualizationState { Imgur.singleton, this.featureProperties, this.osmConnection, - this.changes + this.changes, ) this.favourites = new FavouritesFeatureSource(this) @@ -402,7 +397,7 @@ export default class ThemeViewState implements SpecialVisualizationState { LayoutSource.fromCacheZoomLevel, fs, this.featureProperties, - fs.layer.layerDef.maxAgeOfCache + fs.layer.layerDef.maxAgeOfCache, ) toLocalStorage.set(layerId, storage) }) @@ -415,7 +410,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const doShowLayer = this.mapProperties.zoom.map( (z) => (fs.layer.isDisplayed?.data ?? true) && z >= (fs.layer.layerDef?.minzoom ?? 0), - [fs.layer.isDisplayed] + [fs.layer.isDisplayed], ) if (!doShowLayer.data && this.featureSwitches.featureSwitchFilter.data === false) { @@ -432,7 +427,7 @@ export default class ThemeViewState implements SpecialVisualizationState { fs.layer, fs, (id) => this.featureProperties.getStore(id), - this.layerState.globalFilters + this.layerState.globalFilters, ) filteringFeatureSource.set(layerName, filtered) @@ -573,7 +568,7 @@ export default class ThemeViewState implements SpecialVisualizationState { return } this.selectClosestAtCenter(0) - } + }, ) for (let i = 1; i < 9; i++) { @@ -591,7 +586,7 @@ export default class ThemeViewState implements SpecialVisualizationState { onUp: true, }, doc, - () => this.selectClosestAtCenter(i - 1) + () => this.selectClosestAtCenter(i - 1), ) } @@ -608,7 +603,7 @@ export default class ThemeViewState implements SpecialVisualizationState { if (this.featureSwitches.featureSwitchBackgroundSelection.data) { this.guistate.backgroundLayerSelectionIsOpened.setData(true) } - } + }, ) Hotkeys.RegisterHotkey( { @@ -620,14 +615,14 @@ export default class ThemeViewState implements SpecialVisualizationState { if (this.featureSwitches.featureSwitchFilter.data) { this.guistate.openFilterView() } - } + }, ) Hotkeys.RegisterHotkey( { shift: "O" }, Translations.t.hotkeyDocumentation.selectMapnik, () => { this.mapProperties.rasterLayer.setData(AvailableRasterLayers.osmCarto) - } + }, ) const setLayerCategory = (category: EliCategory) => { const available = this.availableLayers.data @@ -635,7 +630,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const best = RasterLayerUtils.SelectBestLayerAccordingTo( available, category, - current.data + current.data, ) console.log("Best layer for category", category, "is", best.properties.id) current.setData(best) @@ -644,26 +639,26 @@ export default class ThemeViewState implements SpecialVisualizationState { Hotkeys.RegisterHotkey( { nomod: "O" }, Translations.t.hotkeyDocumentation.selectOsmbasedmap, - () => setLayerCategory("osmbasedmap") + () => setLayerCategory("osmbasedmap"), ) Hotkeys.RegisterHotkey( { nomod: "M" }, Translations.t.hotkeyDocumentation.selectMap, - () => setLayerCategory("map") + () => setLayerCategory("map"), ) Hotkeys.RegisterHotkey( { nomod: "P" }, Translations.t.hotkeyDocumentation.selectAerial, - () => setLayerCategory("photo") + () => setLayerCategory("photo"), ) Hotkeys.RegisterHotkey( { nomod: "L" }, Translations.t.hotkeyDocumentation.geolocate, () => { this.geolocationControl.handleClick() - } + }, ) return true }) @@ -675,7 +670,7 @@ export default class ThemeViewState implements SpecialVisualizationState { Translations.t.hotkeyDocumentation.translationMode, () => { Locale.showLinkToWeblate.setData(!Locale.showLinkToWeblate.data) - } + }, ) } @@ -686,7 +681,7 @@ export default class ThemeViewState implements SpecialVisualizationState { const normalLayers = this.layout.layers.filter( (l) => Constants.priviliged_layers.indexOf(l.id) < 0 && - !l.id.startsWith("note_import") + !l.id.startsWith("note_import"), ) const maxzoom = Math.min(...normalLayers.map((l) => l.minzoom)) @@ -694,7 +689,7 @@ export default class ThemeViewState implements SpecialVisualizationState { (l) => Constants.priviliged_layers.indexOf(l.id) < 0 && l.source.geojsonSource === undefined && - l.doCount + l.doCount, ) const summaryTileSource = new SummaryTileSource( Constants.SummaryServer, @@ -703,7 +698,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties, { isActive: this.mapProperties.zoom.map((z) => z <= maxzoom), - } + }, ) return new SummaryTileSourceRewriter(summaryTileSource, this.layerState.filteredLayers) } @@ -723,12 +718,12 @@ export default class ThemeViewState implements SpecialVisualizationState { gps_location_history: this.geolocation.historicalUserLocations, gps_track: this.geolocation.historicalUserLocationsTrack, selected_element: new StaticFeatureSource( - this.selectedElement.map((f) => (f === undefined ? empty : [f])) + this.selectedElement.map((f) => (f === undefined ? empty : [f])), ), range: new StaticFeatureSource( this.mapProperties.maxbounds.map((bbox) => - bbox === undefined ? empty : [bbox.asGeoJson({ id: "range" })] - ) + bbox === undefined ? empty : [bbox.asGeoJson({ id: "range" })], + ), ), current_view: this.currentView, favourite: this.favourites, @@ -743,7 +738,7 @@ export default class ThemeViewState implements SpecialVisualizationState { ShowDataLayer.showRange( this.map, new StaticFeatureSource([bbox.asGeoJson({ id: "range" })]), - this.featureSwitches.featureSwitchIsTesting + this.featureSwitches.featureSwitchIsTesting, ) } const currentViewLayer = this.layout.layers.find((l) => l.id === "current_view") @@ -757,7 +752,7 @@ export default class ThemeViewState implements SpecialVisualizationState { currentViewLayer, this.layout, this.osmObjectDownloader, - this.featureProperties + this.featureProperties, ) }) } @@ -801,20 +796,20 @@ export default class ThemeViewState implements SpecialVisualizationState { const lastClickLayerConfig = new LayerConfig( last_click_layerconfig, - "last_click" + "last_click", ) const lastClickFiltered = lastClickLayerConfig.isShown === undefined ? specialLayers.last_click : specialLayers.last_click.features.mapD((fs) => - fs.filter((f) => { - const matches = lastClickLayerConfig.isShown.matchesProperties( - f.properties - ) - console.log("LastClick ", f, "matches", matches) - return matches - }) - ) + fs.filter((f) => { + const matches = lastClickLayerConfig.isShown.matchesProperties( + f.properties, + ) + console.log("LastClick ", f, "matches", matches) + return matches + }), + ) new ShowDataLayer(this.map, { features: new StaticFeatureSource(lastClickFiltered), layer: lastClickLayerConfig, @@ -859,7 +854,7 @@ export default class ThemeViewState implements SpecialVisualizationState { this.mapProperties.rasterLayer, this.availableLayers, this.featureSwitches.backgroundLayerId, - this.userRelatedState.preferredBackgroundLayer + this.userRelatedState.preferredBackgroundLayer, ) } @@ -867,4 +862,21 @@ export default class ThemeViewState implements SpecialVisualizationState { this.guistate.closeAll() this.selectedElement.setData(this.currentView.features?.data?.[0]) } + + public async reportError(message: string | Error) { + console.log(">>> Reporting error to",Constants.ErrorReportServer, message) + let stacktrace: string = new Error().stack + + await fetch(Constants.ErrorReportServer, { + method: "POST", + body: JSON.stringify({ + stacktrace, + message: ""+message, + layout: this.layout.id, + username: this.osmConnection.userDetails.data?.name, + userid: this.osmConnection.userDetails.data?.uid, + pendingChanges: this.changes.pendingChanges.data, + }), + }) + } } diff --git a/src/UI/Base/LoginToggle.svelte b/src/UI/Base/LoginToggle.svelte index 3192e5acb..288f30543 100644 --- a/src/UI/Base/LoginToggle.svelte +++ b/src/UI/Base/LoginToggle.svelte @@ -7,6 +7,7 @@ import Tr from "./Tr.svelte" import { ImmutableStore, UIEventSource } from "../../Logic/UIEventSource" import Invalid from "../../assets/svg/Invalid.svelte" + import ArrowPath from "@babeard/svelte-heroicons/mini/ArrowPath" export let state: { osmConnection: OsmConnection @@ -40,9 +41,13 @@ {:else if !silentFail && $loadingStatus === "error"} -
+
- + +
{:else if $loadingStatus === "logged-in"} diff --git a/src/UI/SpecialVisualization.ts b/src/UI/SpecialVisualization.ts index 53cd865fa..4b7d850eb 100644 --- a/src/UI/SpecialVisualization.ts +++ b/src/UI/SpecialVisualization.ts @@ -95,6 +95,7 @@ export interface SpecialVisualizationState { readonly geolocation: GeoLocationHandler showCurrentLocationOn(map: Store): ShowDataLayer + reportError(message: string): Promise } export interface SpecialVisualization {