Refactoring: fix delete indication, fix splitroad, fix addition of multiple new points snapped onto the same way (all will properly attach now)

This commit is contained in:
Pieter Vander Vennet 2023-04-20 17:42:07 +02:00
parent 1f9aacfb29
commit 4172af6a72
118 changed files with 1422 additions and 1357 deletions

View file

@ -4,7 +4,6 @@ import Combine from "./Base/Combine"
import MoreScreen from "./BigComponents/MoreScreen"
import Translations from "./i18n/Translations"
import Constants from "../Models/Constants"
import { Utils } from "../Utils"
import LanguagePicker from "./LanguagePicker"
import IndexText from "./BigComponents/IndexText"
import { ImportViewerLinks } from "./BigComponents/UserInformation"
@ -31,10 +30,8 @@ export default class AllThemesGui {
featureSwitchUserbadge: new ImmutableStore(true),
}),
new ImportViewerLinks(state.osmConnection),
Translations.t.general.aboutMapcomplete
.Subs({ osmcha_link: Utils.OsmChaLinkFor(7) })
.SetClass("link-underline"),
new FixedUiElement("v" + Constants.vNumber),
Translations.t.general.aboutMapComplete.intro.SetClass("link-underline"),
new FixedUiElement("v" + Constants.vNumber).SetClass("block"),
])
.SetClass("block m-5 lg:w-3/4 lg:ml-40")
.AttachTo("main")

View file

