Add friendly confirm message on newly created points

This commit is contained in:
Pieter Vander Vennet 2022-11-07 23:12:31 +01:00
parent 5c28ce2995
commit ccf3bb3993
6 changed files with 63 additions and 30 deletions

View file

@ -3,9 +3,11 @@
*/ */
import { UIEventSource } from "./UIEventSource" import { UIEventSource } from "./UIEventSource"
import { GeoJSONObject } from "@turf/turf" import { GeoJSONObject } from "@turf/turf"
import {Feature, Point} from "geojson";
import {OsmTags} from "../Models/OsmFeature";
export class ElementStorage { export class ElementStorage {
public ContainingFeatures = new Map<string, any>() public ContainingFeatures = new Map<string, Feature<Point, OsmTags >>()
private _elements = new Map<string, UIEventSource<any>>() private _elements = new Map<string, UIEventSource<any>>()
constructor() {} constructor() {}

View file

@ -4,6 +4,7 @@ import { TagsFilter } from "./TagsFilter"
export class Tag extends TagsFilter { export class Tag extends TagsFilter {
public key: string public key: string
public value: string public value: string
public static newlyCreated = new Tag("_newly_created","yes") ;
constructor(key: string, value: string) { constructor(key: string, value: string) {
super() super()
this.key = key this.key = key
@ -63,7 +64,7 @@ export class Tag extends TagsFilter {
} }
/** /**
const t = new Tag("key", "value") const t = new Tag("key", "value")
t.asHumanString() // => "key=value" t.asHumanString() // => "key=value"
t.asHumanString(true) // => "<a href='https://wiki.openstreetmap.org/wiki/Key:key' target='_blank'>key</a>=<a href='https://wiki.openstreetmap.org/wiki/Tag:key%3Dvalue' target='_blank'>value</a>" t.asHumanString(true) // => "<a href='https://wiki.openstreetmap.org/wiki/Key:key' target='_blank'>key</a>=<a href='https://wiki.openstreetmap.org/wiki/Tag:key%3Dvalue' target='_blank'>value</a>"

View file

@ -27,6 +27,7 @@ import Loading from "../Base/Loading"
import Hash from "../../Logic/Web/Hash" import Hash from "../../Logic/Web/Hash"
import { GlobalFilter } from "../../Logic/State/MapState" import { GlobalFilter } from "../../Logic/State/MapState"
import { WayId } from "../../Models/OsmFeature" import { WayId } from "../../Models/OsmFeature"
import {Tag} from "../../Logic/Tags/Tag";
/* /*
* The SimpleAddUI is a single panel, which can have multiple states: * 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) const presetsOverview = SimpleAddUI.CreateAllPresetsPanel(selectedPreset, state)
async function createNewPoint( async function createNewPoint(
tags: any[], tags: Tag[],
location: { lat: number; lon: number }, location: { lat: number; lon: number },
snapOntoWay?: OsmWay snapOntoWay?: OsmWay
): Promise<void> { ): Promise<void> {
tags.push(Tag.newlyCreated)
const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, { const newElementAction = new CreateNewNodeAction(tags, location.lat, location.lon, {
theme: state.layoutToUse?.id ?? "unkown", theme: state.layoutToUse?.id ?? "unkown",
changeType: "create", changeType: "create",
@ -109,9 +111,8 @@ export default class SimpleAddUI extends Toggle {
await state.changes.applyAction(newElementAction) await state.changes.applyAction(newElementAction)
selectedPreset.setData(undefined) selectedPreset.setData(undefined)
isShown.setData(false) isShown.setData(false)
state.selectedElement.setData( const selectedFeature = state.allElements.ContainingFeatures.get(newElementAction.newElementId)
state.allElements.ContainingFeatures.get(newElementAction.newElementId) state.selectedElement.setData(selectedFeature)
)
Hash.hash.setData(newElementAction.newElementId) Hash.hash.setData(newElementAction.newElementId)
} }

View file

@ -1,24 +1,24 @@
import { UIEventSource } from "../../Logic/UIEventSource" import {UIEventSource} from "../../Logic/UIEventSource"
import { OsmConnection } from "../../Logic/Osm/OsmConnection" import {OsmConnection} from "../../Logic/Osm/OsmConnection"
import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline" import FeaturePipeline from "../../Logic/FeatureSource/FeaturePipeline"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import LocationInput from "../Input/LocationInput" import LocationInput from "../Input/LocationInput"
import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers" import AvailableBaseLayers from "../../Logic/Actors/AvailableBaseLayers"
import { BBox } from "../../Logic/BBox" import {BBox} from "../../Logic/BBox"
import { TagUtils } from "../../Logic/Tags/TagUtils" import {TagUtils} from "../../Logic/Tags/TagUtils"
import { SubtleButton } from "../Base/SubtleButton" import {SubtleButton} from "../Base/SubtleButton"
import Combine from "../Base/Combine" import Combine from "../Base/Combine"
import Translations from "../i18n/Translations" import Translations from "../i18n/Translations"
import Svg from "../../Svg" import Svg from "../../Svg"
import Toggle from "../Input/Toggle" import Toggle from "../Input/Toggle"
import SimpleAddUI, { PresetInfo } from "../BigComponents/SimpleAddUI" import SimpleAddUI, {PresetInfo} from "../BigComponents/SimpleAddUI"
import BaseLayer from "../../Models/BaseLayer" import BaseLayer from "../../Models/BaseLayer"
import Img from "../Base/Img" import Img from "../Base/Img"
import Title from "../Base/Title" import Title from "../Base/Title"
import { GlobalFilter } from "../../Logic/State/MapState" import {GlobalFilter} from "../../Logic/State/MapState"
import { VariableUiElement } from "../Base/VariableUIElement" import {VariableUiElement} from "../Base/VariableUIElement"
import { Tag } from "../../Logic/Tags/Tag" import {Tag} from "../../Logic/Tags/Tag"
import { WayId } from "../../Models/OsmFeature" import {WayId} from "../../Models/OsmFeature"
export default class ConfirmLocationOfPoint extends Combine { export default class ConfirmLocationOfPoint extends Combine {
constructor( constructor(
@ -46,7 +46,7 @@ export default class ConfirmLocationOfPoint extends Combine {
// Create location input // Create location input
// We uncouple the event source // We uncouple the event source
const zloc = { ...loc, zoom: 19 } const zloc = {...loc, zoom: 19}
const locationSrc = new UIEventSource(zloc) const locationSrc = new UIEventSource(zloc)
let backgroundLayer = new UIEventSource( let backgroundLayer = new UIEventSource(
@ -105,7 +105,7 @@ export default class ConfirmLocationOfPoint extends Combine {
state.featurePipeline state.featurePipeline
.GetFeaturesWithin(layerId, bbox) .GetFeaturesWithin(layerId, bbox)
?.forEach((feats) => ?.forEach((feats) =>
allFeatures.push(...feats.map((f) => ({ feature: f }))) allFeatures.push(...feats.map((f) => ({feature: f})))
) )
}) })
console.log("Snapping to", allFeatures) console.log("Snapping to", allFeatures)
@ -118,7 +118,6 @@ export default class ConfirmLocationOfPoint extends Combine {
preset.icon(), preset.icon(),
new Combine([ new Combine([
confirmText, confirmText,
Translations.t.general.add.warnVisibleForEveryone.Clone().SetClass("alert"),
]).SetClass("flex flex-col") ]).SetClass("flex flex-col")
) )
.SetClass("font-bold break-words") .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) { if (preciseInput !== undefined) {
confirmButton = new Combine([preciseInput, confirmButton]) confirmButton = new Combine([preciseInput, warn, confirmButton])
} else {
confirmButton = new Combine([warn, confirmButton])
} }
const openLayerControl = new SubtleButton( const openLayerControl = new SubtleButton(
Svg.layers_ui(), Svg.layers_ui(),
new Combine([ new Combine([
Translations.t.general.add.layerNotEnabled Translations.t.general.add.layerNotEnabled
.Subs({ layer: preset.layerToAddTo.layerDef.name }) .Subs({layer: preset.layerToAddTo.layerDef.name})
.SetClass("alert"), .SetClass("alert"),
Translations.t.general.add.openLayerControl, Translations.t.general.add.openLayerControl,
]) ])
@ -183,7 +185,7 @@ export default class ConfirmLocationOfPoint extends Combine {
const filterConfirmPanel = new VariableUiElement( const filterConfirmPanel = new VariableUiElement(
state.globalFilters.map((gfs) => { state.globalFilters.map((gfs) => {
const gf = gfs[i] 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([ return new Combine([
gf.onNewPoint?.safetyCheck, gf.onNewPoint?.safetyCheck,
new SubtleButton(Svg.confirm_svg(), confirm).onClick(() => new SubtleButton(Svg.confirm_svg(), confirm).onClick(() =>

View file

@ -1,4 +1,4 @@
import { UIEventSource } from "../../Logic/UIEventSource" import {UIEventSource} from "../../Logic/UIEventSource"
import EditableTagRendering from "./EditableTagRendering" import EditableTagRendering from "./EditableTagRendering"
import QuestionBox from "./QuestionBox" import QuestionBox from "./QuestionBox"
import Combine from "../Base/Combine" import Combine from "../Base/Combine"
@ -7,16 +7,19 @@ import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import Constants from "../../Models/Constants" import Constants from "../../Models/Constants"
import SharedTagRenderings from "../../Customizations/SharedTagRenderings" import SharedTagRenderings from "../../Customizations/SharedTagRenderings"
import BaseUIElement from "../BaseUIElement" import BaseUIElement from "../BaseUIElement"
import { VariableUiElement } from "../Base/VariableUIElement" import {VariableUiElement} from "../Base/VariableUIElement"
import DeleteWizard from "./DeleteWizard" import DeleteWizard from "./DeleteWizard"
import SplitRoadWizard from "./SplitRoadWizard" import SplitRoadWizard from "./SplitRoadWizard"
import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig" import TagRenderingConfig from "../../Models/ThemeConfig/TagRenderingConfig"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import { Utils } from "../../Utils" import {Utils} from "../../Utils"
import MoveWizard from "./MoveWizard" import MoveWizard from "./MoveWizard"
import Toggle from "../Input/Toggle" import Toggle from "../Input/Toggle"
import Lazy from "../Base/Lazy" import Lazy from "../Base/Lazy"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState" 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 { export default class FeatureInfoBox extends ScrollableFullScreen {
public constructor( public constructor(
@ -79,7 +82,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
state: FeaturePipelineState state: FeaturePipelineState
): BaseUIElement { ): BaseUIElement {
let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>() let questionBoxes: Map<string, QuestionBox> = new Map<string, QuestionBox>()
const t = Translations.t.general
const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group)) const allGroupNames = Utils.Dedup(layerConfig.tagRenderings.map((tr) => tr.group))
if (state?.featureSwitchUserbadge?.data ?? true) { if (state?.featureSwitchUserbadge?.data ?? true) {
const questionSpecs = layerConfig.tagRenderings.filter((tr) => tr.id === "questions") 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++) { for (let i = 0; i < allGroupNames.length; i++) {
const groupName = allGroupNames[i] const groupName = allGroupNames[i]
@ -250,15 +275,15 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
editElements.push( editElements.push(
Toggle.If(state.featureSwitchIsDebugging, () => { Toggle.If(state.featureSwitchIsDebugging, () => {
const config_all_tags: TagRenderingConfig = new TagRenderingConfig( const config_all_tags: TagRenderingConfig = new TagRenderingConfig(
{ render: "{all_tags()}" }, {render: "{all_tags()}"},
"" ""
) )
const config_download: TagRenderingConfig = new TagRenderingConfig( const config_download: TagRenderingConfig = new TagRenderingConfig(
{ render: "{export_as_geojson()}" }, {render: "{export_as_geojson()}"},
"" ""
) )
const config_id: TagRenderingConfig = new TagRenderingConfig( const config_id: TagRenderingConfig = new TagRenderingConfig(
{ render: "{open_in_iD()}" }, {render: "{open_in_iD()}"},
"" ""
) )

View file

@ -170,6 +170,7 @@
"error": "Something went wrong", "error": "Something went wrong",
"example": "Example", "example": "Example",
"examples": "Examples", "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.", "fewChangesBefore": "Please, answer a few questions of existing features before adding a new feature.",
"getStartedLogin": "Log in with OpenStreetMap to get started", "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>", "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>." "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 $$$", "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", "next": "Next",
"noMatchingMapping": "No entries mapped your search…", "noMatchingMapping": "No entries mapped your search…",
"noNameCategory": "{category} without a name", "noNameCategory": "{category} without a name",