Merge branch 'develop' into Robin-patch-1

This commit is contained in:
Robin van der Linde 2025-07-18 23:24:53 +02:00
commit 87ba09322e
28 changed files with 127 additions and 55 deletions

View file

@ -2,6 +2,8 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.54.3](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.54.2-experimental-2...v0.54.3) (2025-07-18)
### [0.54.2](https://source.mapcomplete.org/MapComplete/MapComplete/compare/v0.54.1...v0.54.2) (2025-07-11)

12
Docs/Panoramax_login.md Normal file
View file

@ -0,0 +1,12 @@
# Is it possible to login into panoramax.mapcomplete.org?
**No**.
It is not possible to login into `panoramax.mapcomplete.org`, nor is it possible to sign up.
This instance is set up specifically to host the pictures that contributors take with MapComplete.
It is not meant as a general instance where imagery can be added to. You can use https://panoramax.openstreetmap.fr for this.
Even better is to nudge your local community to setup a local instance.
For now, we also don't accept images from other editing software (such as StreetComplete or GoMap).
This is a choice that we, as MapComplete developers made and are not planning to revise, in part to keep the hosting costs low.

View file

@ -4,7 +4,10 @@
import Tr from "../src/UI/Base/Tr.svelte"
import LanguagePicker from "../src/UI/InputElement/LanguagePicker.svelte"
import { ariaLabel } from "../src/Utils/ariaLabel"
import Locale from "../src/UI/i18n/Locale"
const t = Translations.t.app
const lng = Locale.language
let fdroid = t.downloadOnFDroid.current
</script>
<head>
@ -38,12 +41,19 @@
<Tr t={t.older} />
</a>
<div class="flex flex-col items-center my-4 gap-y-4">
<div class="flex flex-col items-center my-4">
<a href="https://f-droid.org/packages/org.mapcomplete">
<img src={`https://f-droid.org/badge/get-it-on-${$lng}.png`}
alt={$fdroid}
style="width: 17rem">
</a>
<a
rel="noopener"
href="https://apps.obtainium.imranr.dev/redirect?r=obtainium://add/https://source.mapcomplete.org/MapComplete/android-wrapper/releases">
<img class="w-96" src="./badge_obtainium.png" alt="Get on obtainium" use:ariaLabel={t.getOnObtanium} />
<img style="width: 15rem" class="p-4" src="./badge_obtainium.png" alt="Get on obtainium" use:ariaLabel={t.getOnObtanium} />
</a>
</div>

View file

@ -4,7 +4,7 @@
"nl": "Welkom bij de groendoener!"
},
"description": {
"nl": "<h3>Welkom bij de Groendoener!</h3>De Zuidrand dat is spelen, ravotten, chillen, wandelen,… in het groen. Meer dan <b>200 grote en kleine speelplekken</b> liggen er in parken, in bossen en op pleintjes te wachten om ontdekt te worden. De verschillende speelplekken werden getest én goedgekeurd door kinder- en jongerenreporters uit de Zuidrand. Met leuke challenges dagen de reporters jou uit om ook op ontdekking te gaan. Klik op een speelplek op de kaart, bekijk het filmpje en ga op verkenning!<br/><br/>Het project groendoener kadert binnen het strategisch project <a href='https://www.provincieantwerpen.be/aanbod/dlm/samenwerkingsverbanden/zuidrand/projecten/strategisch-project-beleefbare-open-ruimte.html' target='_blank'>Beleefbare Open Ruimte in de Antwerpse Zuidrand</a> en is een samenwerking tussen het departement Leefmilieu van provincie Antwerpen, Sportpret vzw, een OpenStreetMap-België Consultent en Createlli vzw. Het project kwam tot stand met steun van Departement Omgeving van de Vlaamse Overheid.<br/><img class='w-full md:w-1/2' src='./assets/themes/speelplekken/provincie_antwerpen.jpg'/><img class='w-full md:w-1/2' src='./assets/themes/speelplekken/Departement_Omgeving_Vlaanderen.png'/>"
"nl": "<h3>Welkom bij de Groendoener!</h3>De Zuidrand dat is spelen, ravotten, chillen, wandelen,… in het groen. Meer dan <b>200 grote en kleine speelplekken</b> liggen er in parken, in bossen en op pleintjes te wachten om ontdekt te worden. De verschillende speelplekken werden getest én goedgekeurd door kinder- en jongerenreporters uit de Zuidrand. Met leuke challenges dagen de reporters jou uit om ook op ontdekking te gaan. Klik op een speelplek op de kaart, bekijk het filmpje en ga op verkenning!<br/><br/>Het project groendoener kadert binnen het strategisch project <a href='https://www.provincieantwerpen.be/aanbod/dlm/samenwerkingsverbanden/zuidrand/projecten/strategisch-project-beleefbare-open-ruimte.html' target='_blank'>Beleefbare Open Ruimte in de Antwerpse Zuidrand</a> en is een samenwerking tussen het departement Leefmilieu van provincie Antwerpen, Sportpret vzw, een OpenStreetMap-België Consultent en Createlli vzw. Het project kwam tot stand met steun van Departement Omgeving van de Vlaamse Overheid.<br/><img class='h-32 w-fit' src='./assets/themes/speelplekken/provincie_antwerpen.jpg'/><img class='h-32 w-fit' src='./assets/themes/speelplekken/Departement_Omgeving_Vlaanderen.png'/>"
},
"shortDescription": {
"*": "En jij? Wat ga jij doen in het groen?",

9
issues.html Normal file
View file

@ -0,0 +1,9 @@
<html lang="en">
<body>
<script>
window.location.href = "https://source.mapcomplete.org/user/login?redirect_to=%2fmapcomplete%2fmapcomplete%2fissues"
</script>
</body>
</html>

View file

@ -5,6 +5,7 @@
"app": {
"back": "Go back to MapComplete",
"download": "Download the app",
"downloadOnFDroid": "Download MapComplete on F-Droid",
"getOnObtanium": "Get on Obtainium",
"intro": "MapComplete is available as Android App as direct download. We are working on publishing this in on FDroid too.",
"noPlayServices": "The app works without Google Play Services",
@ -945,4 +946,4 @@
"startsWithQ": "A wikidata identifier starts with Q and is followed by a number"
}
}
}
}

