diff --git a/assets/layers/usersettings/usersettings.json b/assets/layers/usersettings/usersettings.json
index 7eefabe8d..d8e7b7d1b 100644
--- a/assets/layers/usersettings/usersettings.json
+++ b/assets/layers/usersettings/usersettings.json
@@ -1531,6 +1531,48 @@
}
}
},
+ {
+ "id": "share-login-title",
+ "render": {
+ "en": "
Login via QR code
"
+ }
+ },
+ {
+ "id": "share-login-explanation",
+ "render": {
+ "en": "With the below QR-code, you can login on another device without having to share your password"
+ }
+ },
+ {
+ "id": "share-login-group",
+ "render": {
+ "special": {
+ "type": "group",
+ "header": "share-login-group-title",
+ "labels": "share-login-qr"
+ }
+ }
+ },
+ {
+ "id": "share-login-group-title",
+ "labels": [
+ "hidden"
+ ],
+ "render": {
+ "en": "Allow to log in and act as {_name}"
+ }
+ },
+ {
+ "id": "share-login-qr",
+ "labels": [
+ "hidden"
+ ],
+ "render": {
+ "special": {
+ "type": "qr_login"
+ }
+ }
+ },
{
"id": "debug-title",
"render": {
diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts
index 9c1ef7b74..d3297978f 100644
--- a/src/Logic/Osm/OsmConnection.ts
+++ b/src/Logic/Osm/OsmConnection.ts
@@ -154,7 +154,8 @@ export class OsmConnection {
constructor(options?: {
dryRun?: Store
fakeUser?: false | boolean
- oauth_token?: UIEventSource
+ oauth_token?: UIEventSource,
+ shared_cookie?: string,
// Used to keep multiple changesets open and to write to the correct changeset
singlePage?: boolean
attemptLogin?: boolean
@@ -205,6 +206,10 @@ export class OsmConnection {
this._dryRun = options.dryRun ?? new UIEventSource(false)
+ if (options?.shared_cookie) {
+ this.setToken(options?.shared_cookie)
+ }
+
this.updateAuthObject(false)
AndroidPolyfill.inAndroid.addCallback(() => {
this.updateAuthObject(false)
@@ -600,6 +605,9 @@ export class OsmConnection {
})
}
+ /**
+ * Gets the login token. Sharing this will allow to mimic the user session on another device
+ */
public getToken(): string {
// https://www.openstreetmap.orgoauth2_access_token
let prefix = this.Backend()
@@ -608,12 +616,20 @@ export class OsmConnection {
}
return (
QueryParameters.GetQueryParameter(prefix + "oauth_token", undefined).data ??
- window.localStorage.getItem(this._oauth_config.url + "oauth2_access_token")
+ window.localStorage.getItem(this.getLoginCookieName())
)
}
+ public setToken(token: string) {
+ window.localStorage.setItem(this.getLoginCookieName(), token)
+ }
+
+ private getLoginCookieName() {
+ return this._oauth_config.url + "oauth2_access_token"
+ }
+
private async loginAndroidPolyfill() {
- const key = "https://www.openstreetmap.orgoauth2_access_token"
+ const key = this.getLoginCookieName()
if (localStorage.getItem(key)) {
// We are probably already logged in
return
@@ -629,6 +645,7 @@ export class OsmConnection {
}
await this.loadUserInfo()
}
+
private updateAuthObject(autoLogin: boolean) {
let redirect_uri = Utils.runningFromConsole
? "https://mapcomplete.org/land.html"
diff --git a/src/Logic/State/UserSettingsMetaTagging.ts b/src/Logic/State/UserSettingsMetaTagging.ts
index 6e568c5c3..33a5ae85b 100644
--- a/src/Logic/State/UserSettingsMetaTagging.ts
+++ b/src/Logic/State/UserSettingsMetaTagging.ts
@@ -1,42 +1,14 @@
import { Utils } from "../../Utils"
/** This code is autogenerated - do not edit. Edit ./assets/layers/usersettings/usersettings.json instead */
export class ThemeMetaTagging {
- public static readonly themeName = "usersettings"
+ public static readonly themeName = "usersettings"
- public metaTaggging_for_usersettings(feat: { properties: Record }) {
- Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_md", () =>
- feat.properties._description
- .match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)
- ?.at(1)
- )
- Utils.AddLazyProperty(
- feat.properties,
- "_d",
- () => feat.properties._description?.replace(/</g, "<")?.replace(/>/g, ">") ?? ""
- )
- Utils.AddLazyProperty(feat.properties, "_mastodon_candidate_a", () =>
- ((feat) => {
- const e = document.createElement("div")
- e.innerHTML = feat.properties._d
- return Array.from(e.getElementsByTagName("a")).filter(
- (a) => a.href.match(/mastodon|en.osm.town/) !== null
- )[0]?.href
- })(feat)
- )
- Utils.AddLazyProperty(feat.properties, "_mastodon_link", () =>
- ((feat) => {
- const e = document.createElement("div")
- e.innerHTML = feat.properties._d
- return Array.from(e.getElementsByTagName("a")).filter(
- (a) => a.getAttribute("rel")?.indexOf("me") >= 0
- )[0]?.href
- })(feat)
- )
- Utils.AddLazyProperty(
- feat.properties,
- "_mastodon_candidate",
- () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a
- )
- feat.properties["__current_backgroun"] = "initial_value"
- }
-}
+ public metaTaggging_for_usersettings(feat: {properties: Record}) {
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_md', () => feat.properties._description.match(/\[[^\]]*\]\((.*(mastodon|en.osm.town).*)\).*/)?.at(1) )
+ Utils.AddLazyProperty(feat.properties, '_d', () => feat.properties._description?.replace(/</g,'<')?.replace(/>/g,'>') ?? '' )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate_a', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.href.match(/mastodon|en.osm.town/) !== null)[0]?.href }) (feat) )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_link', () => (feat => {const e = document.createElement('div');e.innerHTML = feat.properties._d;return Array.from(e.getElementsByTagName("a")).filter(a => a.getAttribute("rel")?.indexOf('me') >= 0)[0]?.href})(feat) )
+ Utils.AddLazyProperty(feat.properties, '_mastodon_candidate', () => feat.properties._mastodon_candidate_md ?? feat.properties._mastodon_candidate_a )
+ feat.properties['__current_backgroun'] = 'initial_value'
+ }
+}
\ No newline at end of file
diff --git a/src/Models/ThemeViewState/WithUserRelatedState.ts b/src/Models/ThemeViewState/WithUserRelatedState.ts
index edece3ad0..319881077 100644
--- a/src/Models/ThemeViewState/WithUserRelatedState.ts
+++ b/src/Models/ThemeViewState/WithUserRelatedState.ts
@@ -37,9 +37,11 @@ export class WithUserRelatedState {
}
this.theme = theme
this.featureSwitches = new FeatureSwitchState(theme)
+
this.osmConnection = new OsmConnection({
dryRun: this.featureSwitches.featureSwitchIsTesting,
fakeUser: this.featureSwitches.featureSwitchFakeUser.data,
+ shared_cookie: QueryParameters.GetQueryParameter("shared_oauth_cookie", undefined, "Used to share a session with another device - this saves logging in at another device").data,
oauth_token: QueryParameters.GetQueryParameter(
"oauth_token",
undefined,
diff --git a/src/UI/Popup/QrCode.svelte b/src/UI/Popup/QrCode.svelte
index f1c12d172..e6d02fd87 100644
--- a/src/UI/Popup/QrCode.svelte
+++ b/src/UI/Popup/QrCode.svelte
@@ -13,16 +13,38 @@
export let state: SpecialVisualizationState
export let tags: UIEventSource>
export let feature: Feature
-
- let [lon, lat] = GeoOperations.centerpointCoordinates(feature)
+ export let extraUrlParams: Record = {}
const includeLayout = window.location.pathname.split("/").at(-1).startsWith("theme")
- const layout = includeLayout ? "layout=" + state.theme.id + "&" : ""
let id: Store = tags.mapD((tags) => tags.id)
- let url = id.mapD(
+ extraUrlParams["z"] ??= 15
+ if (includeLayout) {
+ extraUrlParams["layout"] ??= state.theme.id
+ }
+ if (feature) {
+ const [lon, lat] = GeoOperations.centerpointCoordinates(feature)
+ extraUrlParams["lon"] ??= "" + lon
+ extraUrlParams["lat"] ??= "" + lat
+ } else if (state?.mapProperties?.location?.data) {
+ const l = state?.mapProperties?.location?.data
+ extraUrlParams["lon"] ??= "" + l.lon
+ extraUrlParams["lat"] ??= "" + l.lat
+ }
+
+ const params = []
+ for (const key in extraUrlParams) {
+ console.log(key, "-->", extraUrlParams[key])
+ params.push(key + "=" + encodeURIComponent(extraUrlParams[key]))
+ }
+ let url = id.map((id) => {
+ if (id) {
+ return "#" + id
+ } else {
+ return ""
+ }
+ }).map(
(id) =>
- `${window.location.protocol}//${window.location.host}${window.location.pathname}?${layout}lat=${lat}&lon=${lon}&z=15` +
- `#${id}`
+ `${window.location.protocol}//${window.location.host}${window.location.pathname}?${params.join("&")}${id}`
)
function toggleSize() {
@@ -32,9 +54,11 @@
size.setData(smallSize)
}
}
+
+ url.addCallbackAndRunD(url => console.log("URL IS", url))
-{#if $id.startsWith("node/-")}
+{#if $id?.startsWith("node/-")}
{:else}
diff --git a/src/UI/SpecialVisualisations/SettingsVisualisations.ts b/src/UI/SpecialVisualisations/SettingsVisualisations.ts
index d81bc3690..183dd7ef0 100644
--- a/src/UI/SpecialVisualisations/SettingsVisualisations.ts
+++ b/src/UI/SpecialVisualisations/SettingsVisualisations.ts
@@ -14,6 +14,9 @@ import LanguageUtils from "../../Utils/LanguageUtils"
import LanguagePicker from "../InputElement/LanguagePicker.svelte"
import PendingChangesIndicator from "../BigComponents/PendingChangesIndicator.svelte"
import { Utils } from "../../Utils"
+import { Feature } from "geojson"
+import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
+import QrCode from "../Popup/QrCode.svelte"
export class SettingsVisualisations {
public static initList(): SpecialVisualizationSvelte[] {
@@ -146,6 +149,21 @@ export class SettingsVisualisations {
})
},
},
+ {
+ funcName: "qr_login",
+ args: [],
+ docs: "A QR-code which shares the current URL and adds the login token. Anyone with this login token will have the same permissions as you currently have. Logging out from this session will also log them out",
+ group: "settings",
+ constr(state: SpecialVisualizationState, tags: UIEventSource>, argument: string[], feature: Feature, layer: LayerConfig): SvelteUIElement {
+ const shared_oauth_cookie = state.osmConnection.getToken()
+ return new SvelteUIElement(QrCode, {
+ state,
+ tags,
+ feature,
+ extraUrlParams: { shared_oauth_cookie }
+ })
+ }
+ },
{
funcName: "logout",