2024-09-10 02:19:55 +02:00
|
|
|
import { Store, UIEventSource } from "../UIEventSource"
|
|
|
|
import { OsmConnection } from "./OsmConnection"
|
2023-10-06 03:34:26 +02:00
|
|
|
import { LocalStorageSource } from "../Web/LocalStorageSource"
|
2024-08-23 02:16:24 +02:00
|
|
|
import OSMAuthInstance = OSMAuth.osmAuth
|
2020-08-26 15:36:04 +02:00
|
|
|
|
|
|
|
export class OsmPreferences {
|
2024-09-10 02:19:55 +02:00
|
|
|
|
|
|
|
private normalPreferences: Record<string, UIEventSource<string>> = {}
|
|
|
|
private longPreferences: Record<string, UIEventSource<string>> = {}
|
|
|
|
private localStorageInited: Set<string> = new Set()
|
|
|
|
|
|
|
|
private readonly _allPreferences: UIEventSource<Record<string, string>> = new UIEventSource({})
|
|
|
|
public readonly allPreferences: Store<Readonly<Record<string, string>>> = this._allPreferences
|
2024-03-11 00:01:44 +01:00
|
|
|
private readonly _fakeUser: boolean
|
2024-09-10 02:19:55 +02:00
|
|
|
private readonly auth: OSMAuthInstance
|
|
|
|
private readonly osmConnection: OsmConnection
|
2020-08-26 15:36:04 +02:00
|
|
|
|
2024-03-11 00:01:44 +01:00
|
|
|
constructor(auth: OSMAuthInstance, osmConnection: OsmConnection, fakeUser: boolean = false) {
|
2020-08-26 15:36:04 +02:00
|
|
|
this.auth = auth
|
2024-03-11 00:01:44 +01:00
|
|
|
this._fakeUser = fakeUser
|
2024-09-10 02:19:55 +02:00
|
|
|
this.osmConnection = osmConnection
|
2023-11-23 17:06:30 +01:00
|
|
|
osmConnection.OnLoggedIn(() => {
|
2024-09-10 02:19:55 +02:00
|
|
|
this.loadBulkPreferences()
|
2023-11-23 17:06:30 +01:00
|
|
|
return true
|
|
|
|
})
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
private getLongValue(allPrefs: Record<string, string>, key: string): string {
|
|
|
|
const count = Number(allPrefs[key + "-length"])
|
|
|
|
let str = ""
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
str += allPrefs[key + i]
|
2020-08-26 20:11:43 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
return str
|
2020-08-26 20:11:43 +02:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
}
|
2022-02-04 14:36:26 +01:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
private setPreferencesAll(key: string, value: string) {
|
|
|
|
if (this._allPreferences.data[key] !== value) {
|
|
|
|
this._allPreferences.data[key] = value
|
|
|
|
this._allPreferences.ping()
|
2022-02-04 14:36:26 +01:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
}
|
2022-02-04 14:36:26 +01:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
private initPreference(key: string, value: string = "", excludeFromAll: boolean = false): UIEventSource<string> {
|
|
|
|
if (this.normalPreferences[key] !== undefined) {
|
|
|
|
return this.normalPreferences[key]
|
|
|
|
}
|
|
|
|
const pref = this.normalPreferences[key] = new UIEventSource(value, "preference: " + key)
|
|
|
|
if(value && !excludeFromAll){
|
|
|
|
this.setPreferencesAll(key, value)
|
|
|
|
}
|
|
|
|
pref.addCallback(v => {
|
|
|
|
this.UploadPreference(key, v)
|
|
|
|
if(!excludeFromAll){
|
|
|
|
this.setPreferencesAll(key, v)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
})
|
|
|
|
return pref
|
|
|
|
}
|
2020-08-26 15:36:04 +02:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
private initLongPreference(key: string, initialValue: string): UIEventSource<string> {
|
|
|
|
if (this.longPreferences[key] !== undefined) {
|
|
|
|
return this.longPreferences[key]
|
|
|
|
}
|
|
|
|
const pref = this.longPreferences[key] = new UIEventSource<string>(initialValue, "long-preference-"+key)
|
|
|
|
const maxLength = 255
|
|
|
|
const length = UIEventSource.asInt(this.initPreference(key + "-length", "0", true))
|
|
|
|
if(initialValue){
|
|
|
|
this.setPreferencesAll(key, initialValue)
|
|
|
|
}
|
|
|
|
pref.addCallback(v => {
|
|
|
|
length.set(Math.ceil(v.length / maxLength))
|
2020-08-26 15:36:04 +02:00
|
|
|
let i = 0
|
2024-09-10 02:19:55 +02:00
|
|
|
while (v.length > 0) {
|
|
|
|
this.UploadPreference(key + "-" + i, v.substring(0, maxLength))
|
2020-08-26 15:36:04 +02:00
|
|
|
i++
|
2024-09-10 02:19:55 +02:00
|
|
|
v = v.substring(maxLength)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
this.setPreferencesAll(key, v)
|
2020-08-26 15:36:04 +02:00
|
|
|
})
|
2024-09-10 02:19:55 +02:00
|
|
|
return pref
|
|
|
|
}
|
2020-08-26 15:36:04 +02:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
private async loadBulkPreferences() {
|
|
|
|
const prefs = await this.getPreferencesDict()
|
|
|
|
const isCombined = /-combined-/
|
|
|
|
for (const key in prefs) {
|
|
|
|
if (key.endsWith("-combined-length")) {
|
|
|
|
const v = this.getLongValue(prefs, key.substring(0, key.length - "-length".length))
|
|
|
|
this.initLongPreference(key, v)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
if (key.match(isCombined)) {
|
|
|
|
continue
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
this.initPreference(key, prefs[key])
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
}
|
2020-08-26 15:36:04 +02:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
/**
|
|
|
|
* OSM preferences can be at most 255 chars.
|
|
|
|
* This method chains multiple together.
|
|
|
|
* Values written into this key will be erased when the user logs in
|
|
|
|
*/
|
|
|
|
public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
|
|
|
|
return this.getPreferenceSeedFromlocal(key, true, undefined, { prefix })
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
|
|
|
|
2022-06-03 01:33:41 +02:00
|
|
|
public GetPreference(
|
|
|
|
key: string,
|
|
|
|
defaultValue: string = undefined,
|
2023-03-08 01:36:27 +01:00
|
|
|
options?: {
|
|
|
|
documentation?: string
|
|
|
|
prefix?: string
|
2024-09-10 02:19:55 +02:00
|
|
|
},
|
|
|
|
) {
|
|
|
|
return this.getPreferenceSeedFromlocal(key, false, defaultValue, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets a OSM-preference.
|
|
|
|
* The OSM-preference is cached in local storage and updated from the OSM.org as soon as those values come in.
|
|
|
|
* THis means that values written before being logged in might be erased by the cloud settings
|
|
|
|
* @param key
|
|
|
|
* @param defaultValue
|
|
|
|
* @param options
|
|
|
|
* @constructor
|
|
|
|
*/
|
|
|
|
private getPreferenceSeedFromlocal(
|
|
|
|
key: string,
|
|
|
|
long: boolean,
|
|
|
|
defaultValue: string = undefined,
|
|
|
|
options?: {
|
|
|
|
prefix?: string,
|
|
|
|
saveToLocalStorage?: true | boolean
|
|
|
|
},
|
2022-06-03 01:33:41 +02:00
|
|
|
): UIEventSource<string> {
|
2024-09-10 02:19:55 +02:00
|
|
|
if (options?.prefix) {
|
|
|
|
key = options.prefix + key
|
2022-06-21 18:22:09 +02:00
|
|
|
}
|
2024-08-23 02:16:24 +02:00
|
|
|
key = key.replace(/[:/"' {}.%\\]/g, "")
|
2024-09-10 02:19:55 +02:00
|
|
|
|
|
|
|
|
|
|
|
let pref : UIEventSource<string>
|
|
|
|
const localStorage = LocalStorageSource.Get(key)
|
|
|
|
if(localStorage.data === "null" || localStorage.data === "undefined"){
|
|
|
|
localStorage.set(undefined)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
if(long){
|
|
|
|
pref = this.initLongPreference(key, localStorage.data ?? defaultValue)
|
|
|
|
}else{
|
|
|
|
pref = this.initPreference(key, localStorage.data ?? defaultValue)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2022-09-08 21:40:48 +02:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
if (this.localStorageInited.has(key)) {
|
|
|
|
return pref
|
|
|
|
}
|
2023-01-11 03:53:58 +01:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
if (options?.saveToLocalStorage ?? true) {
|
|
|
|
pref.addCallback(v => localStorage.set(v)) // Keep a local copy
|
|
|
|
}
|
|
|
|
this.localStorageInited.add(key)
|
2020-08-26 15:36:04 +02:00
|
|
|
return pref
|
|
|
|
}
|
|
|
|
|
2022-01-26 21:40:38 +01:00
|
|
|
public ClearPreferences() {
|
2024-09-10 02:19:55 +02:00
|
|
|
console.log("Starting to remove all preferences")
|
|
|
|
this.removeAllWithPrefix("")
|
2022-01-26 21:40:38 +01:00
|
|
|
}
|
|
|
|
|
2024-03-11 00:01:44 +01:00
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
/**
|
|
|
|
* Bulk-downloads all preferences
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
private getPreferencesDict(): Promise<Record<string, string>> {
|
|
|
|
return new Promise<Record<string, string>>((resolve, reject) => {
|
|
|
|
this.auth.xhr(
|
|
|
|
{
|
|
|
|
method: "GET",
|
|
|
|
path: "/api/0.6/user/preferences",
|
|
|
|
},
|
|
|
|
(error, value: XMLDocument) => {
|
|
|
|
if (error) {
|
|
|
|
console.log("Could not load preferences", error)
|
|
|
|
reject(error)
|
|
|
|
return
|
2023-11-23 17:06:30 +01:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
const prefs = value.getElementsByTagName("preference")
|
|
|
|
const dict: Record<string, string> = {}
|
|
|
|
for (let i = 0; i < prefs.length; i++) {
|
|
|
|
const pref = prefs[i]
|
|
|
|
const k = pref.getAttribute("k")
|
|
|
|
dict[k] = pref.getAttribute("v")
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
resolve(dict)
|
|
|
|
},
|
|
|
|
)
|
|
|
|
})
|
2022-09-08 21:40:48 +02:00
|
|
|
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
|
|
|
|
2024-09-10 02:19:55 +02:00
|
|
|
/**
|
|
|
|
* UPloads the given k=v to the OSM-server
|
|
|
|
* Deletes it if 'v' is undefined, null or empty
|
|
|
|
*/
|
2022-02-04 14:36:26 +01:00
|
|
|
private UploadPreference(k: string, v: string) {
|
2024-09-10 02:19:55 +02:00
|
|
|
if (!this.osmConnection.userDetails.data.loggedIn) {
|
2021-09-22 16:07:56 +02:00
|
|
|
console.debug(`Not saving preference ${k}: user not logged in`)
|
2020-08-26 15:36:04 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-03-11 00:01:44 +01:00
|
|
|
if (this._fakeUser) {
|
|
|
|
return
|
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
if (v === undefined || v === "" || v === null) {
|
2020-08-26 15:36:04 +02:00
|
|
|
this.auth.xhr(
|
|
|
|
{
|
|
|
|
method: "DELETE",
|
2021-01-15 01:57:46 +01:00
|
|
|
path: "/api/0.6/user/preferences/" + encodeURIComponent(k),
|
2024-03-11 00:01:44 +01:00
|
|
|
headers: { "Content-Type": "text/plain" },
|
2020-08-30 01:13:18 +02:00
|
|
|
},
|
2024-08-23 02:16:24 +02:00
|
|
|
(error) => {
|
2020-08-26 15:36:04 +02:00
|
|
|
if (error) {
|
2021-09-22 16:07:56 +02:00
|
|
|
console.warn("Could not remove preference", error)
|
2020-08-26 15:36:04 +02:00
|
|
|
return
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2021-09-22 16:07:56 +02:00
|
|
|
console.debug("Preference ", k, "removed!")
|
2024-09-10 02:19:55 +02:00
|
|
|
},
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2020-08-26 15:36:04 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
this.auth.xhr(
|
|
|
|
{
|
|
|
|
method: "PUT",
|
2021-01-15 01:57:46 +01:00
|
|
|
path: "/api/0.6/user/preferences/" + encodeURIComponent(k),
|
2024-03-11 00:01:44 +01:00
|
|
|
headers: { "Content-Type": "text/plain" },
|
2020-08-26 15:36:04 +02:00
|
|
|
content: v,
|
2020-08-30 01:13:18 +02:00
|
|
|
},
|
2024-09-10 02:19:55 +02:00
|
|
|
(error) => {
|
2020-08-26 15:36:04 +02:00
|
|
|
if (error) {
|
2022-01-25 00:46:57 +01:00
|
|
|
console.warn(`Could not set preference "${k}"'`, error)
|
2020-08-26 15:36:04 +02:00
|
|
|
return
|
2022-09-08 21:40:48 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
},
|
2022-09-08 21:40:48 +02:00
|
|
|
)
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|
2024-09-10 02:19:55 +02:00
|
|
|
|
|
|
|
removeAllWithPrefix(prefix: string) {
|
|
|
|
for (const key in this.normalPreferences) {
|
|
|
|
if(key.startsWith(prefix)){
|
|
|
|
this.normalPreferences[key].set(null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (const key in this.longPreferences) {
|
|
|
|
if(key.startsWith(prefix)){
|
|
|
|
this.longPreferences[key].set(null)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getExistingPreference(key: string, defaultValue: undefined, prefix: string ): UIEventSource<string> {
|
|
|
|
if (prefix) {
|
|
|
|
key = prefix + key
|
|
|
|
}
|
|
|
|
key = key.replace(/[:/"' {}.%\\]/g, "")
|
|
|
|
|
|
|
|
if(this.normalPreferences[key]){
|
|
|
|
return this.normalPreferences[key]
|
|
|
|
}
|
|
|
|
return this.longPreferences[key]
|
|
|
|
|
|
|
|
}
|
2020-08-26 15:36:04 +02:00
|
|
|
}
|