Android: get login working

This commit is contained in:
Pieter Vander Vennet 2024-12-31 19:55:08 +01:00
parent 00c233a2eb
commit 88c76498b6
16 changed files with 199 additions and 171 deletions

@ -1 +1 @@
Subproject commit b165ec6005cb5e9fcc7c1c2bb860344287f3d281 Subproject commit 917fe6a0f9ef67530f281d5603432f9c8daae0c7

View file

@ -3,6 +3,7 @@
<head><title>MapComplete Auth</title></head> <head><title>MapComplete Auth</title></head>
<body> <body>
Authorizing and redirecting, hang on... Authorizing and redirecting, hang on...
<div id="token"></div>
<script type="module" src="./land.ts"></script> <script type="module" src="./land.ts"></script>
</body> </body>
</html> </html>

View file

@ -1,11 +1,17 @@
import { OsmConnection } from "../src/Logic/Osm/OsmConnection" import { OsmConnection } from "../src/Logic/Osm/OsmConnection"
import Constants from "../src/Models/Constants" import Constants from "../src/Models/Constants"
import { Utils } from "../src/Utils"
import { UIEventSource } from "../src/Logic/UIEventSource"
import { VariableUiElement } from "../src/UI/Base/VariableUIElement"
console.log("Authorizing...") console.log("Authorizing...")
const key = Constants.osmAuthConfig.url + "oauth2_state" const key = Constants.osmAuthConfig.url + "oauth2_state"
const st =window.localStorage.getItem(key ) const st =window.localStorage.getItem(key )
console.log("Prev state is",key, st) console.log("Prev state is",key, st)
new OsmConnection().finishLogin((_, token: string) => { const tokenSrc = new UIEventSource("")
console.log("Login finished, redirecting to passthrough") new VariableUiElement(tokenSrc).AttachTo("token")
new OsmConnection().finishLogin(async (_, token: string) => {
console.log("Login finished, redirecting to passthrough; token is "+token)
await Utils.waitFor(10)
window.location.href = "https://app.mapcomplete.org/passthrough.html?oauth_token="+token window.location.href = "https://app.mapcomplete.org/passthrough.html?oauth_token="+token
}) })

View file

@ -49,6 +49,7 @@ cp -r dist/assets/templates dist-full/assets
cp -r dist/assets/themes dist-full/assets cp -r dist/assets/themes dist-full/assets
# mkdir dist-full/assets/generated # mkdir dist-full/assets/generated
nvm use
# assets/icon-only.png will be used as the app icon # assets/icon-only.png will be used as the app icon
# See https://capacitorjs.com/docs/guides/splash-screens-and-icons # See https://capacitorjs.com/docs/guides/splash-screens-and-icons
@ -56,4 +57,4 @@ npx capacitor-assets generate
npx cap sync npx cap sync
echo "All done! Don't forget to click 'gradly sync files' in Android Studio" echo "All done! Don't forget to click 'gradle sync files' in Android Studio"

View file

