Merge develop

This commit is contained in:
Pieter Vander Vennet 2024-08-29 23:39:56 +02:00
commit 3be286c2b1
30 changed files with 3315 additions and 2458 deletions

View file

@ -13,64 +13,39 @@
import type { MapProperties } from "../Models/MapProperties"
import Geosearch from "./Search/Geosearch.svelte"
import Translations from "./i18n/Translations"
import usersettings from "../assets/generated/layers/usersettings.json"
import {
CogIcon,
EyeIcon,
HeartIcon,
MenuIcon,
XCircleIcon
MenuIcon
} from "@rgossiaux/svelte-heroicons/solid"
import Tr from "./Base/Tr.svelte"
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte"
import FloatOver from "./Base/FloatOver.svelte"
import Constants from "../Models/Constants"
import TabbedGroup from "./Base/TabbedGroup.svelte"
import UserRelatedState from "../Logic/State/UserRelatedState"
import LoginToggle from "./Base/LoginToggle.svelte"
import LoginButton from "./Base/LoginButton.svelte"
import CopyrightPanel from "./BigComponents/CopyrightPanel.svelte"
import DownloadPanel from "./DownloadFlow/DownloadPanel.svelte"
import ModalRight from "./Base/ModalRight.svelte"
import LevelSelector from "./BigComponents/LevelSelector.svelte"
import ThemeIntroPanel from "./BigComponents/ThemeIntroPanel.svelte"
import type { RasterLayerPolygon } from "../Models/RasterLayers"
import { AvailableRasterLayers } from "../Models/RasterLayers"
import RasterLayerOverview from "./Map/RasterLayerOverview.svelte"
import IfHidden from "./Base/IfHidden.svelte"
import { onDestroy } from "svelte"
import OpenBackgroundSelectorButton from "./BigComponents/OpenBackgroundSelectorButton.svelte"
import StateIndicator from "./BigComponents/StateIndicator.svelte"
import ShareScreen from "./BigComponents/ShareScreen.svelte"
import UploadingImageCounter from "./Image/UploadingImageCounter.svelte"
import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte"
import Cross from "../assets/svg/Cross.svelte"
import LanguagePicker from "./InputElement/LanguagePicker.svelte"
import Min from "../assets/svg/Min.svelte"
import Plus from "../assets/svg/Plus.svelte"
import Filter from "../assets/svg/Filter.svelte"
import Community from "../assets/svg/Community.svelte"
import Favourites from "./Favourites/Favourites.svelte"
import ImageOperations from "./Image/ImageOperations.svelte"
import VisualFeedbackPanel from "./BigComponents/VisualFeedbackPanel.svelte"
import { Orientation } from "../Sensors/Orientation"
import GeolocationIndicator from "./BigComponents/GeolocationIndicator.svelte"
import Compass_arrow from "../assets/svg/Compass_arrow.svelte"
import ReverseGeocoding from "./BigComponents/ReverseGeocoding.svelte"
import FilterPanel from "./BigComponents/FilterPanel.svelte"
import PrivacyPolicy from "./BigComponents/PrivacyPolicy.svelte"
import { BBox } from "../Logic/BBox"
import ReviewsOverview from "./Reviews/ReviewsOverview.svelte"
import ExtraLinkButton from "./BigComponents/ExtraLinkButton.svelte"
import CloseAnimation from "./Base/CloseAnimation.svelte"
import { LastClickFeatureSource } from "../Logic/FeatureSource/Sources/LastClickFeatureSource"
import ArrowDownTray from "@babeard/svelte-heroicons/mini/ArrowDownTray"
import Share from "@babeard/svelte-heroicons/solid/Share"
import ChevronRight from "@babeard/svelte-heroicons/solid/ChevronRight"
import Marker from "./Map/Marker.svelte"
import AboutMapComplete from "./BigComponents/AboutMapComplete.svelte"
import HotkeyTable from "./BigComponents/HotkeyTable.svelte"
import SelectedElementPanel from "./Base/SelectedElementPanel.svelte"
import MenuDrawer from "./BigComponents/MenuDrawer.svelte"
import DrawerLeft from "./Base/DrawerLeft.svelte"
import type { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson"
import { GeocodingUtils } from "../Logic/Geocoding/GeocodingProvider"
@ -109,7 +84,6 @@
let visualFeedback = state.visualFeedback
let viewport: UIEventSource<HTMLDivElement> = new UIEventSource<HTMLDivElement>(undefined)
let mapproperties: MapProperties = state.mapProperties
let usersettingslayer = new LayerConfig(<LayerConfigJson>usersettings, "usersettings", true)
state.mapProperties.installCustomKeyboardHandler(viewport)
let canZoomIn = mapproperties.maxzoom.map(
(mz) => mapproperties.zoom.data < mz,
@ -145,7 +119,6 @@
updateViewport()
})
let featureSwitches: FeatureSwitchState = state.featureSwitches
let availableLayers = state.availableLayers
let currentViewLayer: LayerConfig = layout.layers.find((l) => l.id === "current_view")
let rasterLayer: Store<RasterLayerPolygon> = state.mapProperties.rasterLayer
let rasterLayerName =
@ -157,8 +130,12 @@
})
)
let previewedImage = state.previewedImage
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
let debug = state.featureSwitches.featureSwitchIsDebugging
debug.addCallbackAndRun((dbg) => {
if (dbg) {
document.body.classList.add("debug")
@ -179,26 +156,6 @@
animation?.cameraAnimation(mlmap)
}
/**
* Needed for the animations
*/
let openMapButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openMenuButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openCurrentViewLayerButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(
undefined
)
let _openNewElementButton: HTMLButtonElement
let openNewElementButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
$: {
openNewElementButton.setData(_openNewElementButton)
}
let openFilterButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let openBackgroundButton: UIEventSource<HTMLElement> = new UIEventSource<HTMLElement>(undefined)
let addNewFeatureMode = state.userRelatedState.addNewFeatureMode
let gpsAvailable = state.geolocation.geolocationState.gpsAvailable
let gpsButtonAriaLabel = state.geolocation.geolocationState.gpsStateExplanation
</script>
<main>
@ -221,6 +178,55 @@
<div class="pointer-events-none absolute top-0 left-0 w-full">
<!-- Top components -->
<div
class="flex bg-black-light-transparent pointer-events-auto items-center justify-between px-4 py-1 flex-wrap-reverse">
<!-- Top bar with tools -->
<div class="flex items-center">
<MapControlButton
cls="m-0.5 p-0.5 sm:p-1"
arialabel={Translations.t.general.labels.menu}
on:click={() => {console.log("Opening...."); state.guistate.menuIsOpened.setData(true)}}
on:keydown={forwardEventToMap}
>
<MenuIcon class="h-6 w-6 cursor-pointer" />
</MapControlButton>
<MapControlButton
on:click={() => state.guistate.pageStates.about_theme.set(true)}
on:keydown={forwardEventToMap}
>
<div
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 mr-2"
>
<Marker icons={layout.icon} size="h-6 w-6 shrink-0 mr-0.5 sm:mr-1 md:mr-2" />
<b class="mr-1">
<Tr t={layout.title} />
</b>
</div>
</MapControlButton>
</div>
<If condition={state.featureSwitches.featureSwitchSearch}>
<div class="w-full sm:w-64 my-2 sm:mt-0">
<Geosearch
bounds={state.mapProperties.bounds}
on:searchCompleted={() => {
state.map?.data?.getCanvas()?.focus()
}}
perLayer={state.perLayer}
selectedElement={state.selectedElement}
geolocationState={state.geolocation.geolocationState}
searcher={state.geosearch}
{state}
/>
</div>
</If>
</div>
<div class="pointer-events-auto float-right mt-1 flex flex-col px-1 max-[480px]:w-full sm:m-2">
<If condition={state.visualFeedback}>
{#if $selectedElement === undefined}
@ -229,46 +235,12 @@
</div>
{/if}
</If>
<If condition={state.featureSwitches.featureSwitchSearch}>
<Geosearch
bounds={state.mapProperties.bounds}
on:searchCompleted={() => {
state.map?.data?.getCanvas()?.focus()
}}
perLayer={state.perLayer}
selectedElement={state.selectedElement}
geolocationState={state.geolocation.geolocationState}
searcher={state.geosearch}
{state}
/>
</If>
</div>
<div class="float-left m-1 flex flex-col sm:mt-2">
<If condition={state.featureSwitches.featureSwitchWelcomeMessage}>
<MapControlButton
on:click={() => state.guistate.themeIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMapButton}
>
<div
class="m-0.5 mx-1 flex cursor-pointer items-center max-[480px]:w-full sm:mx-1 md:mx-2"
>
<Marker icons={layout.icon} size="h-4 w-4 md:h-8 md:w-8 mr-0.5 sm:mr-1 md:mr-2" />
<b class="mr-1">
<Tr t={layout.title} />
</b>
<ChevronRight class="h-4 w-4" />
</div>
</MapControlButton>
<MapControlButton
arialabel={Translations.t.general.labels.menu}
on:click={() => state.guistate.menuIsOpened.setData(true)}
on:keydown={forwardEventToMap}
htmlElem={openMenuButton}
>
<MenuIcon class="h-8 w-8 cursor-pointer" />
</MapControlButton>
</If>
{#if currentViewLayer?.tagRenderings && currentViewLayer.defaultIcon()}
<MapControlButton
@ -276,7 +248,6 @@
state.selectCurrentView()
}}
on:keydown={forwardEventToMap}
htmlElem={openCurrentViewLayerButton}
>
<div class="h-8 w-8 cursor-pointer">
<ToSvelte construct={() => currentViewLayer.defaultIcon()} />
@ -313,7 +284,6 @@
<button
class="low-interaction pointer-events-auto w-fit"
class:disabled={$currentZoom < Constants.minZoomLevelToAddNewPoint}
bind:this={_openNewElementButton}
on:click={() => {
state.openNewDialog()
}}
@ -337,7 +307,6 @@
arialabel={Translations.t.general.labels.filter}
on:click={() => state.guistate.openFilterView()}
on:keydown={forwardEventToMap}
htmlElem={openFilterButton}
>
<Filter class="h-6 w-6" />
</MapControlButton>
@ -346,25 +315,18 @@
<OpenBackgroundSelectorButton
hideTooltip={true}
{state}
htmlElem={openBackgroundButton}
/>
</If>
<a
class="bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer self-end self-center overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
on:click={() => {
if (featureSwitches.featureSwitchWelcomeMessage.data) {
state.guistate.themeViewTab.setData("copyright")
state.guistate.themeIsOpened.setData(true)
} else {
state.guistate.copyrightPanelIsOpened.setData(true)
}
}}
<button
class="unstyled bg-black-transparent pointer-events-auto ml-1 h-fit max-h-12 cursor-pointer overflow-hidden rounded-2xl px-1 text-white opacity-50 hover:opacity-100"
style="background: #00000088; padding: 0.25rem; border-radius: 2rem;"
on:click={() => {state.guistate.pageStates.copyright.set(true)}}
>
© <span class="hidden sm:inline sm:pr-2">
OpenStreetMap
<span class="hidden w-24 md:inline md:pr-2">, {rasterLayerName}</span>
</span>
</a>
</button>
</div>
</div>
@ -434,6 +396,13 @@
<svelte:fragment slot="error" />
</LoginToggle>
<DrawerLeft shown={state.guistate.menuIsOpened}>
<div class="h-screen overflow-y-auto">
<MenuDrawer onlyLink={true} {state} />
</div>
</DrawerLeft>
<MenuDrawer onlyLink={false} {state} />
{#if $selectedElement !== undefined && $selectedLayer !== undefined && !$selectedLayer.popupInFloatover}
<!-- right modal with the selected element view -->
<ModalRight
@ -476,225 +445,5 @@
</FloatOver>
</If>
<!-- big theme menu -->
<If condition={state.guistate.themeIsOpened}>
<FloatOver on:close={() => state.guistate.themeIsOpened.setData(false)}>
<span slot="close-button"><!-- Disable the close button --></span>
<TabbedGroup
condition1={state.featureSwitches.featureSwitchEnableExport}
tab={state.guistate.themeViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
on:click={() => state.guistate.themeIsOpened.setData(false)}
/>
</div>
<div class="flex" slot="title0">
<Marker icons={layout.icon} size="h-4 w-4" />
<Tr t={layout.title} />
</div>
<div class="m-4 h-full" slot="content0">
<ThemeIntroPanel {state} />
</div>
<div class="flex" slot="title1">
<If condition={state.featureSwitches.featureSwitchEnableExport}>
<ArrowDownTray class="h-4 w-4" />
<Tr t={Translations.t.general.download.title} />
</If>
</div>
<div class="m-4" slot="content1">
<DownloadPanel {state} />
</div>
<div slot="title2">
<Tr t={Translations.t.general.attribution.title} />
</div>
<div slot="content2" class="m-2 flex flex-col">
<CopyrightPanel {state} />
</div>
<div class="flex" slot="title3">
<Share class="h-4 w-4" />
<Tr t={Translations.t.general.sharescreen.title} />
</div>
<div class="m-2" slot="content3">
<ShareScreen {state} />
</div>
</TabbedGroup>
</FloatOver>
</If>
<!-- Filterpane -->
<If condition={state.guistate.filtersPanelIsOpened}>
<FloatOver on:close={() => state.guistate.filtersPanelIsOpened.setData(false)}>
<FilterPanel {state} />
</FloatOver>
</If>
<!-- background layer selector -->
<IfHidden condition={state.guistate.backgroundLayerSelectionIsOpened}>
<FloatOver
on:close={() => {
state.guistate.backgroundLayerSelectionIsOpened.setData(false)
}}
>
<RasterLayerOverview
{availableLayers}
map={state.map}
mapproperties={state.mapProperties}
userstate={state.userRelatedState}
visible={state.guistate.backgroundLayerSelectionIsOpened}
/>
</FloatOver>
</IfHidden>
<!-- Menu page -->
<If condition={state.guistate.menuIsOpened}>
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
<span slot="close-button"><!-- Hide the default close button --></span>
<TabbedGroup
condition1={featureSwitches.featureSwitchEnableLogin}
condition2={state.featureSwitches.featureSwitchCommunityIndex}
tab={state.guistate.menuViewTabIndex}
>
<div slot="post-tablist">
<XCircleIcon
class="mr-2 h-8 w-8"
on:click={() => state.guistate.menuIsOpened.setData(false)}
/>
</div>
<div class="flex" slot="title0">
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</div>
<div slot="content0" class="flex flex-col">
<AboutMapComplete {state} />
<div class="m-2 flex flex-col">
<HotkeyTable />
</div>
</div>
<div class="flex" slot="title1">
<CogIcon class="h-6 w-6" />
<Tr t={UserRelatedState.usersettingsConfig.title.GetRenderValue({})} />
</div>
<div class="links-as-button py-8" slot="content1">
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}>
<div class="flex flex-col" slot="not-logged-in">
<LanguagePicker availableLanguages={layout.language} />
<Tr cls="alert" t={Translations.t.userinfo.notLoggedIn} />
<LoginButton clss="primary" osmConnection={state.osmConnection} />
</div>
<SelectedElementView
highlightedRendering={state.guistate.highlightedUserSetting}
layer={usersettingslayer}
selectedElement={{
type: "Feature",
properties: { id: "settings" },
geometry: { type: "Point", coordinates: [0, 0] },
}}
{state}
tags={state.userRelatedState.preferencesAsTags}
/>
</LoginToggle>
</div>
<div class="flex" slot="title2">
<HeartIcon class="h-6 w-6" />
<Tr t={Translations.t.favouritePoi.tab} />
</div>
<div class="m-2 flex flex-col" slot="content2">
<h3>
<Tr t={Translations.t.favouritePoi.title} />
</h3>
<Favourites {state} />
<h3>
<Tr t={Translations.t.reviews.your_reviews} />
</h3>
<ReviewsOverview {state} />
</div>
</TabbedGroup>
</FloatOver>
</If>
<!-- Privacy policy -->
<If condition={state.guistate.privacyPanelIsOpened}>
<FloatOver on:close={() => state.guistate.privacyPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
<h2 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
<EyeIcon class="w-6 pr-2" />
<Tr t={Translations.t.privacy.title} />
</h2>
<div class="overflow-auto p-4">
<PrivacyPolicy {state} />
</div>
</div>
</FloatOver>
</If>
<!-- Attribution, copyright and about MapComplete (no menu case) -->
<If condition={state.guistate.copyrightPanelIsOpened}>
<FloatOver on:close={() => state.guistate.copyrightPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
<h1 class="low-interaction m-0 flex items-center p-4 drop-shadow-md">
<Tr t={Translations.t.general.attribution.title} />
</h1>
<div class="overflow-auto p-4">
<h2>
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</h2>
<AboutMapComplete {state} />
<CopyrightPanel {state} />
</div>
</div>
</FloatOver>
</If>
<!-- Community index -->
<If condition={state.guistate.communityIndexPanelIsOpened}>
<FloatOver on:close={() => state.guistate.communityIndexPanelIsOpened.setData(false)}>
<div class="flex h-full flex-col overflow-hidden">
<h2 class="low-interaction m-0 flex items-center p-4">
<Community class="h-6 w-6" />
<Tr t={Translations.t.communityIndex.title} />
</h2>
<div class="overflow-auto p-4">
<CommunityIndexView location={state.mapProperties.location} />
</div>
</div>
</FloatOver>
</If>
<CloseAnimation isOpened={state.guistate.themeIsOpened} moveTo={openMapButton} debug="theme" />
<CloseAnimation isOpened={state.guistate.menuIsOpened} moveTo={openMenuButton} debug="menu" />
<CloseAnimation
isOpened={selectedLayer.map((sl) => sl !== undefined && sl === currentViewLayer)}
moveTo={openCurrentViewLayerButton}
debug="currentViewLayer"
/>
<CloseAnimation
isOpened={selectedElement.map(
(sl) => sl !== undefined && sl?.properties?.id === LastClickFeatureSource.newPointElementId
)}
moveTo={openNewElementButton}
debug="newElement"
/>
<CloseAnimation
isOpened={state.guistate.filtersPanelIsOpened}
moveTo={openFilterButton}
debug="filter"
/>
<CloseAnimation
isOpened={state.guistate.backgroundLayerSelectionIsOpened}
moveTo={openBackgroundButton}
debug="bg"
/>
</main>