@ -99,14 +99,23 @@ export default class Hotkeys {
}
static generateDocumentation(): BaseUIElement {
const byKey: [string, string | Translation][] = Hotkeys._docs.data
let byKey: [string, string | Translation][] = Hotkeys._docs.data
.map(({ key, documentation }) => {
const modifiers = Object.keys(key).filter((k) => k !== "nomod" && k !== "onUp")
const keycode: string = key["ctrl"] ?? key["shift"] ?? key["alt"] ?? key["nomod"]
let keycode: string = key["ctrl"] ?? key["shift"] ?? key["alt"] ?? key["nomod"]
if (keycode.length == 1) {
keycode = keycode.toUpperCase()
}
modifiers.push(keycode)
return <[string, string | Translation]>[modifiers.join("+"), documentation]
})
.sort()
byKey = Utils.NoNull(byKey)
for (let i = byKey.length - 1; i > 0; i--) {
if (byKey[i - 1][0] === byKey[i][0]) {
byKey.splice(i, 1)
}
}
const t = Translations.t.hotkeyDocumentation
return new Combine([
new Title(t.title, 1),

View file

@ -1,72 +0,0 @@
import Combine from "../Base/Combine"
import { Store } from "../../Logic/UIEventSource"
import Translations from "../i18n/Translations"
import { SubtleButton } from "../Base/SubtleButton"
import Svg from "../../Svg"
import { Utils } from "../../Utils"
import { MapillaryLink } from "./MapillaryLink"
import { OpenIdEditor, OpenJosm } from "./CopyrightPanel"
import Toggle from "../Input/Toggle"
import { SpecialVisualizationState } from "../SpecialVisualization"
export class BackToThemeOverview extends Toggle {
constructor(
state: {
readonly featureSwitchMoreQuests: Store<boolean>
},
options: {
imgSize: string
}
) {
const t = Translations.t.general
const button = new SubtleButton(Svg.add_ui(), t.backToIndex, options).onClick(() => {
const path = window.location.href.split("/")
path.pop()
path.push("index.html")
window.location.href = path.join("/")
})
super(button, undefined, state.featureSwitchMoreQuests)
}
}
export class ActionButtons extends Combine {
constructor(state:SpecialVisualizationState) {
const imgSize = "h-6 w-6"
const iconStyle = "height: 1.5rem; width: 1.5rem"
const t = Translations.t.general.attribution
super([
new BackToThemeOverview(state, { imgSize }),
new SubtleButton(Svg.liberapay_ui(), t.donate, {
url: "https://liberapay.com/pietervdvn/",
newTab: true,
imgSize,
}),
new SubtleButton(Svg.bug_ui(), t.openIssueTracker, {
url: "https://github.com/pietervdvn/MapComplete/issues",
newTab: true,
imgSize,
}),
new SubtleButton(
Svg.statistics_ui(),
t.openOsmcha.Subs({ theme: state.layoutToUse.title }),
{
url: Utils.OsmChaLinkFor(31, state.layoutToUse.id),
newTab: true,
imgSize,
}
),
new SubtleButton(Svg.mastodon_ui(), t.followOnMastodon, {
url: "https://en.osm.town/@MapComplete",
newTab: true,
imgSize,
}),
new OpenIdEditor(state, iconStyle),
new MapillaryLink(state, iconStyle),
new OpenJosm(state.osmConnection,state.mapProperties.bounds, iconStyle).SetClass("hidden-on-mobile"),
])
this.SetClass("block w-full link-no-underline")
}
}

View file

@ -4,7 +4,6 @@ import Translations from "../i18n/Translations"
import { UIEventSource } from "../../Logic/UIEventSource"
import BaseUIElement from "../BaseUIElement"
import Toggle from "../Input/Toggle"
import { DownloadPanel } from "./DownloadPanel"
import { SubtleButton } from "../Base/SubtleButton"
import Svg from "../../Svg"
import ExportPDF from "../ExportPDF"
@ -79,13 +78,6 @@ export default class AllDownloads extends ScrollableFullScreen {
isExporting
)
const pdf = new Toggle(
new SubtleButton(icon, text),
undefined,
state.featureSwitchExportAsPdf
)
return pdf
return new SubtleButton(icon, text)
}
}

View file

@ -6,6 +6,8 @@
import TagRenderingAnswer from "../Popup/TagRendering/TagRenderingAnswer.svelte";
import TagRenderingEditable from "../Popup/TagRendering/TagRenderingEditable.svelte";
import { onDestroy } from "svelte";
import Translations from "../i18n/Translations";
import Tr from "../Base/Tr.svelte";
export let state: SpecialVisualizationState;
export let layer: LayerConfig;
@ -18,39 +20,45 @@
onDestroy(tags.addCallbackAndRun(tags => {
_tags = tags;
}));
let _metatags: Record<string, string>
onDestroy(state.userRelatedState.preferencesAsTags .addCallbackAndRun(tags => {
let _metatags: Record<string, string>;
onDestroy(state.userRelatedState.preferencesAsTags.addCallbackAndRun(tags => {
_metatags = tags;
}));
</script>
<div>
<div class="flex flex-col sm:flex-row flex-grow justify-between">
<!-- Title element-->
<h3>
<TagRenderingAnswer config={layer.title} {selectedElement} {state} {tags} {layer}></TagRenderingAnswer>
</h3>
<div class="flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2">
{#each layer.titleIcons as titleIconConfig (titleIconConfig.id)}
<div class="w-8 h-8">
<TagRenderingAnswer config={titleIconConfig} {tags} {selectedElement} {state} {layer}></TagRenderingAnswer>
</div>
{#if _tags._deleted === "yes"}
<Tr t={ Translations.t.delete.isDeleted} />
{:else}
<div>
<div class="flex flex-col sm:flex-row flex-grow justify-between">
<!-- Title element-->
<h3>
<TagRenderingAnswer config={layer.title} {selectedElement} {state} {tags} {layer}></TagRenderingAnswer>
</h3>
<div class="flex flex-row flex-wrap pt-0.5 sm:pt-1 items-center mr-2">
{#each layer.titleIcons as titleIconConfig (titleIconConfig.id)}
<div class="w-8 h-8">
<TagRenderingAnswer config={titleIconConfig} {tags} {selectedElement} {state} {layer}></TagRenderingAnswer>
</div>
{/each}
</div>
</div>
<div class="flex flex-col">
{#each layer.tagRenderings as config (config.id)}
{#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties({ ..._tags, ..._metatags }))}
{#if config.IsKnown(_tags)}
<TagRenderingEditable {tags} {config} {state} {selectedElement} {layer}
{highlightedRendering}></TagRenderingEditable>
{/if}
{/if}
{/each}
</div>
</div>
<div class="flex flex-col">
{#each layer.tagRenderings as config (config.id)}
{#if (config.condition === undefined || config.condition.matchesProperties(_tags)) && (config.metacondition === undefined || config.metacondition.matchesProperties(_metatags))}
{#if config.IsKnown(_tags)}
<TagRenderingEditable {tags} {config} {state} {selectedElement} {layer} {highlightedRendering}></TagRenderingEditable>
{/if}
{/if}
{/each}
</div>
</div>
{/if}

View file

@ -8,7 +8,6 @@ import { LoginToggle } from "../Popup/LoginButton"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
import LoggedInUserIndicator from "../LoggedInUserIndicator"
import { ActionButtons } from "./ActionButtons"
import { BBox } from "../../Logic/BBox"
import Loc from "../../Models/Loc"
import { DefaultGuiState } from "../DefaultGuiState"
@ -80,7 +79,6 @@ export default class ThemeIntroductionPanel extends Combine {
layout.descriptionTail?.Clone().SetClass("block mt-4"),
languagePicker?.SetClass("block mt-4 pb-8 border-b-2 border-dotted border-gray-400"),
new ActionButtons(state),
...layout.CustomCodeSnippets(),
])

View file

@ -14,7 +14,7 @@
/**
* Called when setup is done, can be used to add more layers to the map
*/
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void
export let onCreated : (value: Store<{lon: number, lat: number}> , map: Store<MlMap>, mapProperties: MapProperties ) => void = undefined
export let map: UIEventSource<MlMap> = new UIEventSource<MlMap>(undefined);
let mla = new MapLibreAdaptor(map, mapProperties);

View file

@ -240,13 +240,10 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
container.style.height = document.documentElement.clientHeight + "px"
}
await html2canvas(
map.getCanvasContainer(),
{
backgroundColor: "#00000000",
canvas: drawOn,
}
)
await html2canvas(map.getCanvasContainer(), {
backgroundColor: "#00000000",
canvas: drawOn,
})
} catch (e) {
console.error(e)
} finally {
@ -261,7 +258,7 @@ export class MapLibreAdaptor implements MapProperties, ExportableMap {
private updateStores() {
const map = this._maplibreMap.data
if (map === undefined) {
if (!map) {
return
}
const dt = this.location.data

View file

@ -9,7 +9,6 @@ import { OsmTags } from "../../Models/OsmFeature"
import { FeatureSource } from "../../Logic/FeatureSource/FeatureSource"
import { BBox } from "../../Logic/BBox"
import { Feature, Point } from "geojson"
import ScrollableFullScreen from "../Base/ScrollableFullScreen"
import LineRenderingConfig from "../../Models/ThemeConfig/LineRenderingConfig"
import { Utils } from "../../Utils"
import * as range_layer from "../../assets/layers/range/range.json"
@ -360,7 +359,6 @@ export default class ShowDataLayer {
drawMarkers?: true | boolean
drawLines?: true | boolean
}
private readonly _popupCache: Map<string, ScrollableFullScreen>
constructor(
map: Store<MlMap>,
@ -372,7 +370,6 @@ export default class ShowDataLayer {
) {
this._map = map
this._options = options
this._popupCache = new Map()
const self = this
map.addCallbackAndRunD((map) => self.initDrawFeatures(map))
}

View file

@ -88,16 +88,18 @@
changeType: "create",
snapOnto: snapToWay
});
await state.changes.applyAction(newElementAction);
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);
{
// Set some metainfo
const properties = tagsStore.data;
if (snapTo) {
// metatags (starting with underscore) are not uploaded, so we can safely mark this
delete properties["_referencing_ways"]
properties["_referencing_ways"] = `["${snapTo}"]`;
}
properties["_backend"] = state.osmConnection.Backend()

View file

@ -1,15 +1,9 @@
import { Store, UIEventSource } from "../../Logic/UIEventSource"
import QuestionBox from "./QuestionBox"
import { UIEventSource } from "../../Logic/UIEventSource"
import Combine from "../Base/Combine"
import TagRenderingAnswer from "./TagRenderingAnswer"
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 LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import Toggle from "../Input/Toggle"
import Lazy from "../Base/Lazy"
import FeaturePipelineState from "../../Logic/State/FeaturePipelineState"
import Svg from "../../Svg"
import Translations from "../i18n/Translations"
@ -31,7 +25,7 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
)
super(
() => undefined,
() => FeatureInfoBox.GenerateContent(tags, layerConfig, state, showAllQuestions),
() => FeatureInfoBox.GenerateContent(tags, layerConfig),
options?.hashToShow ?? tags.data.id ?? "item",
options?.isShown,
options
@ -42,60 +36,14 @@ export default class FeatureInfoBox extends ScrollableFullScreen {
}
}
public static GenerateContent(
tags: UIEventSource<any>,
layerConfig: LayerConfig
): BaseUIElement {
public static GenerateContent(tags: UIEventSource<any>): BaseUIElement {
return new Toggle(
new Combine([
Svg.delete_icon_svg().SetClass("w-8 h-8"),
Translations.t.delete.isDeleted,
]).SetClass("flex justify-center font-bold items-center"),
FeatureInfoBox.GenerateMainContent(tags, layerConfig),
new Combine([]).SetClass("block"),
tags.map((t) => t["_deleted"] == "yes")
)
}
private static GenerateMainContent(
tags: UIEventSource<any>,
layerConfig: LayerConfig
): BaseUIElement {
const t = Translations.t.general
const withQuestion = layerConfig.tagRenderings.filter(
(tr) => tr.question !== undefined
).length
const allRenderings: BaseUIElement[] = [
new VariableUiElement(
tags
.map((data) => data["_newly_created"])
.map((isCreated) => {
if (isCreated === undefined) {
return undefined
}
const createdDate = new Date(isCreated)
const secondsSinceCreation = (Date.now() - createdDate.getTime()) / 1000
if (secondsSinceCreation >= 60 * 5) {
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")
})
),
]
return new Combine(allRenderings).SetClass("block")
}
}

View file

@ -65,7 +65,6 @@ export default class SplitRoadWizard extends Combine {
leafletMap.setData(mapComponent.SetClass("w-full h-80"))
)
}
initMap()
// Toggle between splitmap
const splitButton = new SubtleButton(
@ -94,7 +93,6 @@ export default class SplitRoadWizard extends Combine {
await state.changes?.applyAction(splitAction)
// We throw away the old map and splitpoints, and create a new map from scratch
splitPoints.setData([])
initMap()
// Close the popup. The contributor has to select a segment again to make sure they continue editing the correct segment; see #1219
state.selectedElement?.setData(undefined)
@ -134,6 +132,11 @@ export default class SplitRoadWizard extends Combine {
),
new Toggle(mapView, splitToggle, splitClicked),
])
splitClicked.addCallback((view) => {
if (view) {
initMap()
}
})
this.dialogIsOpened = splitClicked
const self = this
splitButton.onClick(() => {

View file

@ -45,7 +45,6 @@
let questionsToAsk = tags.map(tags => {
const baseQuestions = (layer.tagRenderings ?? [])?.filter(tr => allowed(tr.labels) && tr.question !== undefined);
console.log("Determining questions for", baseQuestions)
const questionsToAsk: TagRenderingConfig[] = [];
for (const baseQuestion of baseQuestions) {
if (skippedQuestions.data.has(baseQuestion.id) > 0) {

View file

@ -23,10 +23,12 @@
export let layer: LayerConfig;
let trs: { then: Translation; icon?: string; iconClass?: string }[];
$: trs = Utils.NoNull(config?.GetRenderValues(_tags));
let classes = ""
$:classes = config?.classes?.join(" ") ?? "";
</script>
{#if config !== undefined && (config?.condition === undefined || config.condition.matchesProperties(_tags))}
<div class="flex flex-col w-full">
<div class={"flex flex-col w-full "+classes}>
{#if trs.length === 1}
<TagRenderingMapping mapping={trs[0]} {tags} {state} {selectedElement} {layer}></TagRenderingMapping>
{/if}

View file

@ -22,7 +22,6 @@
import CommunityIndexView from "./BigComponents/CommunityIndexView.svelte";
import FloatOver from "./Base/FloatOver.svelte";
import PrivacyPolicy from "./BigComponents/PrivacyPolicy";
import { Utils } from "../Utils";
import Constants from "../Models/Constants";
import TabbedGroup from "./Base/TabbedGroup.svelte";
import UserRelatedState from "../Logic/State/UserRelatedState";
@ -31,6 +30,8 @@
import CopyrightPanel from "./BigComponents/CopyrightPanel";
import { DownloadPanel } from "./BigComponents/DownloadPanel";
import ModalRight from "./Base/ModalRight.svelte";
import { Utils } from "../Utils";
import Hotkeys from "./Base/Hotkeys";
export let state: ThemeViewState;
let layout = state.layout;
@ -95,7 +96,8 @@
<div class="absolute top-0 right-0 mt-4 mr-4">
<If condition={state.featureSwitches.featureSwitchSearch}>
<Geosearch bounds={state.mapProperties.bounds} {selectedElement} {selectedLayer} perLayer={state.perLayer}></Geosearch>
<Geosearch bounds={state.mapProperties.bounds} perLayer={state.perLayer} {selectedElement}
{selectedLayer}></Geosearch>
</If>
</div>
@ -152,20 +154,22 @@
<RasterLayerPicker {availableLayers} value={mapproperties.rasterLayer}></RasterLayerPicker>
</If>
</div>
<div slot="title2" class="flex">
<img src="./assets/svg/download.svg" class="w-4 h-4"/>
<Tr t={Translations.t.general.download.title}/>
<div class="flex" slot="title2">
<If condition={state.featureSwitches.featureSwitchEnableExport}>
<img class="w-4 h-4" src="./assets/svg/download.svg" />
<Tr t={Translations.t.general.download.title} />
</If>
</div>
<div slot="content2">
<ToSvelte construct={() => new DownloadPanel(state)}/>
<ToSvelte construct={() => new DownloadPanel(state)} />
</div>
<div slot="title3">
<Tr t={Translations.t.general.attribution.title}/>
<Tr t={Translations.t.general.attribution.title} />
</div>
<ToSvelte slot="content3" construct={() => new CopyrightPanel(state)}></ToSvelte>
<ToSvelte construct={() => new CopyrightPanel(state)} slot="content3"></ToSvelte>
</TabbedGroup>
</FloatOver>
@ -177,17 +181,43 @@
<FloatOver on:close={() => state.guistate.menuIsOpened.setData(false)}>
<TabbedGroup tab={state.guistate.menuViewTabIndex}>
<div class="flex" slot="title0">
<Tr t={Translations.t.general.aboutMapcompleteTitle}></Tr>
<Tr t={Translations.t.general.menu.aboutMapComplete} />
</div>
<div class="flex flex-col" slot="content0">
<Tr t={Translations.t.general.aboutMapcomplete.Subs({
osmcha_link: Utils.OsmChaLinkFor(7),
})}></Tr>
<Tr t={Translations.t.general.aboutMapComplete.intro} />
<a class="flex" href={Utils.HomepageLink()}>
<img class="w-6 h-6" src="./assets/svg/add.svg">
<Tr t={Translations.t.general.backToIndex} />
</a>
<a class="flex" href="https://github.com/pietervdvn/MapComplete/issues" target="_blank">
<img class="w-6 h-6" src="./assets/svg/bug.svg">
<Tr t={Translations.t.general.attribution.openIssueTracker} />
</a>
<a class="flex" href="https://en.osm.town/@MapComplete" target="_blank">
<img class="w-6 h-6" src="./assets/svg/mastodon.svg">
<Tr t={Translations.t.general.attribution.followOnMastodon} />
</a>
<a class="flex" href="https://liberapay.com/pietervdvn/" target="_blank">
<img class="w-6 h-6" src="./assets/svg/liberapay.svg">
<Tr t={Translations.t.general.attribution.donate} />
</a>
<a class="flex" href={Utils.OsmChaLinkFor(7)} target="_blank">
<img class="w-6 h-6" src="./assets/svg/statistics.svg">
<Tr t={Translations.t.general.attribution.openOsmcha.Subs({theme: "MapComplete"})} />
</a>
{Constants.vNumber}
<ToSvelte construct={Hotkeys.generateDocumentationDynamic}></ToSvelte>
</div>
<If condition={state.osmConnection.isLoggedIn} slot="title1">
<div class="flex">
<CogIcon class="w-6 h-6" />
@ -199,16 +229,16 @@
<!-- All shown components are set by 'usersettings.json', which happily uses some special visualisations created specifically for it -->
<LoginToggle {state}>
<div slot="not-logged-in">
<Tr class="alert" t={Translations.t.userinfo.notLoggedIn}/>
<Tr class="alert" t={Translations.t.userinfo.notLoggedIn} />
<LoginButton osmConnection={state.osmConnection}></LoginButton>
</div>
<SelectedElementView
layer={UserRelatedState.usersettingsConfig}
selectedElement={({
highlightedRendering={state.guistate.highlightedUserSetting}
layer={UserRelatedState.usersettingsConfig} selectedElement={({
type:"Feature",properties: {}, geometry: {type:"Point", coordinates: [0,0]}
})} {state}
tags={state.userRelatedState.preferencesAsTags}
highlightedRendering={state.guistate.highlightedUserSetting}
})}
{state}
tags={state.userRelatedState.preferencesAsTags}
/>
</LoginToggle>
</div>

View file

@ -5,8 +5,7 @@ import CompiledTranslations from "../../assets/generated/CompiledTranslations"
import LanguageUtils from "../../Utils/LanguageUtils"
export default class Translations {
static readonly t: typeof CompiledTranslations.t & Readonly<typeof CompiledTranslations.t> =
CompiledTranslations.t
static readonly t: Readonly<typeof CompiledTranslations.t> = CompiledTranslations.t
private static knownLanguages = LanguageUtils.usedLanguages
constructor() {
throw "Translations is static. If you want to intitialize a new translation, use the singular form"