Only override keys

This commit is contained in:
Robin van der Linde 2023-09-26 14:01:46 +02:00
parent 0e531ca017
commit ba909ea0ec
Signed by: Robin-van-der-Linde
GPG key ID: 53956B3252478F0D

View file

@ -1,63 +1,63 @@
// @ts-ignore // @ts-ignore
import { osmAuth } from "osm-auth"; import { osmAuth } from "osm-auth"
import { Store, Stores, UIEventSource } from "../UIEventSource"; import { Store, Stores, UIEventSource } from "../UIEventSource"
import { OsmPreferences } from "./OsmPreferences"; import { OsmPreferences } from "./OsmPreferences"
import { Utils } from "../../Utils"; import { Utils } from "../../Utils"
import { LocalStorageSource } from "../Web/LocalStorageSource"; import { LocalStorageSource } from "../Web/LocalStorageSource"
import * as config from "../../../package.json"; import * as config from "../../../package.json"
export default class UserDetails { export default class UserDetails {
public loggedIn = false; public loggedIn = false
public name = "Not logged in"; public name = "Not logged in"
public uid: number; public uid: number
public csCount = 0; public csCount = 0
public img?: string; public img?: string
public unreadMessages = 0; public unreadMessages = 0
public totalMessages: number = 0; public totalMessages: number = 0
public home: { lon: number; lat: number }; public home: { lon: number; lat: number }
public backend: string; public backend: string
public account_created: string; public account_created: string
public tracesCount: number = 0; public tracesCount: number = 0
public description: string; public description: string
constructor(backend: string) { constructor(backend: string) {
this.backend = backend; this.backend = backend
} }
} }
export interface AuthConfig { export interface AuthConfig {
"#"?: string; // optional comment "#"?: string // optional comment
oauth_client_id: string; oauth_client_id: string
oauth_secret: string; oauth_secret: string
url: string; url: string
} }
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable" export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
export class OsmConnection { export class OsmConnection {
public static readonly oauth_configs: Record<string, AuthConfig> = public static readonly oauth_configs: Record<string, AuthConfig> =
config.config.oauth_credentials; config.config.oauth_credentials
public auth; public auth
public userDetails: UIEventSource<UserDetails>; public userDetails: UIEventSource<UserDetails>
public isLoggedIn: Store<boolean>; public isLoggedIn: Store<boolean>
public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>( public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
"unknown" "unknown"
); )
public apiIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>( public apiIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
"unknown" "unknown"
); )
public loadingStatus = new UIEventSource<"not-attempted" | "loading" | "error" | "logged-in">( public loadingStatus = new UIEventSource<"not-attempted" | "loading" | "error" | "logged-in">(
"not-attempted" "not-attempted"
); )
public preferencesHandler: OsmPreferences; public preferencesHandler: OsmPreferences
public readonly _oauth_config: AuthConfig; public readonly _oauth_config: AuthConfig
private readonly _dryRun: Store<boolean>; private readonly _dryRun: Store<boolean>
private fakeUser: boolean; private fakeUser: boolean
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []; private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []
private readonly _iframeMode: Boolean | boolean; private readonly _iframeMode: Boolean | boolean
private readonly _singlePage: boolean; private readonly _singlePage: boolean
private isChecking = false; private isChecking = false
constructor(options?: { constructor(options?: {
dryRun?: Store<boolean> dryRun?: Store<boolean>
@ -68,83 +68,80 @@ export class OsmConnection {
osmConfiguration?: "osm" | "osm-test" osmConfiguration?: "osm" | "osm-test"
attemptLogin?: true | boolean attemptLogin?: true | boolean
}) { }) {
options = options ?? {}; 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 =
OsmConnection.oauth_configs[options.osmConfiguration ?? "osm"] ?? OsmConnection.oauth_configs[options.osmConfiguration ?? "osm"] ??
OsmConnection.oauth_configs.osm; OsmConnection.oauth_configs.osm
console.debug("Using backend", this._oauth_config.url); console.debug("Using backend", this._oauth_config.url)
this._iframeMode = Utils.runningFromConsole ? false : window !== window.top; this._iframeMode = Utils.runningFromConsole ? false : window !== window.top
// Check if there are settings available in environment variables, and if so, use those // Check if there are settings available in environment variables, and if so, use those
if ( if (
import.meta.env.VITE_OSM_OAUTH_CLIENT_ID !== undefined && import.meta.env.VITE_OSM_OAUTH_CLIENT_ID !== undefined &&
import.meta.env.VITE_OSM_OAUTH_SECRET !== undefined import.meta.env.VITE_OSM_OAUTH_SECRET !== undefined
) { ) {
console.debug("Using environment variables for oauth config"); console.debug("Using environment variables for oauth config")
this._oauth_config = { this._oauth_config.oauth_client_id = import.meta.env.VITE_OSM_OAUTH_CLIENT_ID
oauth_client_id: import.meta.env.VITE_OSM_OAUTH_CLIENT_ID, this._oauth_config.oauth_secret = import.meta.env.VITE_OSM_OAUTH_SECRET
oauth_secret: import.meta.env.VITE_OSM_OAUTH_SECRET,
url: "https://api.openstreetmap.org"
};
} }
this.userDetails = new UIEventSource<UserDetails>( this.userDetails = new UIEventSource<UserDetails>(
new UserDetails(this._oauth_config.url), new UserDetails(this._oauth_config.url),
"userDetails" "userDetails"
); )
if (options.fakeUser) { if (options.fakeUser) {
const ud = this.userDetails.data; const ud = this.userDetails.data
ud.csCount = 5678; ud.csCount = 5678
ud.loggedIn = true; ud.loggedIn = true
ud.unreadMessages = 0; ud.unreadMessages = 0
ud.name = "Fake user"; ud.name = "Fake user"
ud.totalMessages = 42; ud.totalMessages = 42
} }
const self = this; const self = this
this.UpdateCapabilities(); this.UpdateCapabilities()
this.isLoggedIn = this.userDetails.map( this.isLoggedIn = this.userDetails.map(
(user) => (user) =>
user.loggedIn && user.loggedIn &&
(self.apiIsOnline.data === "unknown" || self.apiIsOnline.data === "online"), (self.apiIsOnline.data === "unknown" || self.apiIsOnline.data === "online"),
[this.apiIsOnline] [this.apiIsOnline]
); )
this.isLoggedIn.addCallback((isLoggedIn) => { this.isLoggedIn.addCallback((isLoggedIn) => {
if (self.userDetails.data.loggedIn == false && isLoggedIn == true) { if (self.userDetails.data.loggedIn == false && isLoggedIn == true) {
// We have an inconsistency: the userdetails say we _didn't_ log in, but this actor says we do // We have an inconsistency: the userdetails say we _didn't_ log in, but this actor says we do
// This means someone attempted to toggle this; so we attempt to login! // This means someone attempted to toggle this; so we attempt to login!
self.AttemptLogin(); self.AttemptLogin()
} }
}); })
this._dryRun = options.dryRun ?? new UIEventSource<boolean>(false); this._dryRun = options.dryRun ?? new UIEventSource<boolean>(false)
this.updateAuthObject(); this.updateAuthObject()
this.preferencesHandler = new OsmPreferences( this.preferencesHandler = new OsmPreferences(
this.auth, this.auth,
<any /*This is needed to make the tests work*/>this <any /*This is needed to make the tests work*/>this
); )
if (options.oauth_token?.data !== undefined) { if (options.oauth_token?.data !== undefined) {
console.log(options.oauth_token.data); console.log(options.oauth_token.data)
const self = this; const self = this
this.auth.bootstrapToken( this.auth.bootstrapToken(
options.oauth_token.data, options.oauth_token.data,
(x) => { (x) => {
console.log("Called back: ", x); console.log("Called back: ", x)
self.AttemptLogin(); self.AttemptLogin()
}, },
this.auth this.auth
); )
options.oauth_token.setData(undefined); options.oauth_token.setData(undefined)
} }
if (this.auth.authenticated() && options.attemptLogin !== false) { if (this.auth.authenticated() && options.attemptLogin !== false) {
this.AttemptLogin(); // Also updates the user badge this.AttemptLogin() // Also updates the user badge
} else { } else {
console.log("Not authenticated"); console.log("Not authenticated")
} }
} }
@ -156,25 +153,25 @@ export class OsmConnection {
prefix?: string prefix?: string
} }
): UIEventSource<string> { ): UIEventSource<string> {
return this.preferencesHandler.GetPreference(key, defaultValue, options); return this.preferencesHandler.GetPreference(key, defaultValue, options)
} }
public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> { public GetLongPreference(key: string, prefix: string = "mapcomplete-"): UIEventSource<string> {
return this.preferencesHandler.GetLongPreference(key, prefix); return this.preferencesHandler.GetLongPreference(key, prefix)
} }
public OnLoggedIn(action: (userDetails: UserDetails) => void) { public OnLoggedIn(action: (userDetails: UserDetails) => void) {
this._onLoggedIn.push(action); this._onLoggedIn.push(action)
} }
public LogOut() { public LogOut() {
this.auth.logout(); this.auth.logout()
this.userDetails.data.loggedIn = false; this.userDetails.data.loggedIn = false
this.userDetails.data.csCount = 0; this.userDetails.data.csCount = 0
this.userDetails.data.name = ""; this.userDetails.data.name = ""
this.userDetails.ping(); this.userDetails.ping()
console.log("Logged out"); console.log("Logged out")
this.loadingStatus.setData("not-attempted"); this.loadingStatus.setData("not-attempted")
} }
/** /**
@ -183,95 +180,95 @@ export class OsmConnection {
* new OsmConnection().Backend() // => "https://www.openstreetmap.org" * new OsmConnection().Backend() // => "https://www.openstreetmap.org"
*/ */
public Backend(): string { public Backend(): string {
return this._oauth_config.url; return this._oauth_config.url
} }
public AttemptLogin() { public AttemptLogin() {
this.UpdateCapabilities(); this.UpdateCapabilities()
this.loadingStatus.setData("loading"); this.loadingStatus.setData("loading")
if (this.fakeUser) { if (this.fakeUser) {
this.loadingStatus.setData("logged-in"); this.loadingStatus.setData("logged-in")
console.log("AttemptLogin called, but ignored as fakeUser is set"); console.log("AttemptLogin called, but ignored as fakeUser is set")
return; return
} }
const self = this; const self = this
console.log("Trying to log in..."); console.log("Trying to log in...")
this.updateAuthObject(); this.updateAuthObject()
LocalStorageSource.Get("location_before_login").setData( LocalStorageSource.Get("location_before_login").setData(
Utils.runningFromConsole ? undefined : window.location.href Utils.runningFromConsole ? undefined : window.location.href
); )
this.auth.xhr( this.auth.xhr(
{ {
method: "GET", method: "GET",
path: "/api/0.6/user/details" path: "/api/0.6/user/details",
}, },
function (err, details) { function (err, details) {
if (err != null) { if (err != null) {
console.log(err); console.log(err)
self.loadingStatus.setData("error"); self.loadingStatus.setData("error")
if (err.status == 401) { if (err.status == 401) {
console.log("Clearing tokens..."); console.log("Clearing tokens...")
// Not authorized - our token probably got revoked // Not authorized - our token probably got revoked
self.auth.logout(); self.auth.logout()
self.LogOut(); self.LogOut()
} }
return; return
} }
if (details == null) { if (details == null) {
self.loadingStatus.setData("error"); self.loadingStatus.setData("error")
return; return
} }
self.CheckForMessagesContinuously(); self.CheckForMessagesContinuously()
// details is an XML DOM of user details // details is an XML DOM of user details
let userInfo = details.getElementsByTagName("user")[0]; let userInfo = details.getElementsByTagName("user")[0]
let data = self.userDetails.data; let data = self.userDetails.data
data.loggedIn = true; data.loggedIn = true
console.log("Login completed, userinfo is ", userInfo); console.log("Login completed, userinfo is ", userInfo)
data.name = userInfo.getAttribute("display_name"); data.name = userInfo.getAttribute("display_name")
data.account_created = userInfo.getAttribute("account_created"); data.account_created = userInfo.getAttribute("account_created")
data.uid = Number(userInfo.getAttribute("id")); data.uid = Number(userInfo.getAttribute("id"))
data.csCount = Number.parseInt( data.csCount = Number.parseInt(
userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? 0 userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? 0
); )
data.tracesCount = Number.parseInt( data.tracesCount = Number.parseInt(
userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? 0 userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? 0
); )
data.img = undefined; data.img = undefined
const imgEl = userInfo.getElementsByTagName("img"); const imgEl = userInfo.getElementsByTagName("img")
if (imgEl !== undefined && imgEl[0] !== undefined) { if (imgEl !== undefined && imgEl[0] !== undefined) {
data.img = imgEl[0].getAttribute("href"); data.img = imgEl[0].getAttribute("href")
} }
const description = userInfo.getElementsByTagName("description"); const description = userInfo.getElementsByTagName("description")
if (description !== undefined && description[0] !== undefined) { if (description !== undefined && description[0] !== undefined) {
data.description = description[0]?.innerHTML; data.description = description[0]?.innerHTML
} }
const homeEl = userInfo.getElementsByTagName("home"); const homeEl = userInfo.getElementsByTagName("home")
if (homeEl !== undefined && homeEl[0] !== undefined) { if (homeEl !== undefined && homeEl[0] !== undefined) {
const lat = parseFloat(homeEl[0].getAttribute("lat")); const lat = parseFloat(homeEl[0].getAttribute("lat"))
const lon = parseFloat(homeEl[0].getAttribute("lon")); const lon = parseFloat(homeEl[0].getAttribute("lon"))
data.home = { lat: lat, lon: lon }; data.home = { lat: lat, lon: lon }
} }
self.loadingStatus.setData("logged-in"); self.loadingStatus.setData("logged-in")
const messages = userInfo const messages = userInfo
.getElementsByTagName("messages")[0] .getElementsByTagName("messages")[0]
.getElementsByTagName("received")[0]; .getElementsByTagName("received")[0]
data.unreadMessages = parseInt(messages.getAttribute("unread")); data.unreadMessages = parseInt(messages.getAttribute("unread"))
data.totalMessages = parseInt(messages.getAttribute("count")); data.totalMessages = parseInt(messages.getAttribute("count"))
self.userDetails.ping(); self.userDetails.ping()
for (const action of self._onLoggedIn) { for (const action of self._onLoggedIn) {
action(self.userDetails.data); action(self.userDetails.data)
} }
self._onLoggedIn = []; self._onLoggedIn = []
} }
); )
} }
/** /**
@ -290,20 +287,20 @@ export class OsmConnection {
{ {
method, method,
options: { options: {
header header,
}, },
content, content,
path: `/api/0.6/${path}` path: `/api/0.6/${path}`,
}, },
function (err, response) { function (err, response) {
if (err !== null) { if (err !== null) {
error(err); error(err)
} else { } else {
ok(response); ok(response)
} }
} }
); )
}); })
} }
public async post( public async post(
@ -311,7 +308,7 @@ export class OsmConnection {
content?: string, content?: string,
header?: Record<string, string | number> header?: Record<string, string | number>
): Promise<any> { ): Promise<any> {
return await this.interact(path, "POST", header, content); return await this.interact(path, "POST", header, content)
} }
public async put( public async put(
@ -319,60 +316,60 @@ export class OsmConnection {
content?: string, content?: string,
header?: Record<string, string | number> header?: Record<string, string | number>
): Promise<any> { ): Promise<any> {
return await this.interact(path, "PUT", header, content); return await this.interact(path, "PUT", header, content)
} }
public async get(path: string, header?: Record<string, string | number>): Promise<any> { public async get(path: string, header?: Record<string, string | number>): Promise<any> {
return await this.interact(path, "GET", header); return await this.interact(path, "GET", header)
} }
public closeNote(id: number | string, text?: string): Promise<void> { public closeNote(id: number | string, text?: string): Promise<void> {
let textSuffix = ""; let textSuffix = ""
if ((text ?? "") !== "") { if ((text ?? "") !== "") {
textSuffix = "?text=" + encodeURIComponent(text); textSuffix = "?text=" + encodeURIComponent(text)
} }
if (this._dryRun.data) { if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually closing note ", id, " with text ", text); console.warn("Dryrun enabled - not actually closing note ", id, " with text ", text)
return new Promise((ok) => { return new Promise((ok) => {
ok(); ok()
}); })
} }
return this.post(`notes/${id}/close${textSuffix}`); return this.post(`notes/${id}/close${textSuffix}`)
} }
public reopenNote(id: number | string, text?: string): Promise<void> { public reopenNote(id: number | string, text?: string): Promise<void> {
if (this._dryRun.data) { if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually reopening note ", id, " with text ", text); console.warn("Dryrun enabled - not actually reopening note ", id, " with text ", text)
return new Promise((ok) => { return new Promise((ok) => {
ok(); ok()
}); })
} }
let textSuffix = ""; let textSuffix = ""
if ((text ?? "") !== "") { if ((text ?? "") !== "") {
textSuffix = "?text=" + encodeURIComponent(text); textSuffix = "?text=" + encodeURIComponent(text)
} }
return this.post(`notes/${id}/reopen${textSuffix}`); return this.post(`notes/${id}/reopen${textSuffix}`)
} }
public async openNote(lat: number, lon: number, text: string): Promise<{ id: number }> { public async openNote(lat: number, lon: number, text: string): Promise<{ id: number }> {
if (this._dryRun.data) { if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually opening note with text ", text); console.warn("Dryrun enabled - not actually opening note with text ", text)
return new Promise<{ id: number }>((ok) => { return new Promise<{ id: number }>((ok) => {
window.setTimeout( window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }), () => ok({ id: Math.floor(Math.random() * 1000) }),
Math.random() * 5000 Math.random() * 5000
); )
}); })
} }
// Lat and lon must be strings for the API to accept it // Lat and lon must be strings for the API to accept it
const content = `lat=${lat}&lon=${lon}&text=${encodeURIComponent(text)}` const content = `lat=${lat}&lon=${lon}&text=${encodeURIComponent(text)}`
const response = await this.post("notes.json", content, { const response = await this.post("notes.json", content, {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
}); })
const parsed = JSON.parse(response); const parsed = JSON.parse(response)
const id = parsed.properties; const id = parsed.properties
console.log("OPENED NOTE", id); console.log("OPENED NOTE", id)
return id; return id
} }
public async uploadGpxTrack( public async uploadGpxTrack(
@ -390,61 +387,61 @@ export class OsmConnection {
} }
): Promise<{ id: number }> { ): Promise<{ id: number }> {
if (this._dryRun.data) { if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually uploading GPX ", gpx); console.warn("Dryrun enabled - not actually uploading GPX ", gpx)
return new Promise<{ id: number }>((ok, error) => { return new Promise<{ id: number }>((ok, error) => {
window.setTimeout( window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }), () => ok({ id: Math.floor(Math.random() * 1000) }),
Math.random() * 5000 Math.random() * 5000
); )
}); })
} }
const contents = { const contents = {
file: gpx, file: gpx,
description: options.description ?? "", description: options.description ?? "",
tags: options.labels?.join(",") ?? "", tags: options.labels?.join(",") ?? "",
visibility: options.visibility visibility: options.visibility,
}; }
const extras = { const extras = {
file: file:
"; filename=\"" + '; filename="' +
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) + (options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
"\"\r\nContent-Type: application/gpx+xml" '"\r\nContent-Type: application/gpx+xml',
}; }
const boundary = "987654"; const boundary = "987654"
let body = ""; let body = ""
for (const key in contents) { for (const key in contents) {
body += "--" + boundary + "\r\n"; body += "--" + boundary + "\r\n"
body += "Content-Disposition: form-data; name=\"" + key + "\""; body += 'Content-Disposition: form-data; name="' + key + '"'
if (extras[key] !== undefined) { if (extras[key] !== undefined) {
body += extras[key]; body += extras[key]
} }
body += "\r\n\r\n"; body += "\r\n\r\n"
body += contents[key] + "\r\n"; body += contents[key] + "\r\n"
} }
body += "--" + boundary + "--\r\n"; body += "--" + boundary + "--\r\n"
const response = await this.post("gpx/create", body, { const response = await this.post("gpx/create", body, {
"Content-Type": "multipart/form-data; boundary=" + boundary, "Content-Type": "multipart/form-data; boundary=" + boundary,
"Content-Length": body.length "Content-Length": body.length,
}); })
const parsed = JSON.parse(response); const parsed = JSON.parse(response)
console.log("Uploaded GPX track", parsed); console.log("Uploaded GPX track", parsed)
return { id: parsed }; return { id: parsed }
} }
public addCommentToNote(id: number | string, text: string): Promise<void> { public addCommentToNote(id: number | string, text: string): Promise<void> {
if (this._dryRun.data) { if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually adding comment ", text, "to note ", id); console.warn("Dryrun enabled - not actually adding comment ", text, "to note ", id)
return new Promise((ok) => { return new Promise((ok) => {
ok(); ok()
}); })
} }
if ((text ?? "") === "") { if ((text ?? "") === "") {
throw "Invalid text!"; throw "Invalid text!"
} }
return new Promise((ok, error) => { return new Promise((ok, error) => {
@ -452,17 +449,17 @@ export class OsmConnection {
{ {
method: "POST", method: "POST",
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}` path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
}, },
function (err, _) { function (err, _) {
if (err !== null) { if (err !== null) {
error(err); error(err)
} else { } else {
ok(); ok()
} }
} }
); )
}); })
} }
/** /**
@ -471,31 +468,31 @@ export class OsmConnection {
public finishLogin(callback: (previousURL: string) => void) { public finishLogin(callback: (previousURL: string) => void) {
this.auth.authenticate(function () { this.auth.authenticate(function () {
// Fully authed at this point // Fully authed at this point
console.log("Authentication successful!"); console.log("Authentication successful!")
const previousLocation = LocalStorageSource.Get("location_before_login"); const previousLocation = LocalStorageSource.Get("location_before_login")
callback(previousLocation.data); callback(previousLocation.data)
}); })
} }
private updateAuthObject() { private updateAuthObject() {
let pwaStandAloneMode = false; let pwaStandAloneMode = false
try { try {
if (Utils.runningFromConsole) { if (Utils.runningFromConsole) {
pwaStandAloneMode = true; pwaStandAloneMode = true
} else if ( } else if (
window.matchMedia("(display-mode: standalone)").matches || window.matchMedia("(display-mode: standalone)").matches ||
window.matchMedia("(display-mode: fullscreen)").matches window.matchMedia("(display-mode: fullscreen)").matches
) { ) {
pwaStandAloneMode = true; pwaStandAloneMode = true
} }
} catch (e) { } catch (e) {
console.warn( console.warn(
"Detecting standalone mode failed", "Detecting standalone mode failed",
e, e,
". Assuming in browser and not worrying furhter" ". Assuming in browser and not worrying furhter"
); )
} }
const standalone = this._iframeMode || pwaStandAloneMode || !this._singlePage; const standalone = this._iframeMode || pwaStandAloneMode || !this._singlePage
// In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway... // In standalone mode, we DON'T use single page login, as 'redirecting' opens a new window anyway...
// Same for an iframe... // Same for an iframe...
@ -508,46 +505,46 @@ export class OsmConnection {
? "https://mapcomplete.org/land.html" ? "https://mapcomplete.org/land.html"
: window.location.protocol + "//" + window.location.host + "/land.html", : window.location.protocol + "//" + window.location.host + "/land.html",
singlepage: !standalone, singlepage: !standalone,
auto: true auto: true,
}); })
} }
private CheckForMessagesContinuously() { private CheckForMessagesContinuously() {
const self = this; const self = this
if (this.isChecking) { if (this.isChecking) {
return; return
} }
this.isChecking = true; this.isChecking = true
Stores.Chronic(5 * 60 * 1000).addCallback((_) => { Stores.Chronic(5 * 60 * 1000).addCallback((_) => {
if (self.isLoggedIn.data) { if (self.isLoggedIn.data) {
console.log("Checking for messages"); console.log("Checking for messages")
self.AttemptLogin(); self.AttemptLogin()
} }
}); })
} }
private UpdateCapabilities(): void { private UpdateCapabilities(): void {
const self = this; const self = this
this.FetchCapabilities().then(({ api, gpx }) => { this.FetchCapabilities().then(({ api, gpx }) => {
self.apiIsOnline.setData(api); self.apiIsOnline.setData(api)
self.gpxServiceIsOnline.setData(gpx); self.gpxServiceIsOnline.setData(gpx)
}); })
} }
private async FetchCapabilities(): Promise<{ api: OsmServiceState; gpx: OsmServiceState }> { private async FetchCapabilities(): Promise<{ api: OsmServiceState; gpx: OsmServiceState }> {
if (Utils.runningFromConsole) { if (Utils.runningFromConsole) {
return { api: "online", gpx: "online" }; return { api: "online", gpx: "online" }
} }
const result = await Utils.downloadAdvanced(this.Backend() + "/api/0.6/capabilities"); const result = await Utils.downloadAdvanced(this.Backend() + "/api/0.6/capabilities")
if (result["content"] === undefined) { if (result["content"] === undefined) {
console.log("Something went wrong:", result); console.log("Something went wrong:", result)
return { api: "unreachable", gpx: "unreachable" }; return { api: "unreachable", gpx: "unreachable" }
} }
const xmlRaw = result["content"]; const xmlRaw = result["content"]
const parsed = new DOMParser().parseFromString(xmlRaw, "text/xml"); const parsed = new DOMParser().parseFromString(xmlRaw, "text/xml")
const statusEl = parsed.getElementsByTagName("status")[0]; const statusEl = parsed.getElementsByTagName("status")[0]
const api = <OsmServiceState>statusEl.getAttribute("api"); const api = <OsmServiceState>statusEl.getAttribute("api")
const gpx = <OsmServiceState>statusEl.getAttribute("gpx"); const gpx = <OsmServiceState>statusEl.getAttribute("gpx")
return { api, gpx }; return { api, gpx }
} }
} }