View file

@ -5,6 +5,7 @@
"app": {
"back": "Ga terug naar MapComplete",
"download": "Download de laatste versie",
"downloadOnFDroid": "Download MapComplete op F-Droid",
"intro": "MapComplete is beschikbaar als Android App. Deze is binnenkort ook in F-Droid beschikbaar",
"older": "Bekijk oudere versies",
"title": "MapComplete Anrdoid App"

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "mapcomplete",
"version": "0.54.2",
"version": "0.54.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mapcomplete",
"version": "0.54.2",
"version": "0.54.3",
"hasInstallScript": true,
"license": "GPL-3.0-or-later",
"dependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "mapcomplete",
"version": "0.54.2",
"version": "0.54.3",
"repository": "https://source.mapcomplete.org/MapComplete/MapComplete",
"description": "A small website to edit OSM easily",
"bugs": "hhttps://source.mapcomplete.org/MapComplete/MapComplete/issues",
@ -112,7 +112,7 @@
"generate": "npm run generate:licenses && npm run generate:images && npm run generate:charging-stations && npm run generate:translations && npm run reset:layeroverview && npm run generate:service-worker",
"generate:charging-stations": "cd ./assets/layers/charging_station && vite-node csvToJson.ts && cd -",
"clean:tests": "find . -type f -name \"*.doctest.ts\" | xargs -r rm",
"clean": "echo '{\n \"#\": \"Settings in this file override the `config`-section of `package.json`\"\n}' > config.json && rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|privacy\\|test\\|studio\\|theme\\|style_test\\|statistics\\|status\\|leaderboard\\|inspector\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
"clean": "echo '{\n \"#\": \"Settings in this file override the `config`-section of `package.json`\"\n}' > config.json && rm -rf .cache/ && (find *.html | grep -v \"^\\(404\\|index\\|land\\|privacy\\|test\\|studio\\|theme\\|style_test\\|statistics\\|status\\|leaderboard\\|inspector\\|issues\\).html\" | xargs -r rm) && (ls | grep \"^index_[a-zA-Z_-]\\+\\.ts$\" | xargs -r rm)",
"clean:deep": "rm -rf ./public/assets/generated/themes && rm ./public/assets/generated/theme_overview.json && rm -rf ./public/assets/generated/layers/*",
"generate:dependency-graph": "node_modules/.bin/depcruise --exclude \"^node_modules\" --output-type dot Logic/State/MapState.ts > dependencies.dot && dot dependencies.dot -T svg -o dependencies.svg && rm dependencies.dot",
"scrapeWebsites": "vite-node scripts/importscripts/compareWebsiteData.ts -- ~/Downloads/ShopsWithWebsiteNodes.csv ~/data/scraped_websites/",

View file

@ -3075,6 +3075,10 @@ input[type="range"].range-lg::-moz-range-thumb {
border-width: 2px;
}
.border-4 {
border-width: 4px;
}
.border-\[10px\] {
border-width: 10px;
}
@ -5527,7 +5531,7 @@ h2.group {
.warning {
/* The class to convey important information, but not as grave as 'alert' */
background-color: var(--low-interaction-background);
background-color: var(--alert-color);
color: var(--alert-foreground-color);
font-weight: bold;
border-radius: 1em;

View file

@ -36,7 +36,7 @@ export default class SearchState {
constructor(state: ThemeViewState) {
this.state = state
this.showSearchDrawer = state.guistate.pageStates.search
this.searchTerm = QueryParameters.GetQueryParameter("q", "", "The term in the search field")
this.locationSearchers = [
@ -112,8 +112,6 @@ export default class SearchState {
)
this.locationResults = new GeocodingFeatureSource(this.suggestions.stabilized(250))
this.showSearchDrawer = new UIEventSource(false)
this.searchIsFocused.addCallbackAndRunD((sugg) => {
if (sugg) {
this.showSearchDrawer.set(true)

View file

@ -113,6 +113,9 @@ export class AndroidPolyfill {
DatabridgePluginSingleton.request<{ top: number, bottom: number }>({
key: "insets",
}).then((result) => {
if(!result){
return
}
let v = result.value
if(typeof v === "string"){
v = JSON.parse(v)

View file

@ -16,20 +16,21 @@ export type PageType = (typeof MenuState.pageNames)[number]
*/
export class MenuState {
public static readonly pageNames = [
"about_theme",
"background",
"copyright",
"copyright_icons",
"community_index",
"hotkeys",
"privacy",
"filter",
"background",
"about_theme",
"download",
"favourites",
"failedImages",
"usersettings",
"share",
"favourites",
"filter",
"hotkeys",
"menu",
"privacy",
"search",
"share",
"usersettings",
] as const
/**
@ -172,6 +173,7 @@ export class MenuState {
this._selectedElement.setData(undefined)
return true
}
} finally {
this.isClosingAll = false
}

View file

@ -3,6 +3,8 @@
import { sineIn } from "svelte/easing"
import { Store } from "../../Logic/UIEventSource.js"
import { onMount } from "svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
import InsetSpacer from "./InsetSpacer.svelte"
export let shown: Store<boolean>
let transitionParams = {
@ -16,11 +18,17 @@
hidden = !sh
})
// Does not need a 'top-inset-spacer' as the code below will apply the padding automatically
let height = 0
onMount(() => {
function setHeight(){
let topbar = document.getElementById("top-bar")
height = topbar?.clientHeight ?? 0
})
height = (topbar?.clientHeight ?? 0) + AndroidPolyfill.getInsetSizes().top.data
}
onMount(() => setHeight())
AndroidPolyfill.getInsetSizes().top.addCallback(() => setHeight())
</script>
<Drawer
@ -35,11 +43,15 @@
rightOffset="inset-y-0 right-0"
bind:hidden
>
<div class="flex flex-col h-full">
<div class="low-interaction h-screen">
<div class="h-full" style={`padding-top: ${height}px`}>
<div style={`padding-top: ${height}px`}>
<div class="flex h-full flex-col overflow-y-auto">
<slot />
</div>
</div>
</div>
<InsetSpacer clss="low-interaction" height={AndroidPolyfill.getInsetSizes().bottom}/>
</div>
</Drawer>

View file

@ -1,36 +1,37 @@
<script lang="ts">
import { createEventDispatcher } from "svelte"
import { CloseButton } from "flowbite-svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
/**
* The slotted element will be shown on top, with a lower-opacity border
*/
const dispatch = createEventDispatcher<{ close }>()
const top = AndroidPolyfill.getInsetSizes().top
const bottom = AndroidPolyfill.getInsetSizes().bottom
</script>
<!-- Draw the background over the total screen -->
<div
class="absolute left-0 top-0 h-screen w-screen"
style={`background-color: #00000088; z-index: 20; padding-top: calc( max( 1rem, ${$top} ));padding-bottom: calc( max( 1rem, ${$bottom} )); `}
on:click={() => {
console.log("OnClose")
dispatch("close")
}}
style="background-color: #00000088; z-index: 20"
/>
<!-- draw a _second_ absolute div, placed using 'bottom' which will be above the navigation bar on mobile browsers -->
<div
class="pointer-events-none absolute bottom-0 right-0 h-full w-screen p-4 md:p-6"
style="z-index: 21"
on:click={() => {
console.log("Closing...")
dispatch("close")
}}
on:click={() => { dispatch("close") }}
>
<div
class="content normal-background pointer-events-auto relative h-full"
on:click|stopPropagation={() => {}}
>
<div class="h-full rounded-xl">
<div class="h-full">
<!-- THe main content-->
<slot />
</div>
<slot name="close-button">
@ -43,9 +44,9 @@
</div>
<style>
.content {
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
.content {
border-radius: 0.5rem;
overflow-x: hidden;
box-shadow: 0 0 1rem #00000088;
}
</style>

View file

@ -1,8 +1,11 @@
<script lang="ts">
import type { Store } from "../../Logic/UIEventSource"
/**
* in pixels
*/
export let height: Store<number>
export let clss: string = ""
</script>
<div style={"height: "+$height+"px; background: red;"} />
<div class={clss+" shrink-0"} style={"height: "+$height+"px"} />

View file

@ -1,6 +1,7 @@
<script lang="ts">
import { Modal } from "flowbite-svelte"
import { UIEventSource } from "../../Logic/UIEventSource"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
/**
* Basically a flowbite-svelte modal made more ergonomical
@ -8,7 +9,7 @@
export let fullscreen: boolean = false
export let bodyPadding = "p-4 md:p-5 "
export let shown: UIEventSource<boolean>
export let shown: UIEventSource<boolean> = new UIEventSource(false)
export let dismissable = true
/**
* Default: 50
@ -17,7 +18,7 @@
const shared =
"in-page normal-background dark:bg-gray-800 rounded-lg border-gray-200 dark:border-gray-700 border-gray-200 dark:border-gray-700 divide-gray-200 dark:divide-gray-700 shadow-md"
let defaultClass = "relative flex flex-col mx-auto w-full divide-y " + shared
let defaultClass = "relative flex flex-col mx-auto w-full divide-y border-4 border-red-500 " + shared
if (fullscreen) {
defaultClass = shared
}
@ -40,6 +41,9 @@
shown.addCallbackAndRun((sh) => {
_shown = sh
})
let marginTop = AndroidPolyfill.getInsetSizes().top
let marginBottom = AndroidPolyfill.getInsetSizes().bottom
</script>
<Modal
@ -54,6 +58,7 @@
{headerClass}
{backdropClass}
color="none"
style={`margin-top: ${$marginTop}px; margin-bottom: ${$marginBottom}px`}
>
<svelte:fragment slot="header">
{#if $$slots.header}

View file

@ -5,6 +5,8 @@
import SelectedElementTitle from "../BigComponents/SelectedElementTitle.svelte"
import Loading from "./Loading.svelte"
import { onDestroy } from "svelte"
import InsetSpacer from "./InsetSpacer.svelte"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
export let state: SpecialVisualizationState
export let selected: Feature
@ -39,6 +41,7 @@
<Loading />
{:else}
<div class="normal-background flex h-full w-full flex-col" class:absolute>
<InsetSpacer clss="low-interaction" height={AndroidPolyfill.getInsetSizes().top} />
<SelectedElementTitle {state} {layer} selectedElement={selected} />
<SelectedElementView {state} {layer} selectedElement={selected} />
</div>

View file

@ -10,7 +10,6 @@
import OpenIdEditor from "./OpenIdEditor.svelte"
import OpenJosm from "../Base/OpenJosm.svelte"
import MapillaryLink from "./MapillaryLink.svelte"
import UserRelatedState from "../../Logic/State/UserRelatedState"
import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray"
import DownloadPanel from "../DownloadFlow/DownloadPanel.svelte"
import Share from "@babeard/svelte-heroicons/solid/Share"
@ -25,20 +24,12 @@
import { UIEventSource } from "../../Logic/UIEventSource"
import ChartBar from "@babeard/svelte-heroicons/solid/ChartBar"
import QueueList from "@babeard/svelte-heroicons/solid/QueueList"
import { MenuState } from "../../Models/MenuState"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import FeatureSwitchState from "../../Logic/State/FeatureSwitchState"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import type { MapProperties } from "../../Models/MapProperties"
import FavouritesFeatureSource from "../../Logic/FeatureSource/Sources/FavouritesFeatureSource"
import Hotkeys from "../Base/Hotkeys"
import MenuDrawerIndex from "./MenuDrawerIndex.svelte"
import ThemeViewState from "../../Models/ThemeViewState"
export let onlyLink: boolean
export let state: ThemeViewState
let hotkeys = Hotkeys._docs
let userdetails = state.osmConnection.userDetails
let theme = state.theme
let featureSwitches = state.featureSwitches

View file

@ -333,8 +333,9 @@
Android
{/if}
</div>
{#if onlyLink}
<InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} />
{/if}
</div>
{#if onlyLink}
<InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} />
{/if}
</div>

View file

@ -11,6 +11,8 @@
import BackButton from "../Base/BackButton.svelte"
import TagRenderingEditableDynamic from "../Popup/TagRendering/TagRenderingEditableDynamic.svelte"
import { LastClickFeatureSource } from "../../Logic/FeatureSource/Sources/LastClickFeatureSource"
import { AndroidPolyfill } from "../../Logic/Web/AndroidPolyfill"
import InsetSpacer from "../Base/InsetSpacer.svelte"
export let state: SpecialVisualizationState
export let selectedElement: Feature
@ -93,5 +95,7 @@
config.classes.join(" ")}
/>
{/each}
<InsetSpacer height={AndroidPolyfill.getInsetSizes().bottom} />
</div>
{/if}

View file

@ -6,7 +6,7 @@
*/
import type { SpecialVisualizationState } from "../SpecialVisualization"
import { Store } from "../../Logic/UIEventSource"
import { ImmutableStore, Store } from "../../Logic/UIEventSource"
import type { NoteId, OsmTags, OsmId } from "../../Models/OsmFeature"
import Translations from "../i18n/Translations"
import Tr from "../Base/Tr.svelte"
@ -25,6 +25,9 @@
Number of images uploaded succesfully
*/
function getCount(input: Store<string[]>): Store<number> {
if(!input){
return new ImmutableStore(0)
}
if (featureId == "*") {
return input.map((inp) => inp.length)
}

View file

@ -17,6 +17,9 @@
import type { GeocodeResult } from "../../Logic/Search/GeocodingProvider"
import { default as GeocodeResultSvelte } from "./GeocodeResult.svelte"
/**
* The big overview of all search bar results
*/
export let state: WithSearchState
let activeFilters: Store<(ActiveFilter & FilterSearchResult)[]> =
state.layerState.activeFilters.map((fs) =>

View file

@ -20,6 +20,7 @@
import DeleteButton from "./DeleteButton.svelte"
import StudioHashSetter from "./StudioHashSetter"
import TitledPanel from "../Base/TitledPanel.svelte"
import Popup from "../Base/Popup.svelte"
const layerSchema: ConfigMeta[] = <any>layerSchemaRaw

View file

@ -343,11 +343,13 @@
<!-- Top components -->
<div class="z-4 pointer-events-none absolute left-0 top-0 w-full">
<InsetSpacer height={AndroidPolyfill.getInsetSizes().top}/>
<div
id="top-bar"
class="bg-black-light-transparent pointer-events-auto flex flex-wrap items-center justify-between px-4 py-1"
>
<div class="w-full">
<InsetSpacer height={AndroidPolyfill.getInsetSizes().top}/>
</div>
<!-- Top bar with tools -->
<div class="flex items-center">
<MapControlButton
@ -466,6 +468,7 @@
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
<!-- right modal with the selected element view -->
<Drawer
placement="right"
transitionType="fly"

View file

@ -1,4 +1,4 @@
export function dragDetection(htmlElement: HTMLElement, callback: () => {}) {
export function dragDetection(htmlElement: HTMLElement, callback: () => void) {
let isDown = false
const threshold = 5
let start = null

View file

@ -406,7 +406,7 @@ h2.group {
.warning {
/* The class to convey important information, but not as grave as 'alert' */
background-color: var(--low-interaction-background);
background-color: var(--alert-color);
color: var(--alert-foreground-color);
font-weight: bold;
border-radius: 1em;

View file

@ -74,7 +74,7 @@
</div>
</div>
<div class="flex justify-between items-end w-full absolute bottom-0 p-4 h-fit delete-on-load">
<div class="flex justify-between items-end w-full absolute bottom-0 p-8 h-fit delete-on-load">
<!-- IMAGE-START -->
<img aria-hidden="true" class="p-4 h-32 w-32 self-start" src="./assets/svg/add.svg">