@ -6,6 +6,7 @@ import Constants from "../../Models/Constants"
import { Changes } from "./Changes" import { Changes } from "./Changes"
import { Utils } from "../../Utils" import { Utils } from "../../Utils"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore" import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
export interface ChangesetTag { export interface ChangesetTag {
key: string key: string
@ -52,7 +53,7 @@ export class ChangesetHandler {
| { addAlias: (id0: string, id1: string) => void } | { addAlias: (id0: string, id1: string) => void }
| undefined, | undefined,
changes: Changes, changes: Changes,
reportError: (e: string | Error, extramessage: string) => void reportError: (e: string | Error, extramessage: string) => void,
) { ) {
this.osmConnection = osmConnection this.osmConnection = osmConnection
this._reportError = reportError this._reportError = reportError
@ -113,7 +114,7 @@ export class ChangesetHandler {
private async UploadWithNew( private async UploadWithNew(
generateChangeXML: (csid: number, remappings: Map<string, string>) => string, generateChangeXML: (csid: number, remappings: Map<string, string>) => string,
openChangeset: UIEventSource<number>, openChangeset: UIEventSource<number>,
extraMetaTags: ChangesetTag[] extraMetaTags: ChangesetTag[],
) { ) {
const csId = await this.OpenChangeset(extraMetaTags) const csId = await this.OpenChangeset(extraMetaTags)
openChangeset.setData(csId) openChangeset.setData(csId)
@ -121,7 +122,7 @@ export class ChangesetHandler {
console.log( console.log(
"Opened a new changeset (openChangeset.data is undefined):", "Opened a new changeset (openChangeset.data is undefined):",
changeset, changeset,
extraMetaTags extraMetaTags,
) )
const changes = await this.UploadChange(csId, changeset) const changes = await this.UploadChange(csId, changeset)
const hasSpecialMotivationChanges = ChangesetHandler.rewriteMetaTags(extraMetaTags, changes) const hasSpecialMotivationChanges = ChangesetHandler.rewriteMetaTags(extraMetaTags, changes)
@ -144,7 +145,7 @@ export class ChangesetHandler {
public async UploadChangeset( public async UploadChangeset(
generateChangeXML: (csid: number, remappings: Map<string, string>) => string, generateChangeXML: (csid: number, remappings: Map<string, string>) => string,
extraMetaTags: ChangesetTag[], extraMetaTags: ChangesetTag[],
openChangeset: UIEventSource<number> openChangeset: UIEventSource<number>,
): Promise<void> { ): Promise<void> {
if ( if (
!extraMetaTags.some((tag) => tag.key === "comment") || !extraMetaTags.some((tag) => tag.key === "comment") ||
@ -179,13 +180,13 @@ export class ChangesetHandler {
try { try {
const rewritings = await this.UploadChange( const rewritings = await this.UploadChange(
csId, csId,
generateChangeXML(csId, this._remappings) generateChangeXML(csId, this._remappings),
) )
const rewrittenTags = this.RewriteTagsOf( const rewrittenTags = this.RewriteTagsOf(
extraMetaTags, extraMetaTags,
rewritings, rewritings,
oldChangesetMeta oldChangesetMeta,
) )
await this.UpdateTags(csId, rewrittenTags) await this.UpdateTags(csId, rewrittenTags)
return // We are done! return // We are done!
@ -196,7 +197,7 @@ export class ChangesetHandler {
} catch (e) { } catch (e) {
this._reportError( this._reportError(
e, e,
"While getting metadata from a changeset " + openChangeset.data "While getting metadata from a changeset " + openChangeset.data,
) )
} }
} }
@ -224,7 +225,7 @@ export class ChangesetHandler {
console.warn( console.warn(
"Could not open/upload changeset due to ", "Could not open/upload changeset due to ",
e, e,
"trying again with a another fresh changeset " "trying again with a another fresh changeset ",
) )
openChangeset.setData(undefined) openChangeset.setData(undefined)
@ -250,7 +251,7 @@ export class ChangesetHandler {
uid: number // User ID uid: number // User ID
changes_count: number changes_count: number
tags: any tags: any
} },
): ChangesetTag[] { ): ChangesetTag[] {
// Note: extraMetaTags is where all the tags are collected into // Note: extraMetaTags is where all the tags are collected into
@ -387,7 +388,7 @@ export class ChangesetHandler {
tag.key !== undefined && tag.key !== undefined &&
tag.value !== undefined && tag.value !== undefined &&
tag.key !== "" && tag.key !== "" &&
tag.value !== "" tag.value !== "",
) )
const metadata = tags.map((kv) => `<tag k="${kv.key}" v="${escapeHtml(kv.value)}"/>`) const metadata = tags.map((kv) => `<tag k="${kv.key}" v="${escapeHtml(kv.value)}"/>`)
const content = [`<osm><changeset>`, metadata, `</changeset></osm>`].join("") const content = [`<osm><changeset>`, metadata, `</changeset></osm>`].join("")
@ -398,10 +399,16 @@ export class ChangesetHandler {
const usedGps = this.changes.state["currentUserLocation"]?.features?.data?.length > 0 const usedGps = this.changes.state["currentUserLocation"]?.features?.data?.length > 0
const hasMorePrivacy = !!this.changes.state?.featureSwitches?.featureSwitchMorePrivacy?.data const hasMorePrivacy = !!this.changes.state?.featureSwitches?.featureSwitchMorePrivacy?.data
const setSourceAsSurvey = !hasMorePrivacy && usedGps const setSourceAsSurvey = !hasMorePrivacy && usedGps
let shell = ""
let host = `${window.location.origin}${window.location.pathname}`
if (AndroidPolyfill.inAndroid.data) {
shell = " (Android)"
host = "https://mapcomplete.org/" + window.location.pathname
}
return [ return [
["created_by", `MapComplete ${Constants.vNumber}`], ["created_by", `MapComplete ${Constants.vNumber}${shell}`],
["locale", Locale.language.data], ["locale", Locale.language.data],
["host", `${window.location.origin}${window.location.pathname}`], // Note: deferred changes might give a different hostpath then the theme with which the changes were made ["host", host], // Note: deferred changes might give a different hostpath then the theme with which the changes were made
["source", setSourceAsSurvey ? "survey" : undefined], ["source", setSourceAsSurvey ? "survey" : undefined],
["imagery", this.changes.state["backgroundLayer"]?.data?.id], ["imagery", this.changes.state["backgroundLayer"]?.data?.id],
].map(([key, value]) => ({ ].map(([key, value]) => ({
@ -427,7 +434,7 @@ export class ChangesetHandler {
const csId = await this.osmConnection.put( const csId = await this.osmConnection.put(
"changeset/create", "changeset/create",
[`<osm><changeset>`, metadata, `</changeset></osm>`].join(""), [`<osm><changeset>`, metadata, `</changeset></osm>`].join(""),
{ "Content-Type": "text/xml" } { "Content-Type": "text/xml" },
) )
return Number(csId) return Number(csId)
} }
@ -437,12 +444,12 @@ export class ChangesetHandler {
*/ */
private async UploadChange( private async UploadChange(
changesetId: number, changesetId: number,
changesetXML: string changesetXML: string,
): Promise<Map<string, string>> { ): Promise<Map<string, string>> {
const response = await this.osmConnection.post<XMLDocument>( const response = await this.osmConnection.post<XMLDocument>(
"changeset/" + changesetId + "/upload", "changeset/" + changesetId + "/upload",
changesetXML, changesetXML,
{ "Content-Type": "text/xml" } { "Content-Type": "text/xml" },
) )
const changes = this.parseUploadChangesetResponse(response) const changes = this.parseUploadChangesetResponse(response)
console.log("Uploaded changeset ", changesetId) console.log("Uploaded changeset ", changesetId)

View file

@ -5,41 +5,70 @@ import { Utils } from "../../Utils"
import { LocalStorageSource } from "../Web/LocalStorageSource" import { LocalStorageSource } from "../Web/LocalStorageSource"
import { AuthConfig } from "./AuthConfig" import { AuthConfig } from "./AuthConfig"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
import { Feature, Point } from "geojson" import { Feature, Point } from "geojson"
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
import { QueryParameters } from "../Web/QueryParameters"
interface OsmUserInfo { interface OsmUserInfo {
id: number
display_name: string
account_created: string
description: string
contributor_terms: { agreed: boolean }
roles: []
changesets: { count: number }
traces: { count: number }
blocks: { received: { count: number; active: number } }
}
export default class UserDetails { "id": number,
public loggedIn = false "display_name": string,
public name = "Not logged in" "account_created": string,
public uid: number "description": string,
public csCount = 0 "contributor_terms": {
public img?: string "agreed": boolean,
public unreadMessages = 0 "pd": boolean
public totalMessages: number = 0 },
public home: { lon: number; lat: number } "img": {
public backend: string "href": string,
public account_created: string },
public tracesCount: number = 0 "roles": string[],
public description: string "changesets": {
public languages: string[] "count": number
},
constructor(backend: string) { "traces": {
this.backend = backend "count": number
},
"blocks": {
"received": {
"count": number,
"active": number
} }
},
"home": {
"lat": number,
"lon": number,
"zoom": number
},
"languages": string[],
"messages": {
"received": {
"count": number,
"unread": number
},
"sent": {
"count": number
}
}
} }
export default interface UserDetails {
loggedIn: boolean
name: string
uid: number
csCount: number
img?: string
unreadMessages: number
totalMessages: number
home?: { lon: number; lat: number }
backend: string
account_created: string
tracesCount: number
description?: string
languages: string[]
}
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable" export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
interface CapabilityResult { interface CapabilityResult {
@ -77,7 +106,10 @@ interface CapabilityResult {
export class OsmConnection { export class OsmConnection {
public auth: osmAuth public auth: osmAuth
public userDetails: UIEventSource<UserDetails> /**
* Details of the currently logged-in user; undefined if not logged in
*/
public userDetails: UIEventSource<UserDetails | undefined>
public isLoggedIn: Store<boolean> public isLoggedIn: Store<boolean>
public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>( public gpxServiceIsOnline: UIEventSource<OsmServiceState> = new UIEventSource<OsmServiceState>(
"unknown", "unknown",
@ -93,7 +125,6 @@ export class OsmConnection {
public readonly _oauth_config: AuthConfig public readonly _oauth_config: AuthConfig
private readonly _dryRun: Store<boolean> private readonly _dryRun: Store<boolean>
private readonly fakeUser: boolean private readonly fakeUser: boolean
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []
private readonly _iframeMode: boolean private readonly _iframeMode: boolean
private readonly _singlePage: boolean private readonly _singlePage: boolean
private isChecking = false private isChecking = false
@ -129,10 +160,7 @@ export class OsmConnection {
this._oauth_config.oauth_secret = import.meta.env.VITE_OSM_OAUTH_SECRET this._oauth_config.oauth_secret = import.meta.env.VITE_OSM_OAUTH_SECRET
} }
this.userDetails = new UIEventSource<UserDetails>( this.userDetails = new UIEventSource<UserDetails>(undefined, "userDetails")
new UserDetails(this._oauth_config.url),
"userDetails",
)
if (options.fakeUser) { if (options.fakeUser) {
const ud = this.userDetails.data const ud = this.userDetails.data
ud.csCount = 5678 ud.csCount = 5678
@ -146,25 +174,21 @@ export class OsmConnection {
"The 'fake-user' is a URL-parameter which allows to test features without needing an OSM account or even internet connection." "The 'fake-user' is a URL-parameter which allows to test features without needing an OSM account or even internet connection."
this.loadingStatus.setData("logged-in") this.loadingStatus.setData("logged-in")
} }
this.UpdateCapabilities() this.updateCapabilities()
this.isLoggedIn = this.userDetails.map( this.isLoggedIn = this.userDetails.map(
(user) => (user) =>
user.loggedIn && !!user &&
(this.apiIsOnline.data === "unknown" || this.apiIsOnline.data === "online"), (this.apiIsOnline.data === "unknown" || this.apiIsOnline.data === "online"),
[this.apiIsOnline], [this.apiIsOnline],
) )
this.isLoggedIn.addCallback((isLoggedIn) => {
if (this.userDetails.data.loggedIn == false && isLoggedIn == true) {
// 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.AttemptLogin()
}
})
this._dryRun = options.dryRun ?? new UIEventSource<boolean>(false) this._dryRun = options.dryRun ?? new UIEventSource<boolean>(false)
this.updateAuthObject() this.createAuthObject()
AndroidPolyfill.inAndroid.addCallback(() => {
this.createAuthObject()
})
if (!this.fakeUser) { if (!this.fakeUser) {
this.CheckForMessagesContinuously() this.CheckForMessagesContinuously()
} }
@ -209,9 +233,6 @@ export class OsmConnection {
return <UIEventSource<T>>this.preferencesHandler.getPreference(key, defaultValue, prefix) return <UIEventSource<T>>this.preferencesHandler.getPreference(key, defaultValue, prefix)
} }
public OnLoggedIn(action: (userDetails: UserDetails) => void) {
this._onLoggedIn.push(action)
}
public LogOut() { public LogOut() {
this.auth.logout() this.auth.logout()
@ -233,7 +254,7 @@ export class OsmConnection {
} }
public AttemptLogin() { public AttemptLogin() {
this.UpdateCapabilities() this.updateCapabilities()
if (this.loadingStatus.data !== "logged-in") { if (this.loadingStatus.data !== "logged-in") {
// Stay 'logged-in' if we are already logged in; this simply means we are checking for messages // Stay 'logged-in' if we are already logged in; this simply means we are checking for messages
this.loadingStatus.setData("loading") this.loadingStatus.setData("loading")
@ -245,18 +266,51 @@ export class OsmConnection {
} }
console.log("Trying to log in...") console.log("Trying to log in...")
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.authenticate((err, result) => {
method: "GET", if (!err) {
path: "/api/0.6/user/details", this.loadUserInfo()
}, }
(err, details: XMLDocument) => { })
if (err != null) {
}
private async loadUserInfo() {
try {
const result = await this.interact("user/details.json")
if (result === null) {
this.loadingStatus.setData("error")
return
}
const data = <{
"version": "0.6",
"license": "http://opendatacommons.org/licenses/odbl/1-0/",
"user": OsmUserInfo
}>JSON.parse(result)
const user = data.user
const userdetails: UserDetails = {
uid: user.id,
name: user.display_name,
csCount: user.changesets.count,
description: user.description,
loggedIn: true,
backend: this.Backend(),
home: user.home,
languages: user.languages,
totalMessages: user.messages.received?.count ?? 0,
img: user.img?.href,
account_created: user.account_created,
tracesCount: user.traces.count,
unreadMessages: user.messages.received?.unread ?? 0,
}
console.log("Login completed, userinfo is ", userdetails)
this.userDetails.set(userdetails)
this.loadingStatus.setData("logged-in")
} catch (err) {
console.log("Could not login due to:", err) console.log("Could not login due to:", err)
this.loadingStatus.setData("error") this.loadingStatus.setData("error")
if (err.status == 401) { if (err.status == 401) {
@ -268,64 +322,7 @@ export class OsmConnection {
console.log("Other error. Status:", err.status) console.log("Other error. Status:", err.status)
this.apiIsOnline.setData("unreachable") this.apiIsOnline.setData("unreachable")
} }
return
} }
if (details == null) {
this.loadingStatus.setData("error")
return
}
// details is an XML DOM of user details
const userInfo = details.getElementsByTagName("user")[0]
const data = this.userDetails.data
data.loggedIn = true
console.log("Login completed, userinfo is ", userInfo)
data.name = userInfo.getAttribute("display_name")
data.account_created = userInfo.getAttribute("account_created")
data.uid = Number(userInfo.getAttribute("id"))
data.languages = Array.from(
userInfo.getElementsByTagName("languages")[0].getElementsByTagName("lang"),
).map((l) => l.textContent)
data.csCount = Number.parseInt(
userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? "0",
)
data.tracesCount = Number.parseInt(
userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? "0",
)
data.img = undefined
const imgEl = userInfo.getElementsByTagName("img")
if (imgEl !== undefined && imgEl[0] !== undefined) {
data.img = imgEl[0].getAttribute("href")
}
const description = userInfo.getElementsByTagName("description")
if (description !== undefined && description[0] !== undefined) {
data.description = description[0]?.innerHTML
}
const homeEl = userInfo.getElementsByTagName("home")
if (homeEl !== undefined && homeEl[0] !== undefined) {
const lat = parseFloat(homeEl[0].getAttribute("lat"))
const lon = parseFloat(homeEl[0].getAttribute("lon"))
data.home = { lat: lat, lon: lon }
}
this.loadingStatus.setData("logged-in")
const messages = userInfo
.getElementsByTagName("messages")[0]
.getElementsByTagName("received")[0]
data.unreadMessages = parseInt(messages.getAttribute("unread"))
data.totalMessages = parseInt(messages.getAttribute("count"))
this.userDetails.ping()
for (const action of this._onLoggedIn) {
action(this.userDetails.data)
}
this._onLoggedIn = []
},
)
} }
/** /**
@ -339,7 +336,7 @@ export class OsmConnection {
*/ */
public async interact( public async interact(
path: string, path: string,
method: "GET" | "POST" | "PUT" | "DELETE", method: "GET" | "POST" | "PUT" | "DELETE" = "GET",
header?: Record<string, string>, header?: Record<string, string>,
content?: string, content?: string,
allowAnonymous: boolean = false, allowAnonymous: boolean = false,
@ -359,6 +356,11 @@ export class OsmConnection {
throw "Could not interact with OSM:" + possibleResult["error"] throw "Could not interact with OSM:" + possibleResult["error"]
} }
if (!this.auth.authenticated()) {
console.trace("Not authenticated")
await Utils.waitFor(10000)
}
return new Promise((ok, error) => { return new Promise((ok, error) => {
connection.xhr( connection.xhr(
{ {
@ -504,7 +506,7 @@ export class OsmConnection {
(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",
} }
user
const boundary = "987654" const boundary = "987654"
let body = "" let body = ""
@ -563,19 +565,29 @@ export class OsmConnection {
this.auth.authenticate(() => { this.auth.authenticate(() => {
// Fully authed at this point // Fully authed at this point
console.log("Authentication successful!") console.log("Authentication successful!")
const oauth_token = window.localStorage.getItem(this._oauth_config.url + "oauth2_access_token") const oauth_token = QueryParameters.GetQueryParameter("oauth_token", undefined).data ?? window.localStorage.getItem(this._oauth_config.url + "oauth2_access_token")
const previousLocation = LocalStorageSource.get("location_before_login") const previousLocation = LocalStorageSource.get("location_before_login")
callback(previousLocation.data, oauth_token) callback(previousLocation.data, oauth_token)
}) })
} }
private updateAuthObject() { private async loginAndroidPolyfill() {
const token = await AndroidPolyfill.requestLoginCodes()
console.log("Got login token!", token)
localStorage.setItem("https://www.openstreetmap.orgoauth2_access_token", token)
if (this.auth.authenticated()) {
console.log("Logged in!")
}
await this.loadUserInfo()
}
private createAuthObject() {
let redirect_uri = Utils.runningFromConsole let redirect_uri = Utils.runningFromConsole
? "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"
if (AndroidPolyfill.inAndroid.data) { if (AndroidPolyfill.inAndroid.data) {
redirect_uri = "https://app.mapcomplete.org/land.html" redirect_uri = "https://app.mapcomplete.org/land.html"
AndroidPolyfill.requestLoginCodes(this)
} }
this.auth = new osmAuth({ this.auth = new osmAuth({
client_id: this._oauth_config.oauth_client_id, client_id: this._oauth_config.oauth_client_id,
@ -586,9 +598,12 @@ export class OsmConnection {
* However, this breaks in iframes so we open a popup in that case * However, this breaks in iframes so we open a popup in that case
*/ */
singlepage: !this._iframeMode && !AndroidPolyfill.inAndroid.data, singlepage: !this._iframeMode && !AndroidPolyfill.inAndroid.data,
auto: true, auto: false,
apiUrl: this._oauth_config.api_url ?? this._oauth_config.url, apiUrl: this._oauth_config.api_url ?? this._oauth_config.url,
}) })
if (AndroidPolyfill.inAndroid.data) {
this.loginAndroidPolyfill() // NO AWAIT!
}
} }
private CheckForMessagesContinuously() { private CheckForMessagesContinuously() {
@ -611,9 +626,10 @@ export class OsmConnection {
return return
} }
Stores.Chronic(60 * 5 * 1000).addCallback(() => { Stores.Chronic(60 * 5 * 1000).addCallback(() => {
// Check for new messages every 5 minutes
if (this.isLoggedIn.data) { if (this.isLoggedIn.data) {
try { try {
this.AttemptLogin() this.loadUserInfo()
} catch (e) { } catch (e) {
console.log("Could not login due to", e) console.log("Could not login due to", e)
} }
@ -621,11 +637,11 @@ export class OsmConnection {
}) })
} }
private UpdateCapabilities(): void { private updateCapabilities(): void {
if (this.fakeUser) { if (this.fakeUser) {
return return
} }
this.FetchCapabilities().then(({ api, gpx }) => { this.fetchCapabilities().then(({ api, gpx }) => {
this.apiIsOnline.setData(api) this.apiIsOnline.setData(api)
this.gpxServiceIsOnline.setData(gpx) this.gpxServiceIsOnline.setData(gpx)
}) })
@ -646,7 +662,8 @@ export class OsmConnection {
return parsed return parsed
} }
private async FetchCapabilities(): Promise<{ /**Does not use the OSM-auth object*/
private async fetchCapabilities(): Promise<{
api: OsmServiceState api: OsmServiceState
gpx: OsmServiceState gpx: OsmServiceState
database: OsmServiceState database: OsmServiceState
@ -656,7 +673,7 @@ export class OsmConnection {
} }
try { try {
const result = await Utils.downloadJson<CapabilityResult>( const result = await Utils.downloadJson<CapabilityResult>(
this.Backend() + "/api/0.6/capabilities.json" this.Backend() + "/api/0.6/capabilities.json",
) )
if (result?.api?.status === undefined) { if (result?.api?.status === undefined) {
console.log("Something went wrong:", result) console.log("Something went wrong:", result)

View file

@ -32,7 +32,7 @@ export class OsmPreferences {
this.auth = auth this.auth = auth
this._fakeUser = fakeUser this._fakeUser = fakeUser
this.osmConnection = osmConnection this.osmConnection = osmConnection
osmConnection.OnLoggedIn(() => { osmConnection.userDetails.addCallbackAndRunD(() => {
this.loadBulkPreferences() this.loadBulkPreferences()
return true return true
}) })

View file

@ -7,6 +7,7 @@ import themeOverview from "../../assets/generated/theme_overview.json"
import LayerSearch from "./LayerSearch" import LayerSearch from "./LayerSearch"
import SearchUtils from "./SearchUtils" import SearchUtils from "./SearchUtils"
import { OsmConnection } from "../Osm/OsmConnection" import { OsmConnection } from "../Osm/OsmConnection"
import { AndroidPolyfill } from "../Web/AndroidPolyfill"
type ThemeSearchScore = { type ThemeSearchScore = {
theme: MinimalThemeInformation theme: MinimalThemeInformation
@ -82,7 +83,7 @@ export default class ThemeSearch {
} }
let linkPrefix = `${path}/${layout.id.toLowerCase()}.html?` let linkPrefix = `${path}/${layout.id.toLowerCase()}.html?`
if (location.hostname === "localhost" || location.hostname === "127.0.0.1") { if ((location.hostname === "localhost" && !AndroidPolyfill.inAndroid.data) || location.hostname === "127.0.0.1") {
linkPrefix = `${path}/theme.html?layout=${layout.id}&` linkPrefix = `${path}/theme.html?layout=${layout.id}&`
} }

View file

@ -182,9 +182,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
let testingDefaultValue = false let testingDefaultValue = false
if ( if (
!Utils.runningFromConsole && !Constants.osmAuthConfig.url.startsWith("https://master.apis.dev.openstreetmap.org") && (location.hostname === "127.0.0.1") && !Utils.runningFromConsole
(location.hostname === "localhost" || location.hostname === "127.0.0.1") &&
!Constants.osmAuthConfig.url.startsWith("https://master.apis.dev.openstreetmap.org")
) { ) {
testingDefaultValue = true testingDefaultValue = true
} }

View file

@ -51,13 +51,11 @@ export class AndroidPolyfill {
this.backfillGeolocation(this.databridgePlugin) this.backfillGeolocation(this.databridgePlugin)
} }
public static async requestLoginCodes(osmConnection: OsmConnection) { public static async requestLoginCodes() {
const result = await DatabridgePluginSingleton.request<{oauth_token: string}>({ key: "request:login" }) const result = await DatabridgePluginSingleton.request<{oauth_token: string}>({ key: "request:login" })
const token: string = result.value.oauth_token const token: string = result.value.oauth_token
console.log("AndroidPolyfill: received code and state; trying to pass them to the oauth lib",token) console.log("AndroidPolyfill: received oauth_token; trying to pass them to the oauth lib",token)
const auth = osmConnection.auth.bootstrapToken(token, (err, result) => { return token
console.log("AndroidPolyFill: bootstraptoken returned", JSON.stringify({err, result}))
})
} }
} }

View file

@ -41,7 +41,7 @@
const tu = Translations.t.general const tu = Translations.t.general
const tr = Translations.t.general.morescreen const tr = Translations.t.general.morescreen
let userLanguages = osmConnection.userDetails.map((ud) => ud.languages) let userLanguages = osmConnection.userDetails.mapD((ud) => ud.languages)
let search: UIEventSource<string | undefined> = new UIEventSource<string>("") let search: UIEventSource<string | undefined> = new UIEventSource<string>("")
let searchStable = search.stabilized(100) let searchStable = search.stabilized(100)
@ -58,7 +58,7 @@
hiddenThemes.filter( hiddenThemes.filter(
(theme) => (theme) =>
knownIds.indexOf(theme.id) >= 0 || knownIds.indexOf(theme.id) >= 0 ||
state.osmConnection.userDetails.data.name === "Pieter Vander Vennet" state.osmConnection.userDetails.data?.name === "Pieter Vander Vennet"
) )
) )

View file

@ -14,7 +14,7 @@
josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined)) josmState.stabilized(15000).addCallbackD(() => josmState.setData(undefined))
const showButton = state.osmConnection.userDetails.map( const showButton = state.osmConnection.userDetails.map(
(ud) => ud.loggedIn && ud.csCount >= Constants.userJourney.historyLinkVisible (ud) => ud?.csCount >= Constants.userJourney.historyLinkVisible
) )
function openJosm() { function openJosm() {

View file

@ -44,7 +44,7 @@
const imageInfo = await panoramax.imageInfo(image.id) const imageInfo = await panoramax.imageInfo(image.id)
let reporter_email: string = undefined let reporter_email: string = undefined
const userdetails = state.userRelatedState.osmConnection.userDetails const userdetails = state.userRelatedState.osmConnection.userDetails
if (userdetails.data.loggedIn) { if (!userdetails.data) {
reporter_email = userdetails.data.name + "@openstreetmap.org" reporter_email = userdetails.data.name + "@openstreetmap.org"
} }

View file

@ -55,7 +55,7 @@ export class DeleteFlowState {
if (ud === undefined) { if (ud === undefined) {
return undefined return undefined
} }
if (!ud.loggedIn) { if (!this._osmConnection.isLoggedIn.data) {
return false return false
} }
return ( return (

View file

@ -23,7 +23,7 @@
$: tagsExplanation = tags?.asHumanString(true, false, currentProperties) $: tagsExplanation = tags?.asHumanString(true, false, currentProperties)
</script> </script>
{#if !userDetails || $userDetails.loggedIn} {#if !userDetails}
<div class="break-words" style="word-break: break-word"> <div class="break-words" style="word-break: break-word">
{#if tags === undefined} {#if tags === undefined}
<slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected} /></slot> <slot name="no-tags"><Tr cls="subtle" t={Translations.t.general.noTagsSelected} /></slot>

View file

@ -1,6 +1,5 @@
import SvelteUIElement from "./UI/Base/SvelteUIElement" import SvelteUIElement from "./UI/Base/SvelteUIElement"
import Test from "./UI/Test.svelte" import Test from "./UI/Test.svelte"
import { OsmConnection } from "./Logic/Osm/OsmConnection"
new Test({ new OsmConnection().interact("user/details.json").then(r => console.log(">>>", r))
target: document.getElementById("maindiv"),
})