MapComplete/src/UI/Popup/AddNewPoint/CreateCopy.svelte

162 lines
5.9 KiB
Svelte
Raw Normal View History

<script lang="ts">
/**
* A special visualisation element which allows to create a full copy (including all tags) of a node
*/
import Popup from "../../Base/Popup.svelte"
import LayerConfig from "../../../Models/ThemeConfig/LayerConfig"
import { UIEventSource } from "../../../Logic/UIEventSource"
import type { Feature } from "geojson"
import NewPointLocationInput from "../../BigComponents/NewPointLocationInput.svelte"
import NextButton from "../../Base/NextButton.svelte"
import { GeoOperations } from "../../../Logic/GeoOperations"
import type { WayId } from "../../../Models/OsmFeature"
import { Tag } from "../../../Logic/Tags/Tag"
import { twJoin } from "tailwind-merge"
import Translations from "../../i18n/Translations"
import OpenBackgroundSelectorButton from "../../BigComponents/OpenBackgroundSelectorButton.svelte"
import Tr from "../../Base/Tr.svelte"
import ThemeViewState from "../../../Models/ThemeViewState"
import TagExplanation from "../TagExplanation.svelte"
import { And } from "../../../Logic/Tags/And"
import Loading from "../../Base/Loading.svelte"
import CreateNewNodeAction from "../../../Logic/Osm/Actions/CreateNewNodeAction"
import DocumentDuplicate from "@babeard/svelte-heroicons/solid/DocumentDuplicate"
export let state: ThemeViewState
export let layer: LayerConfig
export let tags: UIEventSource<Record<string, string>>
export let feature: Feature
let c = GeoOperations.centerpointCoordinates(feature)
let coordinate: { lon: number; lat: number } = { lat: c[1], lon: c[0] }
let preciseCoordinate: UIEventSource<{ lon: number; lat: number }> = new UIEventSource(undefined)
let snappedToObject: UIEventSource<WayId> = new UIEventSource<WayId>(undefined)
// Small helper variable: if the map is tapped, we should let the 'Next'-button grab some attention as users have to click _that_ to continue, not the map
let preciseInputIsTapped = false
const forbiddenKeys = new Set(["id", "timestamp", "user", "changeset", "version", "uid"])
let asTags = tags.map(tgs => Object.keys(tgs).filter(k => !k.startsWith("_") && !forbiddenKeys.has(k)).map(k => new Tag(k, tgs[k])))
let showPopup: UIEventSource<boolean> = new UIEventSource(false)
const showTags = state.userRelatedState.showTagsB
const loggedIn = state.userRelatedState.osmConnection.isLoggedIn
let creatingCopy = new UIEventSource(false)
const t = Translations.t.copy
async function createCopy() {
creatingCopy.set(true)
const tags: Tag[] = asTags.data
const location: { lon: number; lat: number } = preciseCoordinate.data
const newElementAction = new CreateNewNodeAction(
tags, location.lat, location.lon, {
theme: state.theme?.id ?? "unkown",
changeType: "copy",
})
await state.changes.applyAction(newElementAction)
state.newFeatures.features.ping()
// The 'changes' should have created a new point, which added this into the 'featureProperties'
const newId = newElementAction.newElementId
console.log("Applied pending changes, fetching store for", newId)
const tagsStore = state.featureProperties.getStore(newId)
if (!tagsStore) {
console.error("Bug: no tagsStore found for", newId)
}
{
// Set some metainfo
const properties = tagsStore.data
properties["_backend"] = state.osmConnection.Backend()
properties["_last_edit:timestamp"] = new Date().toISOString()
const userdetails = state.osmConnection.userDetails.data
properties["_last_edit:contributor"] = userdetails.name
properties["_last_edit:uid"] = "" + userdetails.uid
tagsStore.ping()
}
const feature = state.indexedFeatures.featuresById.data.get(newId)
console.log("Selecting feature", feature, "and opening their popup")
creatingCopy.set(false)
showPopup.set(false)
state.selectedElement.setData(feature)
tagsStore.ping()
state.mapProperties.location.setData(location)
}
</script>
<Popup shown={showPopup} fullscreen>
<Tr slot="header" t={t.title} />
{#if $creatingCopy}
<div class="h-full flex flex-col justify-center">
<div class="h-fit flex justify-center">
<Loading>
<Tr t={t.loading} />
</Loading>
</div>
</div>
{:else}
<div class="h-full flex flex-col gap-y-4 justify-between pb-4">
<Tr t={t.intro} />
<div class="relative" style="height: calc(100% - 7rem);">
<NewPointLocationInput
on:click={() => {
preciseInputIsTapped = true
}}
value={preciseCoordinate}
snappedTo={snappedToObject}
{state}
{coordinate}
targetLayer={layer}
presetProperties={$asTags}
/>
<div
class={twJoin(
!preciseInputIsTapped && "hidden",
"absolute top-0 flex w-full justify-center p-12"
)}
>
<!-- This is an _extra_ button that appears when the map is tapped - see usertest 2023-01-07 -->
<NextButton on:click={() => createCopy()} clss="primary w-fit">
<div class="flex w-full justify-end gap-x-2">
<Tr t={t.confirm} />
</div>
</NextButton>
</div>
<div class="absolute bottom-0 left-0 p-4">
<OpenBackgroundSelectorButton {state} />
</div>
</div>
<div class="flex w-full justify-end">
<NextButton clss="primary sm:shrink-0" on:click={() => createCopy()}>
<DocumentDuplicate class="w-8 mx-2" />
<Tr t={t.confirm} />
</NextButton>
</div>
{#if showTags}
<div class="subtle">
<TagExplanation tagsFilter={new And($asTags)} linkToWiki />
</div>
{/if}
</div>
{/if}
</Popup>
{#if $loggedIn}
<div class="flex justify-end">
<button on:click={() => showPopup.set(true)}>
<DocumentDuplicate class="w-4" />
<Tr t={t.button} />
</button>
</div>
{/if}