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 { 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() {}
|
||||||
|
|
|
@ -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>"
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(() =>
|
||||||
|
|
|
@ -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()}"},
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -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",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue