forked from MapComplete/MapComplete
		
	Add friendly confirm message on newly created points
This commit is contained in:
		
							parent
							
								
									5c28ce2995
								
							
						
					
					
						commit
						ccf3bb3993
					
				
					 6 changed files with 63 additions and 30 deletions
				
			
		| 
						 | 
				
			
			@ -3,9 +3,11 @@
 | 
			
		|||
 */
 | 
			
		||||
import { UIEventSource } from "./UIEventSource"
 | 
			
		||||
import { GeoJSONObject } from "@turf/turf"
 | 
			
		||||
import {Feature, Point} from "geojson";
 | 
			
		||||
import {OsmTags} from "../Models/OsmFeature";
 | 
			
		||||
 | 
			
		||||
export class ElementStorage {
 | 
			
		||||
    public ContainingFeatures = new Map<string, any>()
 | 
			
		||||
    public ContainingFeatures = new Map<string, Feature<Point, OsmTags >>()
 | 
			
		||||
    private _elements = new Map<string, UIEventSource<any>>()
 | 
			
		||||
 | 
			
		||||
    constructor() {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import { TagsFilter } from "./TagsFilter"
 | 
			
		|||
export class Tag extends TagsFilter {
 | 
			
		||||
    public key: string
 | 
			
		||||
    public value: string
 | 
			
		||||
    public static newlyCreated  = new Tag("_newly_created","yes") ;
 | 
			
		||||
    constructor(key: string, value: string) {
 | 
			
		||||
        super()
 | 
			
		||||
        this.key = key
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,7 @@ import Loading from "../Base/Loading"
 | 
			
		|||
import Hash from "../../Logic/Web/Hash"
 | 
			
		||||
import { GlobalFilter } from "../../Logic/State/MapState"
 | 
			
		||||
import { WayId } from "../../Models/OsmFeature"
 | 
			
		||||
import {Tag} from "../../Logic/Tags/Tag";
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * The SimpleAddUI is a single panel, which can have multiple states:
 | 
			
		||||
| 
						 | 
				
			
			@ -97,10 +98,11 @@ export default class SimpleAddUI extends Toggle {
 | 
			
		|||
        const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset, state)
 | 
			
		||||
 | 
			
		||||
        async function createNewPoint(
 | 
			
		||||
            tags: any[],
 | 
			
		||||
            tags: Tag[],
 | 
			
		||||
            location: { lat: number; lon: number },
 | 
			
		||||
            snapOntoWay?: OsmWay
 | 
			
		||||
        ): Promise<void> {
 | 
			
		||||
            tags.push(Tag.newlyCreated)
 | 
			
		||||
            const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
 | 
			
		||||
                theme: state.layoutToUse?.id ?? "unkown",
 | 
			
		||||
                changeType: "create",
 | 
			
		||||
| 
						 | 
				
			
			@ -109,9 +111,8 @@ export default class SimpleAddUI extends Toggle {
 | 
			
		|||
            await state.changes.applyAction(newElementAction)
 | 
			
		||||
            selectedPreset.setData(undefined)
 | 
			
		||||
            isShown.setData(false)
 | 
			
		||||
            state.selectedElement.setData(
 | 
			
		||||
                state.allElements.ContainingFeatures.get(newElementAction.newElementId)
 | 
			
		||||
            )
 | 
			
		||||
            const selectedFeature = state.allElements.ContainingFeatures.get(newElementAction.newElementId)
 | 
			
		||||
            state.selectedElement.setData(selectedFeature)
 | 
			
		||||
            Hash.hash.setData(newElementAction.newElementId)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,24 +1,24 @@
 | 
			
		|||
import { UIEventSource } from "../../Logic/UIEventSource"
 | 
			
		||||
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
 | 
			
		||||
import {UIEventSource} from "../../Logic/UIEventSource"
 | 
			
		||||
import {OsmConnection} from "../../Logic/Osm/OsmConnection"
 | 
			
		||||
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
 | 
			
		||||
import BaseUIElement from "../BaseUIElement"
 | 
			
		||||
import LocationInput from "../Input/LocationInput"
 | 
			
		||||
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
 | 
			
		||||
import { BBox } from "../../Logic/BBox"
 | 
			
		||||
import { TagUtils } from "../../Logic/Tags/TagUtils"
 | 
			
		||||
import { SubtleButton } from "../Base/SubtleButton"
 | 
			
		||||
import {BBox} from "../../Logic/BBox"
 | 
			
		||||
import {TagUtils} from "../../Logic/Tags/TagUtils"
 | 
			
		||||
import {SubtleButton} from "../Base/SubtleButton"
 | 
			
		||||
import Combine from "../Base/Combine"
 | 
			
		||||
import Translations from "../i18n/Translations"
 | 
			
		||||
import Svg from "../../Svg"
 | 
			
		||||
import Toggle from "../Input/Toggle"
 | 
			
		||||
import SimpleAddUI, { PresetInfo } from "../BigComponents/SimpleAddUI"
 | 
			
		||||
import SimpleAddUI, {PresetInfo} from "../BigComponents/SimpleAddUI"
 | 
			
		||||
import BaseLayer from "../../Models/BaseLayer"
 | 
			
		||||
import Img from "../Base/Img"
 | 
			
		||||
import Title from "../Base/Title"
 | 
			
		||||
import { GlobalFilter } from "../../Logic/State/MapState"
 | 
			
		||||
import { VariableUiElement } from "../Base/VariableUIElement"
 | 
			
		||||
import { Tag } from "../../Logic/Tags/Tag"
 | 
			
		||||
import { WayId } from "../../Models/OsmFeature"
 | 
			
		||||
import {GlobalFilter} from "../../Logic/State/MapState"
 | 
			
		||||
import {VariableUiElement} from "../Base/VariableUIElement"
 | 
			
		||||
import {Tag} from "../../Logic/Tags/Tag"
 | 
			
		||||
import {WayId} from "../../Models/OsmFeature"
 | 
			
		||||
 | 
			
		||||
export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		||||
    constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +46,7 @@ export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		|||
            // Create location input
 | 
			
		||||
 | 
			
		||||
            // We uncouple the event source
 | 
			
		||||
            const zloc = { ...loc, zoom: 19 }
 | 
			
		||||
            const zloc = {...loc, zoom: 19}
 | 
			
		||||
            const locationSrc = new UIEventSource(zloc)
 | 
			
		||||
 | 
			
		||||
            let backgroundLayer = new UIEventSource(
 | 
			
		||||
| 
						 | 
				
			
			@ -105,7 +105,7 @@ export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		|||
                        state.featurePipeline
 | 
			
		||||
                            .GetFeaturesWithin(layerId, bbox)
 | 
			
		||||
                            ?.forEach((feats) =>
 | 
			
		||||
                                allFeatures.push(...feats.map((f) => ({ feature: f })))
 | 
			
		||||
                                allFeatures.push(...feats.map((f) => ({feature: f})))
 | 
			
		||||
                            )
 | 
			
		||||
                    })
 | 
			
		||||
                    console.log("Snapping to", allFeatures)
 | 
			
		||||
| 
						 | 
				
			
			@ -118,7 +118,6 @@ export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		|||
            preset.icon(),
 | 
			
		||||
            new Combine([
 | 
			
		||||
                confirmText,
 | 
			
		||||
                Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert"),
 | 
			
		||||
            ]).SetClass("flex flex-col")
 | 
			
		||||
        )
 | 
			
		||||
            .SetClass("font-bold break-words")
 | 
			
		||||
| 
						 | 
				
			
			@ -139,15 +138,18 @@ export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		|||
                )
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
        const warn = Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert w-full block");
 | 
			
		||||
        if (preciseInput !== undefined) {
 | 
			
		||||
            confirmButton = new Combine([preciseInput, confirmButton])
 | 
			
		||||
            confirmButton = new Combine([preciseInput, warn, confirmButton])
 | 
			
		||||
        } else {
 | 
			
		||||
            confirmButton = new Combine([warn, confirmButton])
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const openLayerControl = new SubtleButton(
 | 
			
		||||
            Svg.layers_ui(),
 | 
			
		||||
            new Combine([
 | 
			
		||||
                Translations.t.general.add.layerNotEnabled
 | 
			
		||||
                    .Subs({ layer: preset.layerToAddTo.layerDef.name })
 | 
			
		||||
                    .Subs({layer: preset.layerToAddTo.layerDef.name})
 | 
			
		||||
                    .SetClass("alert"),
 | 
			
		||||
                Translations.t.general.add.openLayerControl,
 | 
			
		||||
            ])
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +185,7 @@ export default class ConfirmLocationOfPoint extends Combine {
 | 
			
		|||
            const filterConfirmPanel = new VariableUiElement(
 | 
			
		||||
                state.globalFilters.map((gfs) => {
 | 
			
		||||
                    const gf = gfs[i]
 | 
			
		||||
                    const confirm = gf.onNewPoint?.confirmAddNew?.Subs({ preset: preset.title })
 | 
			
		||||
                    const confirm = gf.onNewPoint?.confirmAddNew?.Subs({preset: preset.title})
 | 
			
		||||
                    return new Combine([
 | 
			
		||||
                        gf.onNewPoint?.safetyCheck,
 | 
			
		||||
                        new SubtleButton(Svg.confirm_svg(), confirm).onClick(() =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { UIEventSource } from "../../Logic/UIEventSource"
 | 
			
		||||
import {UIEventSource} from "../../Logic/UIEventSource"
 | 
			
		||||
import EditableTagRendering from "./EditableTagRendering"
 | 
			
		||||
import QuestionBox from "./QuestionBox"
 | 
			
		||||
import Combine from "../Base/Combine"
 | 
			
		||||
| 
						 | 
				
			
			@ -7,16 +7,19 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen"
 | 
			
		|||
import Constants from "../../Models/Constants"
 | 
			
		||||
import SharedTagRenderings from "../../Customizations/SharedTagRenderings"
 | 
			
		||||
import BaseUIElement from "../BaseUIElement"
 | 
			
		||||
import { VariableUiElement } from "../Base/VariableUIElement"
 | 
			
		||||
import {VariableUiElement} from "../Base/VariableUIElement"
 | 
			
		||||
import DeleteWizard from "./DeleteWizard"
 | 
			
		||||
import SplitRoadWizard from "./SplitRoadWizard"
 | 
			
		||||
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
 | 
			
		||||
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
 | 
			
		||||
import { Utils } from "../../Utils"
 | 
			
		||||
import {Utils} from "../../Utils"
 | 
			
		||||
import MoveWizard from "./MoveWizard"
 | 
			
		||||
import Toggle from "../Input/Toggle"
 | 
			
		||||
import Lazy from "../Base/Lazy"
 | 
			
		||||
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
 | 
			
		||||
import {Tag} from "../../Logic/Tags/Tag";
 | 
			
		||||
import Svg from "../../Svg";
 | 
			
		||||
import Translations from "../i18n/Translations";
 | 
			
		||||
 | 
			
		||||
export default class FeatureInfoBox extends ScrollableFullScreen {
 | 
			
		||||
    public constructor(
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +82,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
 | 
			
		|||
        state: FeaturePipelineState
 | 
			
		||||
    ): BaseUIElement {
 | 
			
		||||
        let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>()
 | 
			
		||||
 | 
			
		||||
        const t = Translations.t.general
 | 
			
		||||
        const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group))
 | 
			
		||||
        if (state?.featureSwitchUserbadge?.data ?? true) {
 | 
			
		||||
            const questionSpecs = layerConfig.tagRenderings.filter((tr) => tr.id === "questions")
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +101,29 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
 | 
			
		|||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const allRenderings = []
 | 
			
		||||
        const withQuestion = layerConfig.tagRenderings.filter(tr => tr.question !== undefined).length
 | 
			
		||||
 | 
			
		||||
        const allRenderings: BaseUIElement[] = [
 | 
			
		||||
            new VariableUiElement(
 | 
			
		||||
                tags.map(data => data[Tag.newlyCreated.key]).map(isCreated => {
 | 
			
		||||
                    if (isCreated !== Tag.newlyCreated.value) {
 | 
			
		||||
                        return undefined
 | 
			
		||||
                    }
 | 
			
		||||
                    const els = []
 | 
			
		||||
                    const thanks =
 | 
			
		||||
                        new Combine([
 | 
			
		||||
                            Svg.party_svg().SetClass("w-12 h-12 shrink-0 p-1 m-1 bg-white rounded-full block"),
 | 
			
		||||
                            t.newlyCreated
 | 
			
		||||
                        ]).SetClass("flex w-full thanks content-center")
 | 
			
		||||
                    els.push(thanks)
 | 
			
		||||
                    if (withQuestion > 0) {
 | 
			
		||||
                        els.push(t.feelFreeToSkip)
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return new Combine(els).SetClass("pb-4 mb-4 border-b block border-black")
 | 
			
		||||
                })
 | 
			
		||||
            )
 | 
			
		||||
        ]
 | 
			
		||||
        for (let i = 0; i < allGroupNames.length; i++) {
 | 
			
		||||
            const groupName = allGroupNames[i]
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -250,15 +275,15 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
 | 
			
		|||
        editElements.push(
 | 
			
		||||
            Toggle.If(state.featureSwitchIsDebugging, () => {
 | 
			
		||||
                const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
 | 
			
		||||
                    { render: "{all_tags()}" },
 | 
			
		||||
                    {render: "{all_tags()}"},
 | 
			
		||||
                    ""
 | 
			
		||||
                )
 | 
			
		||||
                const config_download: TagRenderingConfig = new TagRenderingConfig(
 | 
			
		||||
                    { render: "{export_as_geojson()}" },
 | 
			
		||||
                    {render: "{export_as_geojson()}"},
 | 
			
		||||
                    ""
 | 
			
		||||
                )
 | 
			
		||||
                const config_id: TagRenderingConfig = new TagRenderingConfig(
 | 
			
		||||
                    { render: "{open_in_iD()}" },
 | 
			
		||||
                    {render: "{open_in_iD()}"},
 | 
			
		||||
                    ""
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,6 +170,7 @@
 | 
			
		|||
        "error": "Something went wrong",
 | 
			
		||||
        "example": "Example",
 | 
			
		||||
        "examples": "Examples",
 | 
			
		||||
        "feelFreeToSkip": "You can add or update more information below, but feel free <b>to skip questions</b> you don't know the answer to.",
 | 
			
		||||
        "fewChangesBefore": "Please, answer a few questions of existing features before adding a new feature.",
 | 
			
		||||
        "getStartedLogin": "Log in with OpenStreetMap to get started",
 | 
			
		||||
        "getStartedNewAccount": " or <a href='https://www.openstreetmap.org/user/new' target='_blank'>create a new account</a>",
 | 
			
		||||
| 
						 | 
				
			
			@ -204,6 +205,7 @@
 | 
			
		|||
            "streetcomplete": "Another, similar application is <a href='https://play.google.com/store/apps/details?id=de.westnordost.streetcomplete' class='underline hover:text-blue-800' class='underline hover:text-blue-800' target='_blank'>StreetComplete</a>."
 | 
			
		||||
        },
 | 
			
		||||
        "nameInlineQuestion": "The name of this {category} is $$$",
 | 
			
		||||
        "newlyCreated": "You just created this element! Thanks for sharing this info with the world and helping people worldwide.",
 | 
			
		||||
        "next": "Next",
 | 
			
		||||
        "noMatchingMapping": "No entries mapped your search…",
 | 
			
		||||
        "noNameCategory": "{category} without a name",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue