forked from MapComplete/MapComplete
		
	Only override keys
This commit is contained in:
		
							parent
							
								
									0e531ca017
								
							
						
					
					
						commit
						ba909ea0ec
					
				
					 1 changed files with 512 additions and 515 deletions
				
			
		|  | @ -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 } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue