diff --git a/.gitignore b/.gitignore
index 2dd4cffb60..7f45392a34 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,3 +52,4 @@ android/
dist-full/
public/assets/icons/*.webp
uploaded_images.json
+/app/dist/
diff --git a/Docs/ServerConfig/hetzner/Caddyfile b/Docs/ServerConfig/hetzner/Caddyfile
index 631efadd46..2fca43ec59 100644
--- a/Docs/ServerConfig/hetzner/Caddyfile
+++ b/Docs/ServerConfig/hetzner/Caddyfile
@@ -6,6 +6,14 @@ hosted.mapcomplete.org {
}
}
+app.mapcomplete.org {
+ root * app/
+ file_server
+ header {
+ +Permissions-Policy "interest-cohort=()"
+ }
+}
+
countrycoder.mapcomplete.org {
root * tiles/
file_server
diff --git a/app/passthrough.html b/app/passthrough.html
new file mode 100644
index 0000000000..457877eb28
--- /dev/null
+++ b/app/passthrough.html
@@ -0,0 +1,14 @@
+
+
+
+
+ MapComplete Login
+
+
+
+Hey!
+
+Open this page with the app. (Press the menu, "open in app")
+
+
+
diff --git a/package.json b/package.json
index 40aafdbdc3..bae50114ba 100644
--- a/package.json
+++ b/package.json
@@ -83,18 +83,19 @@
"strt": "vite --host | sed 's/localhost:/127.0.0.1:/g'",
"build": "./scripts/build.sh",
"build:single": "./scripts/single_build.sh",
+ "build:vite:app-landing": "export NODE_OPTIONS=\"--max-old-space-size=12192\" && vite build --sourcemap --config app/app.vite.config.js",
"build:dbscript": "vite-node ./scripts/osm2pgsql/generateBuildDbScript.ts",
"prepare-deploy": "npm run generate:service-worker && ./scripts/prepare-build.sh && npm run build",
"watch:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css --watch",
"generate:css": "tailwindcss -i src/index.css -o public/css/index-tailwind-output.css",
"generate:doctests": "doctest-ts-improved . --ignore .*.spec.ts --ignore .*ConfigJson.ts",
"test:run-only": "vitest --run test",
- "test": " export NODE_OPTIONS=\"--max-old-space-size=8192\" && npm run clean:tests && (npm run generate:doctests 2>&1 | grep -v \"No doctests found in\") && npm run test:run-only && npm run clean:tests",
+ "test": "NODE_OPTIONS=\"--max-old-space-size=8192\" && npm run clean:tests && (npm run generate:doctests 2>&1 | grep -v \"No doctests found in\") && npm run test:run-only && npm run clean:tests",
"generate:polygon-features": "vite-node scripts/downloadFile.ts -- https://raw.githubusercontent.com/tyrasd/osm-polygon-features/master/polygon-features.json assets/polygon-features.json",
"generate:images": "vite-node scripts/generateIncludedImages.ts",
"generate:translations": "vite-node scripts/generateTranslations.ts",
"reset:translations": "vite-node scripts/generateTranslations.ts -- --ignore-weblate",
- "generate:layouts": "vite-node scripts/generateLayouts.ts",
+ "generate:layouts": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayouts.ts",
"generate:docs": "rm -rf Docs/Themes/* && rm -rf Docs/Layers/* && rm -rf Docs/TagInfo && mkdir Docs/TagInfo && export NODE_OPTIONS=\"--max-old-space-size=16000\" && vite-node scripts/generateDocs.ts && vite-node scripts/generateTaginfoProjectFiles.ts",
"generate:layeroverview": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts",
"generate:mapcomplete-changes-theme": "export NODE_OPTIONS=\"--max-old-space-size=8192\" && vite-node scripts/generateLayerOverview.ts -- --generate-change-map",
diff --git a/scripts/prepareAndroid.sh b/scripts/prepareAndroid.sh
index 5207b2898d..50b72b1cab 100755
--- a/scripts/prepareAndroid.sh
+++ b/scripts/prepareAndroid.sh
@@ -2,6 +2,14 @@
# Copy all necessary files from the 'dist' directory into dist full
# To be executed from the `MapComplete` repo root
+nvm use
+if [[ ! -f bookcases.html ]]
+then
+ npm run generate:layeroverview
+ npm run generate:layouts
+fi
+
+npm run build
echo '''
import type { CapacitorConfig } from "@capacitor/cli";
@@ -47,3 +55,5 @@ cp -r dist/assets/themes dist-full/assets
npx capacitor-assets generate
npx cap sync
+
+echo "All done! Don't forget to click 'gradly sync files' in Android Studio"
diff --git a/src/Logic/Osm/OsmConnection.ts b/src/Logic/Osm/OsmConnection.ts
index ba48e5cf5a..70451e1df1 100644
--- a/src/Logic/Osm/OsmConnection.ts
+++ b/src/Logic/Osm/OsmConnection.ts
@@ -46,14 +46,14 @@ export class OsmConnection {
public userDetails: UIEventSource
public isLoggedIn: Store
public gpxServiceIsOnline: UIEventSource = new UIEventSource(
- "unknown"
+ "unknown",
)
public apiIsOnline: UIEventSource = new UIEventSource(
- "unknown"
+ "unknown",
)
public loadingStatus = new UIEventSource<"not-attempted" | "loading" | "error" | "logged-in">(
- "not-attempted"
+ "not-attempted",
)
public preferencesHandler: OsmPreferences
public readonly _oauth_config: AuthConfig
@@ -97,7 +97,7 @@ export class OsmConnection {
this.userDetails = new UIEventSource(
new UserDetails(this._oauth_config.url),
- "userDetails"
+ "userDetails",
)
if (options.fakeUser) {
const ud = this.userDetails.data
@@ -118,7 +118,7 @@ export class OsmConnection {
(user) =>
user.loggedIn &&
(this.apiIsOnline.data === "unknown" || this.apiIsOnline.data === "online"),
- [this.apiIsOnline]
+ [this.apiIsOnline],
)
this.isLoggedIn.addCallback((isLoggedIn) => {
if (this.userDetails.data.loggedIn == false && isLoggedIn == true) {
@@ -161,7 +161,7 @@ export class OsmConnection {
defaultValue: string = undefined,
options?: {
prefix?: string
- }
+ },
): UIEventSource {
const prefix = options?.prefix ?? "mapcomplete-"
return >this.preferencesHandler.getPreference(key, defaultValue, prefix)
@@ -170,7 +170,7 @@ export class OsmConnection {
public getPreference(
key: string,
defaultValue: string = undefined,
- prefix: string = "mapcomplete-"
+ prefix: string = "mapcomplete-",
): UIEventSource {
return >this.preferencesHandler.getPreference(key, defaultValue, prefix)
}
@@ -214,7 +214,7 @@ export class OsmConnection {
this.updateAuthObject()
LocalStorageSource.get("location_before_login").setData(
- Utils.runningFromConsole ? undefined : window.location.href
+ Utils.runningFromConsole ? undefined : window.location.href,
)
this.auth.xhr(
{
@@ -252,13 +252,13 @@ export class OsmConnection {
data.account_created = userInfo.getAttribute("account_created")
data.uid = Number(userInfo.getAttribute("id"))
data.languages = Array.from(
- userInfo.getElementsByTagName("languages")[0].getElementsByTagName("lang")
+ userInfo.getElementsByTagName("languages")[0].getElementsByTagName("lang"),
).map((l) => l.textContent)
data.csCount = Number.parseInt(
- userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? "0"
+ userInfo.getElementsByTagName("changesets")[0].getAttribute("count") ?? "0",
)
data.tracesCount = Number.parseInt(
- userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? "0"
+ userInfo.getElementsByTagName("traces")[0].getAttribute("count") ?? "0",
)
data.img = undefined
@@ -290,7 +290,7 @@ export class OsmConnection {
action(this.userDetails.data)
}
this._onLoggedIn = []
- }
+ },
)
}
@@ -308,7 +308,7 @@ export class OsmConnection {
method: "GET" | "POST" | "PUT" | "DELETE",
header?: Record,
content?: string,
- allowAnonymous: boolean = false
+ allowAnonymous: boolean = false,
): Promise {
const connection: osmAuth = this.auth
if (allowAnonymous && !this.auth.authenticated()) {
@@ -316,7 +316,7 @@ export class OsmConnection {
`${this.Backend()}/api/0.6/${path}`,
header,
method,
- content
+ content,
)
if (possibleResult["content"]) {
return possibleResult["content"]
@@ -333,13 +333,13 @@ export class OsmConnection {
content,
path: `/api/0.6/${path}`,
},
- function (err, response) {
+ function(err, response) {
if (err !== null) {
error(err)
} else {
ok(response)
}
- }
+ },
)
})
}
@@ -348,7 +348,7 @@ export class OsmConnection {
path: string,
content?: string,
header?: Record,
- allowAnonymous: boolean = false
+ allowAnonymous: boolean = false,
): Promise {
return await this.interact(path, "POST", header, content, allowAnonymous)
}
@@ -356,7 +356,7 @@ export class OsmConnection {
public async put(
path: string,
content?: string,
- header?: Record
+ header?: Record,
): Promise {
return await this.interact(path, "PUT", header, content)
}
@@ -364,7 +364,7 @@ export class OsmConnection {
public async get(
path: string,
header?: Record,
- allowAnonymous: boolean = false
+ allowAnonymous: boolean = false,
): Promise {
return await this.interact(path, "GET", header, undefined, allowAnonymous)
}
@@ -403,7 +403,7 @@ export class OsmConnection {
return new Promise<{ id: number }>((ok) => {
window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }),
- Math.random() * 5000
+ Math.random() * 5000,
)
})
}
@@ -415,7 +415,7 @@ export class OsmConnection {
{
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
- true
+ true,
)
const parsed = JSON.parse(response)
console.log("Got result:", parsed)
@@ -438,14 +438,14 @@ export class OsmConnection {
* Note: these are called 'tags' on the wiki, but I opted to name them 'labels' instead as they aren't "key=value" tags, but just words.
*/
labels: string[]
- }
+ },
): Promise<{ id: number }> {
if (this._dryRun.data) {
console.warn("Dryrun enabled - not actually uploading GPX ", gpx)
return new Promise<{ id: number }>((ok) => {
window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }),
- Math.random() * 5000
+ Math.random() * 5000,
)
})
}
@@ -462,9 +462,9 @@ export class OsmConnection {
}
const extras = {
file:
- '; filename="' +
+ "; filename=\"" +
(options.filename ?? "gpx_track_mapcomplete_" + new Date().toISOString()) +
- '"\r\nContent-Type: application/gpx+xml',
+ "\"\r\nContent-Type: application/gpx+xml",
}
const boundary = "987654"
@@ -472,7 +472,7 @@ export class OsmConnection {
let body = ""
for (const key in contents) {
body += "--" + boundary + "\r\n"
- body += 'Content-Disposition: form-data; name="' + key + '"'
+ body += "Content-Disposition: form-data; name=\"" + key + "\""
if (extras[key] !== undefined) {
body += extras[key]
}
@@ -506,13 +506,13 @@ export class OsmConnection {
path: `/api/0.6/notes/${id}/comment?text=${encodeURIComponent(text)}`,
},
- function (err) {
+ function(err) {
if (err !== null) {
error(err)
} else {
ok()
}
- }
+ },
)
})
}
@@ -520,13 +520,14 @@ export class OsmConnection {
/**
* To be called by land.html
*/
- public finishLogin(callback: (previousURL: string) => void) {
+ public finishLogin(callback: (previousURL: string, oauth_token: string) => void) {
console.log(">>> authenticating")
- this.auth.authenticate(function () {
+ this.auth.authenticate(() => {
// Fully authed at this point
console.log("Authentication successful!")
+ const oauth_token = window.localStorage.getItem(this._oauth_config.url + "oauth2_access_token")
const previousLocation = LocalStorageSource.get("location_before_login")
- callback(previousLocation.data)
+ callback(previousLocation.data, oauth_token)
})
}
@@ -534,7 +535,7 @@ export class OsmConnection {
let redirect_uri = Utils.runningFromConsole
? "https://mapcomplete.org/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"
AndroidPolyfill.requestLoginCodes(this)
}
diff --git a/src/Logic/Web/AndroidPolyfill.ts b/src/Logic/Web/AndroidPolyfill.ts
index 16663637b4..81f3c70117 100644
--- a/src/Logic/Web/AndroidPolyfill.ts
+++ b/src/Logic/Web/AndroidPolyfill.ts
@@ -7,7 +7,7 @@ import { Store, UIEventSource } from "../UIEventSource"
import { OsmConnection } from "../Osm/OsmConnection"
export interface DatabridgePlugin {
- request(options: { key: string }): Promise<{ value: string | object }>;
+ request(options: { key: string }): Promise<{ value: T }>;
}
const DatabridgePluginSingleton = registerPlugin("Databridge", {
@@ -52,14 +52,12 @@ export class AndroidPolyfill {
}
public static async requestLoginCodes(osmConnection: OsmConnection) {
- const result = await DatabridgePluginSingleton.request({ key: "request:login" })
- const code: string = result["code"]
- const state: string = result["state"]
- console.log("AndroidPolyfill: received code and state; trying to pass them to the oauth lib")
- window.location.search = "?code=" + code + "&state=" + state
- osmConnection.finishLogin((oldLocation) => {
- console.log("Login should be completed, oldLocation is", oldLocation)
+ const result = await DatabridgePluginSingleton.request<{oauth_token: string}>({ key: "request:login" })
+ const token: string = result.value.oauth_token
+ console.log("AndroidPolyfill: received code and state; trying to pass them to the oauth lib",token)
+ const auth = osmConnection.auth.bootstrapToken(token, (err, result) => {
+ console.log("AndroidPolyFill: bootstraptoken returned", JSON.stringify({err, result}))
})
}
-
}
+
diff --git a/src/UI/InspectorGUI.svelte b/src/UI/InspectorGUI.svelte
index 8d2c99afc2..3ed4352a6e 100644
--- a/src/UI/InspectorGUI.svelte
+++ b/src/UI/InspectorGUI.svelte
@@ -39,7 +39,7 @@
let maplibremap: MapLibreAdaptor = new MapLibreAdaptor(map, {
zoom,
- location: new UIEventSource<{ lon: number; lat: number }>({ lat: lat.data, lon: lon.data })
+ location: new UIEventSource<{ lon: number; lat: number }>({ lat: lat.data, lon: lon.data }),
})
maplibremap.location.stabilized(500).addCallbackAndRunD(l => {
lat.set(l.lat)
@@ -47,18 +47,18 @@
})
let allLayers = HistoryUtils.personalTheme.layers
- let layersNoFixme = allLayers.filter(l => l.id !== "fixme")
-let fixme = allLayers.find(l => l.id === "fixme")
+ let layersNoFixme = allLayers.filter(l => l.id !== "fixme")
+ let fixme = allLayers.find(l => l.id === "fixme")
let featuresStore = new UIEventSource([])
let features = new StaticFeatureSource(featuresStore)
- ShowDataLayer.showMultipleLayers(map, features, [...layersNoFixme, fixme] , {
+ ShowDataLayer.showMultipleLayers(map, features, [...layersNoFixme, fixme], {
zoomToFeatures: true,
onClick: (f: Feature) => {
selectedElement.set(undefined)
Utils.waitFor(200).then(() => {
selectedElement.set(f)
})
- }
+ },
})
let osmConnection = new OsmConnection()
@@ -71,7 +71,7 @@ let fixme = allLayers.find(l => l.id === "fixme")
async function load() {
const user = username.data
- if(user.indexOf(";")<0){
+ if (user.indexOf(";") < 0) {
const inspectedData = inspectedContributors.data
const previousEntry = inspectedData.find(e => e.name === user)
@@ -81,7 +81,7 @@ let fixme = allLayers.find(l => l.id === "fixme")
inspectedData.push({
label: undefined,
visitedTime: new Date().toISOString(),
- name: user
+ name: user,
})
}
inspectedContributors.ping()
@@ -114,44 +114,44 @@ let fixme = allLayers.find(l => l.id === "fixme")
let mode: "map" | "table" | "aggregate" | "images" = "map"
let showPreviouslyVisited = new UIEventSource(true)
-const t = Translations.t.inspector
+ const t = Translations.t.inspector
-
+
-
+
load()} />
{#if loadingData}
{:else}
{/if}
-
+
@@ -215,5 +215,5 @@ const t = Translations.t.inspector
Earlier inspected constributors
{
username.set(e.detail); load();showPreviouslyVisited.set(false)
- }} />
+ }} />
diff --git a/src/land.ts b/src/land.ts
index e77a3497b4..bbcfca8ee7 100644
--- a/src/land.ts
+++ b/src/land.ts
@@ -1,7 +1,12 @@
import { OsmConnection } from "./Logic/Osm/OsmConnection"
+import Constants from "./Models/Constants"
console.log("Authorizing...")
+const key = Constants.osmAuthConfig.url + "oauth2_state"
+const st =window.localStorage.getItem(key )
+console.log("Prev state is",key, st)
new OsmConnection().finishLogin((previousURL) => {
+
const fallback = window.location.protocol + "//" + window.location.host + "/index.html"
previousURL ??= fallback
if (previousURL.indexOf("/land") > 0) {