Feature: stabilize saved history, add code to cleanup old preferences, make loading preferences faster (which prevents a 'hang') when just logged in

This commit is contained in:
Pieter Vander Vennet 2025-04-06 19:17:19 +02:00
parent dc1e582664
commit c1d3f35d30
10 changed files with 249 additions and 146 deletions

View file

@ -17,6 +17,7 @@ import ChangeTagAction from "./Actions/ChangeTagAction"
import DeleteAction from "./Actions/DeleteAction"
import MarkdownUtils from "../../Utils/MarkdownUtils"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import { Feature, Point } from "geojson"
/**
* Handles all changes made to OSM.
@ -37,7 +38,7 @@ export class Changes {
public readonly backend: string
public readonly isUploading = new UIEventSource(false)
public readonly errors = new UIEventSource<string[]>([], "upload-errors")
private readonly historicalUserLocations?: FeatureSource
private readonly historicalUserLocations?: FeatureSource<Feature<Point, GeoLocationPointProperties>>
private _nextId: number = 0 // Newly assigned ID's are negative
private readonly previouslyCreated: OsmObject[] = []
private readonly _leftRightSensitive: boolean
@ -53,7 +54,7 @@ export class Changes {
osmConnection: OsmConnection
reportError?: (error: string) => void
featureProperties?: FeaturePropertiesStore
historicalUserLocations?: FeatureSource
historicalUserLocations?: FeatureSource<Feature<Point, GeoLocationPointProperties>>
allElements?: IndexedFeatureSource
},
leftRightSensitive: boolean = false
@ -66,7 +67,7 @@ export class Changes {
if (isNaN(this._nextId) && state.reportError !== undefined) {
state.reportError(
"Got a NaN as nextID. Pending changes IDs are:" +
this.pendingChanges.data?.map((pch) => pch?.id).join(".")
this.pendingChanges.data?.map((pch) => pch?.id).join(".")
)
this._nextId = -100
}
@ -90,8 +91,8 @@ export class Changes {
return new Changes({
osmConnection: new OsmConnection(),
featureSwitches: {
featureSwitchIsTesting: new ImmutableStore(true),
},
featureSwitchIsTesting: new ImmutableStore(true)
}
})
}
@ -178,50 +179,50 @@ export class Changes {
[
{
key: "comment",
docs: "The changeset comment. Will be a fixed string, mentioning the theme",
docs: "The changeset comment. Will be a fixed string, mentioning the theme"
},
{
key: "theme",
docs: "The name of the theme that was used to create this change. ",
docs: "The name of the theme that was used to create this change. "
},
{
key: "source",
value: "survey",
docs: "The contributor had their geolocation enabled while making changes",
docs: "The contributor had their geolocation enabled while making changes"
},
{
key: "change_within_{distance}",
docs: "If the contributor enabled their geolocation, this will hint how far away they were from the objects they edited. This gives an indication of proximity and if they truly surveyed or were armchair-mapping",
docs: "If the contributor enabled their geolocation, this will hint how far away they were from the objects they edited. This gives an indication of proximity and if they truly surveyed or were armchair-mapping"
},
{
key: "change_over_{distance}",
docs: "If the contributor enabled their geolocation, this will hint how far away they were from the objects they edited. If they were over 5000m away, the might have been armchair-mapping",
docs: "If the contributor enabled their geolocation, this will hint how far away they were from the objects they edited. If they were over 5000m away, the might have been armchair-mapping"
},
{
key: "created_by",
value: "MapComplete <version>",
docs: "The piece of software used to create this changeset; will always start with MapComplete, followed by the version number",
docs: "The piece of software used to create this changeset; will always start with MapComplete, followed by the version number"
},
{
key: "locale",
value: "en|nl|de|...",
docs: "The code of the language that the contributor used MapComplete in. Hints what language the user speaks.",
docs: "The code of the language that the contributor used MapComplete in. Hints what language the user speaks."
},
{
key: "host",
value: "https://mapcomplete.org/<theme>",
docs: "The URL that the contributor used to make changes. One can see the used instance with this",
docs: "The URL that the contributor used to make changes. One can see the used instance with this"
},
{
key: "imagery",
docs: "The identifier of the used background layer, this will probably be an identifier from the [editor layer index](https://github.com/osmlab/editor-layer-index)",
},
docs: "The identifier of the used background layer, this will probably be an identifier from the [editor layer index](https://github.com/osmlab/editor-layer-index)"
}
],
"default"
),
...addSource(ChangeTagAction.metatags, "ChangeTag"),
...addSource(ChangeLocationAction.metatags, "ChangeLocation"),
...addSource(DeleteAction.metatags, "DeleteAction"),
...addSource(DeleteAction.metatags, "DeleteAction")
// TODO
/*
...DeleteAction.metatags,
@ -243,11 +244,11 @@ export class Changes {
docs,
specialMotivation
? "This might give a reason per modified node or way"
: "",
: ""
].join("\n"),
source,
source
])
),
)
].join("\n\n")
}
@ -266,7 +267,7 @@ export class Changes {
this._changesetHandler._remappings.has("node/" + this._nextId) ||
this._changesetHandler._remappings.has("way/" + this._nextId) ||
this._changesetHandler._remappings.has("relation/" + this._nextId)
)
)
return this._nextId
}
@ -333,6 +334,7 @@ export class Changes {
this.previouslyCreated
)
}
public static createChangesetObjectsStatic(
changes: ChangeDescription[],
downloadedOsmObjects: OsmObject[],
@ -502,7 +504,7 @@ export class Changes {
const result = {
newObjects: [],
modifiedObjects: [],
deletedObjects: [],
deletedObjects: []
}
objects.forEach((v, id) => {
@ -559,9 +561,7 @@ export class Changes {
const recentLocationPoints = locations
.filter((feat) => feat.geometry.type === "Point")
.filter((feat) => {
const visitTime = new Date(
(<GeoLocationPointProperties>(<any>feat.properties)).date
)
const visitTime = new Date(feat.properties.date)
// In seconds
const diff = (now.getTime() - visitTime.getTime()) / 1000
return diff < Constants.nearbyVisitTime
@ -665,7 +665,7 @@ export class Changes {
} else {
this._reportError(
`Got an orphaned change. The 'creation'-change description for ${c.type}/${c.id} got lost. Permanently dropping this change:` +
JSON.stringify(c)
JSON.stringify(c)
)
}
return
@ -676,10 +676,10 @@ export class Changes {
} else {
console.log(
"Refusing change about " +
c.type +
"/" +
c.id +
" as not in the objects. No internet?"
c.type +
"/" +
c.id +
" as not in the objects. No internet?"
)
refused.push(c)
}
@ -694,7 +694,7 @@ export class Changes {
*/
private async flushSelectChanges(
pending: ChangeDescription[],
openChangeset: UIEventSource<number>
openChangeset: UIEventSource<{ id: number, opened: number }>
): Promise<ChangeDescription[]> {
const neededIds = Changes.GetNeededIds(pending)
/* Download the latest version of the OSM-objects
@ -775,14 +775,14 @@ export class Changes {
([key, count]) => ({
key: key,
value: count,
aggregate: true,
aggregate: true
})
)
const motivations = pending
.filter((descr) => descr.meta.specialMotivation !== undefined)
.map((descr) => ({
key: descr.meta.changeType + ":" + descr.type + "/" + descr.id,
value: descr.meta.specialMotivation,
value: descr.meta.specialMotivation
}))
const distances = Utils.NoNull(pending.map((descr) => descr.meta.distanceToObject))
@ -813,7 +813,7 @@ export class Changes {
return {
key,
value: count,
aggregate: true,
aggregate: true
}
})
)
@ -828,19 +828,20 @@ export class Changes {
const metatags: ChangesetTag[] = [
{
key: "comment",
value: comment,
value: comment
},
{
key: "theme",
value: theme,
value: theme
},
...perType,
...motivations,
...perBinMessage,
...perBinMessage
]
return metatags
}
private async flushChangesAsync(): Promise<void> {
try {
// At last, we build the changeset and upload
@ -858,16 +859,12 @@ export class Changes {
const refusedChanges: ChangeDescription[][] = await Promise.all(
Array.from(pendingPerTheme, async ([theme, pendingChanges]) => {
try {
const openChangeset = UIEventSource.asInt(
this.state.osmConnection.GetPreference(
"current-open-changeset-" + theme
)
)
const openChangeset = this.state.osmConnection.getCurrentChangesetFor(theme)
console.log(
"Using current-open-changeset-" +
theme +
" from the preferences, got " +
openChangeset.data
theme +
" from the preferences, got " +
openChangeset.data
)
const refused = await this.flushSelectChanges(pendingChanges, openChangeset)