Refactoring: use theme instead of layout in a lot of places

This commit is contained in:
Pieter Vander Vennet 2024-10-17 04:06:03 +02:00
parent 9427083939
commit bdc9ba52a6
104 changed files with 445 additions and 449 deletions

View file

@ -1,5 +1,5 @@
import { ImmutableStore, Store, UIEventSource } from "../UIEventSource"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { LocalStorageSource } from "../Web/LocalStorageSource"
import { QueryParameters } from "../Web/QueryParameters"
import Hash from "../Web/Hash"
@ -25,7 +25,7 @@ export default class InitialMapPositioning {
public location: UIEventSource<{ lon: number; lat: number }>
public useTerrain: Store<boolean>
constructor(layoutToUse: LayoutConfig, geolocationState: GeoLocationState) {
constructor(layoutToUse: ThemeConfig, geolocationState: GeoLocationState) {
function localStorageSynced(
key: string,
deflt: number,

View file

@ -14,7 +14,7 @@ export default class NoElementsInViewDetector {
constructor(themeViewState: ThemeViewState) {
const state = themeViewState
const minZoom = Math.min(
...themeViewState.layout.layers
...themeViewState.theme.layers
.filter((l) => Constants.priviliged_layers.indexOf(<any>l.id) < 0)
.map((l) => l.minzoom)
)
@ -43,7 +43,7 @@ export default class NoElementsInViewDetector {
// Nope, no data loaded
continue
}
const layer = themeViewState.layout.getLayer(layerName)
const layer = themeViewState.theme.getLayer(layerName)
if (mapProperties.zoom.data < layer.minzoom) {
minzoomWithData = Math.min(layer.minzoom)
continue
@ -67,7 +67,7 @@ export default class NoElementsInViewDetector {
continue
}
const layer = themeViewState.layout.getLayer(layerName)
const layer = themeViewState.theme.getLayer(layerName)
if (mapProperties.zoom.data < layer.minzoom) {
continue
}

View file

@ -34,7 +34,7 @@ export default class SelectedElementTagsUpdater {
public static applyUpdate(latestTags: OsmTags, id: string, state: SpecialVisualizationState) {
try {
const leftRightSensitive = state.layout.isLeftRightSensitive()
const leftRightSensitive = state.theme.isLeftRightSensitive()
if (leftRightSensitive) {
SimpleMetaTagger.removeBothTagging(latestTags)
@ -111,7 +111,7 @@ export default class SelectedElementTagsUpdater {
}
private invalidateCache(s: Feature) {
const state = this.state
const wasPartOfLayer = state.layout.getMatchingLayer(s.properties)
const wasPartOfLayer = state.theme.getMatchingLayer(s.properties)
state.toCacheSavers?.get(wasPartOfLayer.id)?.invalidateCacheAround(BBox.get(s))
}
private installCallback() {

View file

@ -12,11 +12,11 @@ export default class TitleHandler {
const currentTitle: Store<string> = selectedElement.map(
(selected) => {
const lng = Locale.language.data
const defaultTitle = state.layout?.title?.textFor(lng) ?? "MapComplete"
const defaultTitle = state.theme?.title?.textFor(lng) ?? "MapComplete"
if (selected === undefined) {
return defaultTitle
}
const layer = state.layout.getMatchingLayer(selected.properties)
const layer = state.theme.getMatchingLayer(selected.properties)
if (layer === undefined) {
return defaultTitle
}

View file

@ -1,4 +1,4 @@
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { QueryParameters } from "./Web/QueryParameters"
import { AllKnownLayouts } from "../Customizations/AllKnownLayouts"
import { FixedUiElement } from "../UI/Base/FixedUiElement"
@ -19,10 +19,10 @@ import { DesugaringContext } from "../Models/ThemeConfig/Conversion/Conversion"
import { TagRenderingConfigJson } from "../Models/ThemeConfig/Json/TagRenderingConfigJson"
import Hash from "./Web/Hash"
import { QuestionableTagRenderingConfigJson } from "../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson"
import { ThemeConfigJson } from "../Models/ThemeConfig/Json/ThemeConfigJson"
import { ValidateThemeAndLayers } from "../Models/ThemeConfig/Conversion/ValidateThemeAndLayers"
export default class DetermineLayout {
export default class DetermineTheme {
private static readonly _knownImages = new Set(Array.from(licenses).map((l) => l.path))
private static readonly loadCustomThemeParam = QueryParameters.GetQueryParameter(
"userlayout",
@ -31,7 +31,7 @@ export default class DetermineLayout {
)
public static getCustomDefinition(): string {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data)
const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) {
return layoutFromBase64
@ -53,8 +53,8 @@ export default class DetermineLayout {
}
private static async expandRemoteLayers(
layoutConfig: LayoutConfigJson
): Promise<LayoutConfigJson> {
layoutConfig: ThemeConfigJson
): Promise<ThemeConfigJson> {
if (!layoutConfig.layers) {
// This is probably a layer in 'layer-only-mode'
return layoutConfig
@ -79,16 +79,16 @@ export default class DetermineLayout {
/**
* Gets the correct layout for this website
*/
public static async GetLayout(): Promise<LayoutConfig | undefined> {
const layoutFromBase64 = decodeURIComponent(DetermineLayout.loadCustomThemeParam.data)
public static async getTheme(): Promise<ThemeConfig | undefined> {
const layoutFromBase64 = decodeURIComponent(DetermineTheme.loadCustomThemeParam.data)
if (layoutFromBase64.startsWith("http")) {
return await DetermineLayout.LoadRemoteTheme(layoutFromBase64)
return await DetermineTheme.LoadRemoteTheme(layoutFromBase64)
}
if (layoutFromBase64 !== "false") {
// We have to load something from the hash (or from disk)
return await DetermineLayout.LoadLayoutFromHash(DetermineLayout.loadCustomThemeParam)
return await DetermineTheme.LoadLayoutFromHash(DetermineTheme.loadCustomThemeParam)
}
let layoutId: string = undefined
@ -127,7 +127,7 @@ export default class DetermineLayout {
public static async LoadLayoutFromHash(
userLayoutParam: UIEventSource<string>
): Promise<LayoutConfig | null> {
): Promise<ThemeConfig | null> {
let hash = location.hash.substr(1)
let json: any
@ -157,7 +157,7 @@ export default class DetermineLayout {
json = await this.expandRemoteLayers(json)
const layoutToUse = DetermineLayout.prepCustomTheme(json)
const layoutToUse = DetermineTheme.prepCustomTheme(json)
userLayoutParam.setData(layoutToUse.id)
return layoutToUse
}
@ -166,7 +166,7 @@ export default class DetermineLayout {
const dict = new Map<string, QuestionableTagRenderingConfigJson>()
for (const tagRendering of questions.tagRenderings) {
dict.set(tagRendering.id, tagRendering)
dict.set(tagRendering.id, <QuestionableTagRenderingConfigJson> tagRendering)
}
return dict
@ -176,7 +176,7 @@ export default class DetermineLayout {
return questions.tagRenderings.map((tr) => tr.id)
}
private static prepCustomTheme(json: any, sourceUrl?: string, forceId?: string): LayoutConfig {
private static prepCustomTheme(json: any, sourceUrl?: string, forceId?: string): ThemeConfig {
if (json.layers === undefined && json.tagRenderings !== undefined) {
// We got fed a layer instead of a theme
const layerConfig = <LayerConfigJson>json
@ -224,15 +224,15 @@ export default class DetermineLayout {
knownLayersDict.set(layer.id, <LayerConfigJson>layer)
}
const convertState: DesugaringContext = {
tagRenderings: DetermineLayout.getSharedTagRenderings(),
tagRenderingOrder: DetermineLayout.getSharedTagRenderingOrder(),
tagRenderings: DetermineTheme.getSharedTagRenderings(),
tagRenderingOrder: DetermineTheme.getSharedTagRenderingOrder(),
sharedLayers: knownLayersDict,
publicLayers: new Set<string>(),
}
json = new FixLegacyTheme().convertStrict(json)
const raw = json
json = new FixImages(DetermineLayout._knownImages).convertStrict(json)
json = new FixImages(DetermineTheme._knownImages).convertStrict(json)
json.enableNoteImports = json.enableNoteImports ?? false
json = new PrepareTheme(convertState).convertStrict(json)
console.log("The layoutconfig is ", json)
@ -249,20 +249,20 @@ export default class DetermineLayout {
false
).convertStrict(json)
}
return new LayoutConfig(json, false, {
return new ThemeConfig(json, false, {
definitionRaw: JSON.stringify(raw, null, " "),
definedAtUrl: sourceUrl,
})
}
private static async LoadRemoteTheme(link: string): Promise<LayoutConfig | null> {
private static async LoadRemoteTheme(link: string): Promise<ThemeConfig | null> {
console.log("Downloading map theme from ", link)
new FixedUiElement(`Downloading the theme from the <a href="${link}">link</a>...`).AttachTo(
"maindiv"
)
let parsed = <LayoutConfigJson>await Utils.downloadJson(link)
let parsed = <ThemeConfigJson>await Utils.downloadJson(link)
let forcedId = parsed.id
const url = new URL(link)
if (!(url.hostname === "localhost" || url.hostname === "127.0.0.1")) {
@ -270,6 +270,6 @@ export default class DetermineLayout {
}
console.log("Loaded remote link:", link)
parsed = await this.expandRemoteLayers(parsed)
return DetermineLayout.prepCustomTheme(parsed, link, forcedId)
return DetermineTheme.prepCustomTheme(parsed, link, forcedId)
}
}

View file

@ -49,7 +49,7 @@ export default class FavouritesFeatureSource extends StaticFeatureSource {
const featuresWithoutAlreadyPresent = features.map((features) =>
features.filter(
(feat) => !state.layout.layers.some((l) => l.id === feat.properties._orig_layer)
(feat) => !state.theme.layers.some((l) => l.id === feat.properties._orig_layer)
)
)

View file

@ -1,4 +1,4 @@
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { ImmutableStore, Store, UIEventSource } from "../../UIEventSource"
import { Feature, Point } from "geojson"
import { TagUtils } from "../../Tags/TagUtils"
@ -22,7 +22,7 @@ export class LastClickFeatureSource implements FeatureSource {
private _usermode: UIEventSource<string>
private _enabledAddMorePoints: UIEventSource<boolean>
constructor(
layout: LayoutConfig,
layout: ThemeConfig,
clickSource: Store<{ lon: number; lat: number; mode: "left" | "right" | "middle" }>,
usermode?: UIEventSource<string>,
enabledAddMorePoints?: UIEventSource<boolean>

View file

@ -18,7 +18,7 @@ import FeatureSourceMerger from "./FeatureSourceMerger"
*
* Note that special layers (with `source=null` will be ignored)
*/
export default class LayoutSource extends FeatureSourceMerger {
export default class ThemeSource extends FeatureSourceMerger {
/**
* Indicates if a data source is loading something
*/
@ -51,7 +51,7 @@ export default class LayoutSource extends FeatureSourceMerger {
const src = new LocalStorageFeatureSource(
backend,
layer,
LayoutSource.fromCacheZoomLevel,
ThemeSource.fromCacheZoomLevel,
mapProperties,
{
isActive: isDisplayed(layer.id),
@ -63,13 +63,13 @@ export default class LayoutSource extends FeatureSourceMerger {
}
const mvtSources: UpdatableFeatureSource[] = osmLayers
.filter((f) => mvtAvailableLayers.has(f.id))
.map((l) => LayoutSource.setupMvtSource(l, mapProperties, isDisplayed(l.id)))
.map((l) => ThemeSource.setupMvtSource(l, mapProperties, isDisplayed(l.id)))
const nonMvtSources = []
const nonMvtLayers = osmLayers.filter((l) => !mvtAvailableLayers.has(l.id))
const isLoading = new UIEventSource(false)
const osmApiSource = LayoutSource.setupOsmApiSource(
const osmApiSource = ThemeSource.setupOsmApiSource(
osmLayers,
bounds,
zoom,
@ -86,7 +86,7 @@ export default class LayoutSource extends FeatureSourceMerger {
nonMvtLayers.map((l) => l.id),
" cannot be fetched from the cache server, defaulting to overpass/OSM-api"
)
overpassSource = LayoutSource.setupOverpass(osmLayers, bounds, zoom, featureSwitches)
overpassSource = ThemeSource.setupOverpass(osmLayers, bounds, zoom, featureSwitches)
nonMvtSources.push(overpassSource)
supportsForceDownload.push(overpassSource)
}
@ -100,7 +100,7 @@ export default class LayoutSource extends FeatureSourceMerger {
osmApiSource?.isRunning?.addCallbackAndRun(() => setIsLoading())
const geojsonSources: UpdatableFeatureSource[] = geojsonlayers.map((l) =>
LayoutSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
ThemeSource.setupGeojsonSource(l, mapProperties, isDisplayed(l.id))
)
super(...geojsonSources, ...Array.from(fromCache.values()), ...mvtSources, ...nonMvtSources)
@ -198,7 +198,7 @@ export default class LayoutSource extends FeatureSourceMerger {
zoom,
bounds,
layers: osmLayers,
widenFactor: featureSwitches.layoutToUse.widenFactor,
widenFactor: 1.5,
overpassUrl: featureSwitches.overpassUrl,
overpassTimeout: featureSwitches.overpassTimeout,
overpassMaxZoom: featureSwitches.overpassMaxZoom,

View file

@ -2,7 +2,7 @@ import { ImageUploader, UploadResult } from "./ImageUploader"
import LinkImageAction from "../Osm/Actions/LinkImageAction"
import FeaturePropertiesStore from "../FeatureSource/Actors/FeaturePropertiesStore"
import { OsmId, OsmTags } from "../../Models/OsmFeature"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Store, UIEventSource } from "../UIEventSource"
import { OsmConnection } from "../Osm/OsmConnection"
import { Changes } from "../Osm/Changes"
@ -17,7 +17,7 @@ import { GeoOperations } from "../GeoOperations"
export class ImageUploadManager {
private readonly _uploader: ImageUploader
private readonly _featureProperties: FeaturePropertiesStore
private readonly _layout: LayoutConfig
private readonly _theme: ThemeConfig
private readonly _indexedFeatures: IndexedFeatureSource
private readonly _gps: Store<GeolocationCoordinates | undefined>
private readonly _uploadStarted: Map<string, UIEventSource<number>> = new Map()
@ -31,7 +31,7 @@ export class ImageUploadManager {
private readonly _reportError: (message: (string | Error | XMLHttpRequest), extramessage?: string) => Promise<void>
constructor(
layout: LayoutConfig,
layout: ThemeConfig,
uploader: ImageUploader,
featureProperties: FeaturePropertiesStore,
osmConnection: OsmConnection,
@ -42,7 +42,7 @@ export class ImageUploadManager {
) {
this._uploader = uploader
this._featureProperties = featureProperties
this._layout = layout
this._theme = layout
this._osmConnection = osmConnection
this._changes = changes
this._indexedFeatures = allFeatures
@ -131,7 +131,7 @@ export class ImageUploadManager {
const properties = this._featureProperties.getStore(featureId)
const action = new LinkImageAction(featureId, uploadResult. key, uploadResult . value, properties, {
theme: tags?.data?.["_orig_theme"] ?? this._layout.id,
theme: tags?.data?.["_orig_theme"] ?? this._theme.id,
changeType: "add-image",
})

View file

@ -3,7 +3,7 @@ import { ExtraFuncParams, ExtraFunctions, ExtraFuncType } from "./ExtraFunctions
import LayerConfig from "../Models/ThemeConfig/LayerConfig"
import { Feature } from "geojson"
import FeaturePropertiesStore from "./FeatureSource/Actors/FeaturePropertiesStore"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import { GeoIndexedStoreForLayer } from "./FeatureSource/Actors/GeoIndexedStore"
import { IndexedFeatureSource } from "./FeatureSource/FeatureSource"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
@ -27,7 +27,7 @@ export default class MetaTagging {
>()
private state: {
readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource
@ -40,7 +40,7 @@ export default class MetaTagging {
constructor(state: {
readonly selectedElement: Store<Feature>
readonly layout: LayoutConfig
readonly theme: ThemeConfig
readonly osmObjectDownloader: OsmObjectDownloader
readonly perLayer: ReadonlyMap<string, GeoIndexedStoreForLayer>
readonly indexedFeatures: IndexedFeatureSource
@ -48,7 +48,7 @@ export default class MetaTagging {
}) {
this.state = state
const params = (this.params = MetaTagging.createExtraFuncParams(state))
for (const layer of state.layout.layers) {
for (const layer of state.theme.layers) {
if (layer.source === null) {
continue
}
@ -69,7 +69,7 @@ export default class MetaTagging {
features,
params,
layer,
state.layout,
state.theme,
state.osmObjectDownloader,
state.featureProperties
)
@ -115,7 +115,7 @@ export default class MetaTagging {
return
}
const state = this.state
const layer = state.layout.getMatchingLayer(feature.properties)
const layer = state.theme.getMatchingLayer(feature.properties)
if (!layer) {
return
}
@ -124,7 +124,7 @@ export default class MetaTagging {
[feature],
this.params,
layer,
state.layout,
state.theme,
state.osmObjectDownloader,
state.featureProperties,
{
@ -161,7 +161,7 @@ export default class MetaTagging {
features: Feature[],
params: ExtraFuncParams,
layer: LayerConfig,
layout: LayoutConfig,
theme: ThemeConfig,
osmObjectDownloader: OsmObjectDownloader,
featurePropertiesStores?: FeaturePropertiesStore,
options?: {
@ -190,7 +190,7 @@ export default class MetaTagging {
// The calculated functions - per layer - which add the new keys
// Calculated functions are defined by the layer
const layerFuncs = this.createRetaggingFunc(layer, ExtraFunctions.constructHelpers(params))
const state: MetataggingState = { layout, osmObjectDownloader }
const state: MetataggingState = { theme: theme, osmObjectDownloader }
let atLeastOneFeatureChanged = false
let strictlyEvaluated = 0
@ -424,7 +424,7 @@ export default class MetaTagging {
}
}
if (!window.location.pathname.endsWith("theme.html")) {
if (!Utils.runningFromConsole && !window.location.pathname.endsWith("theme.html")) {
console.warn(
"Static MetataggingObject for theme is not set; using `new Function` (aka `eval`) to get calculated tags. This might trip up the CSP"
)

View file

@ -7,7 +7,7 @@ import CreateWayWithPointReuseAction, { MergePointConfig } from "./CreateWayWith
import { And } from "../../Tags/And"
import { TagUtils } from "../../Tags/TagUtils"
import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/FeatureSource"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import { Position } from "geojson"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
@ -32,7 +32,7 @@ export default class CreateMultiPolygonWithPointReuseAction
outerRingCoordinates: Position[],
innerRingsCoordinates: Position[][],
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -43,7 +43,7 @@ export default class CreateMultiPolygonWithPointReuseAction
super(null, true)
this._tags = [...tags, new Tag("type", "multipolygon")]
this.changeType = changeType
this.theme = state?.layout?.id ?? ""
this.theme = state?.theme?.id ?? ""
this.createOuterWay = new CreateWayWithPointReuseAction(
[],
<[number, number][]>outerRingCoordinates,
@ -55,7 +55,7 @@ export default class CreateMultiPolygonWithPointReuseAction
new CreateNewWayAction(
[],
ringCoordinates.map(([lon, lat]) => ({ lat, lon })),
{ theme: state?.layout?.id }
{ theme: state?.theme?.id }
)
)

View file

@ -9,7 +9,7 @@ import { FeatureSource, IndexedFeatureSource } from "../../FeatureSource/Feature
import StaticFeatureSource from "../../FeatureSource/Sources/StaticFeatureSource"
import CreateNewNodeAction from "./CreateNewNodeAction"
import CreateNewWayAction from "./CreateNewWayAction"
import LayoutConfig from "../../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../../Models/ThemeConfig/ThemeConfig"
import FullNodeDatabaseSource from "../../FeatureSource/TiledFeatureSource/FullNodeDatabaseSource"
import { Position } from "geojson"
@ -69,7 +69,7 @@ export default class CreateWayWithPointReuseAction
*/
private readonly _coordinateInfo: CoordinateInfo[]
private readonly _state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -80,7 +80,7 @@ export default class CreateWayWithPointReuseAction
tags: Tag[],
coordinates: Position[],
state: {
layout: LayoutConfig
theme: ThemeConfig
changes: Changes
indexedFeatures: IndexedFeatureSource
fullNodeDatabase?: FullNodeDatabaseSource
@ -203,7 +203,7 @@ export default class CreateWayWithPointReuseAction
}
public async CreateChangeDescriptions(changes: Changes): Promise<ChangeDescription[]> {
const theme = this._state?.layout?.id
const theme = this._state?.theme?.id
const allChanges: ChangeDescription[] = []
const nodeIdsToUse: { lat: number; lon: number; nodeId?: number }[] = []
for (let i = 0; i < this._coordinateInfo.length; i++) {

View file

@ -217,7 +217,7 @@ export default class ReplaceGeometryAction extends OsmChangeAction implements Pr
const url = `${
this.state.osmConnection?._oauth_config?.url ?? "https://api.openstreetmap.org"
}/api/0.6/${this.wayToReplaceId}/full`
const rawData = await Utils.downloadJsonCached(url, 1000)
const rawData = await Utils.downloadJsonCached<{elements: any[]}>(url, 1000)
parsed = OsmObject.ParseObjects(rawData.elements)
}
const allNodes = parsed.filter((o) => o.type === "node")

View file

@ -4,7 +4,7 @@ import Constants from "../../Models/Constants"
import FilterConfig, { FilterConfigOption } from "../../Models/ThemeConfig/FilterConfig"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayerState from "../State/LayerState"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
export type FilterSearchResult = { option: FilterConfigOption, filter: FilterConfig, layer: LayerConfig, index: number }
@ -13,9 +13,9 @@ export type FilterSearchResult = { option: FilterConfigOption, filter: FilterCon
* Searches matching filters
*/
export default class FilterSearch {
private readonly _state: {layerState: LayerState, layout: LayoutConfig}
private readonly _state: {layerState: LayerState, theme: ThemeConfig}
constructor(state: {layerState: LayerState, layout: LayoutConfig}) {
constructor(state: {layerState: LayerState, theme: ThemeConfig}) {
this._state = state
}
@ -30,7 +30,7 @@ export default class FilterSearch {
return query
}).filter(q => q.length > 0)
const possibleFilters: FilterSearchResult[] = []
for (const layer of this._state.layout.layers) {
for (const layer of this._state.theme.layers) {
if (!Array.isArray(layer.filters)) {
continue
}

View file

@ -1,17 +1,17 @@
import SearchUtils from "./SearchUtils"
import ThemeSearch from "./ThemeSearch"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { Utils } from "../../Utils"
export default class LayerSearch {
private readonly _layout: LayoutConfig
private readonly _theme: ThemeConfig
private readonly _layerWhitelist: Set<string>
constructor(layout: LayoutConfig) {
this._layout = layout
this._layerWhitelist = new Set(layout.layers
constructor(theme: ThemeConfig) {
this._theme = theme
this._layerWhitelist = new Set(theme.layers
.filter(l => l.isNormal())
.map(l => l.id))
}
@ -29,8 +29,7 @@ export default class LayerSearch {
continue
}
const keywords = ThemeSearch.officialThemes.layers[id]
const distance = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords)))
result[id] = distance
result[id] = Math.min(...queryParts.map(q => SearchUtils.scoreKeywords(q, keywords)))
}
return result
}
@ -44,7 +43,7 @@ export default class LayerSearch {
const asList: ({ layer: LayerConfig, score: number })[] = []
for (const layer in scores) {
asList.push({
layer: this._layout.getLayer(layer),
layer: this._theme.getLayer(layer),
score: scores[layer],
})
}

View file

@ -1,4 +1,4 @@
import LayoutConfig, { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig, { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Store } from "../UIEventSource"
import UserRelatedState from "../State/UserRelatedState"
import { Utils } from "../../Utils"
@ -10,7 +10,7 @@ import { OsmConnection } from "../Osm/OsmConnection"
type ThemeSearchScore = {
theme: MinimalLayoutInformation,
theme: MinimalThemeInformation,
lowest: number,
perLayer?: Record<string, number>,
other: number,
@ -20,10 +20,10 @@ type ThemeSearchScore = {
export default class ThemeSearch {
public static readonly officialThemes: {
themes: MinimalLayoutInformation[],
themes: MinimalThemeInformation[],
layers: Record<string, Record<string, string[]>>
} = <any> themeOverview
public static readonly officialThemesById: Map<string, MinimalLayoutInformation> = new Map<string, MinimalLayoutInformation>()
public static readonly officialThemesById: Map<string, MinimalThemeInformation> = new Map<string, MinimalThemeInformation>()
static {
for (const th of ThemeSearch.officialThemes.themes ?? []) {
ThemeSearch.officialThemesById.set(th.id, th)
@ -33,17 +33,17 @@ export default class ThemeSearch {
private readonly _knownHiddenThemes: Store<Set<string>>
private readonly _layersToIgnore: string[]
private readonly _otherThemes: MinimalLayoutInformation[]
private readonly _otherThemes: MinimalThemeInformation[]
constructor(state: {osmConnection: OsmConnection, layout: LayoutConfig}) {
this._layersToIgnore = state.layout.layers.filter(l => l.isNormal()).map(l => l.id)
constructor(state: {osmConnection: OsmConnection, theme: ThemeConfig}) {
this._layersToIgnore = state.theme.layers.filter(l => l.isNormal()).map(l => l.id)
this._knownHiddenThemes = UserRelatedState.initDiscoveredHiddenThemes(state.osmConnection).map(list => new Set(list))
this._otherThemes = ThemeSearch.officialThemes.themes
.filter(th => th.id !== state.layout.id)
.filter(th => th.id !== state.theme.id)
}
public search(query: string, limit: number, threshold: number = 3): MinimalLayoutInformation[] {
public search(query: string, limit: number, threshold: number = 3): MinimalThemeInformation[] {
if (query.length < 1) {
return []
}
@ -101,7 +101,7 @@ export default class ThemeSearch {
* @param ignoreLayers
* @private
*/
private static scoreThemes(query: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = undefined): Record<string, ThemeSearchScore> {
private static scoreThemes(query: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = undefined): Record<string, ThemeSearchScore> {
if (query?.length < 1) {
return undefined
}
@ -147,13 +147,13 @@ export default class ThemeSearch {
return results
}
public static sortedByLowestScores(search: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = []): ThemeSearchScore[] {
public static sortedByLowestScores(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): ThemeSearchScore[] {
const scored = Object.values(this.scoreThemes(search, themes, ignoreLayers))
scored.sort((a, b) => a.lowest - b.lowest)
return scored
}
public static sortedByLowest(search: string, themes: MinimalLayoutInformation[], ignoreLayers: string[] = []): MinimalLayoutInformation[] {
public static sortedByLowest(search: string, themes: MinimalThemeInformation[], ignoreLayers: string[] = []): MinimalThemeInformation[] {
return this.sortedByLowestScores(search, themes, ignoreLayers)
.map(th => th.theme)
}

View file

@ -8,7 +8,7 @@ import { TagUtils } from "./Tags/TagUtils"
import { Feature, LineString } from "geojson"
import { OsmTags } from "../Models/OsmFeature"
import { UIEventSource } from "./UIEventSource"
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../Models/ThemeConfig/ThemeConfig"
import OsmObjectDownloader from "./Osm/OsmObjectDownloader"
import countryToCurrency from "country-to-currency"
@ -16,7 +16,7 @@ import countryToCurrency from "country-to-currency"
* All elements that are needed to perform metatagging
*/
export interface MetataggingState {
layout: LayoutConfig
theme: ThemeConfig
osmObjectDownloader: OsmObjectDownloader
}
@ -399,7 +399,7 @@ export default class SimpleMetaTaggers {
},
(feature, _, __, state) => {
const units = Utils.NoNull(
[].concat(...(state?.layout?.layers?.map((layer) => layer.units) ?? []))
[].concat(...(state?.theme?.layers?.map((layer) => layer.units) ?? []))
)
if (units.length == 0) {
return

View file

@ -1,7 +1,7 @@
/**
* The part of the global state which initializes the feature switches, based on default values and on the layoutToUse
* The part of the global state which initializes the feature switches, based on default values and on the theme
*/
import LayoutConfig from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig from "../../Models/ThemeConfig/ThemeConfig"
import { UIEventSource } from "../UIEventSource"
import { QueryParameters } from "../Web/QueryParameters"
import Constants from "../../Models/Constants"
@ -45,10 +45,6 @@ export class OsmConnectionFeatureSwitches {
}
export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
/**
* The layout that is being used in this run
*/
public readonly layoutToUse: LayoutConfig
public readonly featureSwitchEnableLogin: UIEventSource<boolean>
public readonly featureSwitchSearch: UIEventSource<boolean>
@ -74,9 +70,8 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
public readonly featureSwitchMorePrivacy: UIEventSource<boolean>
public readonly featureSwitchLayerDefault: UIEventSource<boolean>
public constructor(layoutToUse?: LayoutConfig) {
public constructor(theme?: ThemeConfig) {
super()
this.layoutToUse = layoutToUse
const legacyRewrite: Record<string, string | string[]> = {
"fs-userbadge": "fs-enable-login",
@ -102,7 +97,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchEnableLogin = FeatureSwitchUtils.initSwitch(
"fs-enable-login",
layoutToUse?.enableUserBadge ?? true,
theme?.enableUserBadge ?? true,
"Disables/Enables logging in and thus disables editing all together. This effectively puts MapComplete into read-only mode."
)
{
@ -117,18 +112,18 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchSearch = FeatureSwitchUtils.initSwitch(
"fs-search",
layoutToUse?.enableSearch ?? true,
theme?.enableSearch ?? true,
"Disables/Enables the search bar"
)
this.featureSwitchBackgroundSelection = FeatureSwitchUtils.initSwitch(
"fs-background",
layoutToUse?.enableBackgroundLayerSelection ?? true,
theme?.enableBackgroundLayerSelection ?? true,
"Disables/Enables the background layer control where a user can enable e.g. aerial imagery"
)
this.featureSwitchFilter = FeatureSwitchUtils.initSwitch(
"fs-filter",
layoutToUse?.enableLayers ?? true,
theme?.enableLayers ?? true,
"Disables/Enables the filter view where a user can enable/disable MapComplete-layers or filter for certain properties"
)
@ -149,17 +144,17 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
)
this.featureSwitchBackToThemeOverview = FeatureSwitchUtils.initSwitch(
"fs-homepage-link",
layoutToUse?.enableMoreQuests ?? true,
theme?.enableMoreQuests ?? true,
"Disables/Enables the various links which go back to the index page with the theme overview"
)
this.featureSwitchShareScreen = FeatureSwitchUtils.initSwitch(
"fs-share-screen",
layoutToUse?.enableShareScreen ?? true,
theme?.enableShareScreen ?? true,
"Disables/Enables the 'Share-screen'-tab in the welcome message"
)
this.featureSwitchGeolocation = FeatureSwitchUtils.initSwitch(
"fs-geolocation",
layoutToUse?.enableGeolocation ?? true,
theme?.enableGeolocation ?? true,
"Disables/Enables the geolocation button"
)
@ -170,19 +165,19 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
)
this.featureSwitchShowAllQuestions = FeatureSwitchUtils.initSwitch(
"fs-all-questions",
layoutToUse?.enableShowAllQuestions ?? false,
theme?.enableShowAllQuestions ?? false,
"Always show all questions"
)
this.featureSwitchEnableExport = FeatureSwitchUtils.initSwitch(
"fs-export",
layoutToUse?.enableExportButton ?? true,
theme?.enableExportButton ?? true,
"Enable the export as GeoJSON and CSV button"
)
this.featureSwitchCache = FeatureSwitchUtils.initSwitch(
"fs-cache",
layoutToUse?.enableCache ?? true,
theme?.enableCache ?? true,
"Enable/disable caching from localStorage"
)
@ -209,13 +204,13 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.featureSwitchMorePrivacy = QueryParameters.GetBooleanQueryParameter(
"moreprivacy",
layoutToUse.enableMorePrivacy,
theme.enableMorePrivacy,
"If true, the location distance indication will not be written to the changeset and other privacy enhancing measures might be taken."
)
this.overpassUrl = QueryParameters.GetQueryParameter(
"overpassUrl",
(layoutToUse?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),
(theme?.overpassUrl ?? Constants.defaultOverpassUrls).join(","),
"Point mapcomplete to a different overpass-instance. Example: https://overpass-api.de/api/interpreter"
).sync(
(param) => param?.split(","),
@ -226,7 +221,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassTimeout = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"overpassTimeout",
"" + layoutToUse?.overpassTimeout,
"" + theme?.overpassTimeout,
"Set a different timeout (in seconds) for queries in overpass"
)
)
@ -234,7 +229,7 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.overpassMaxZoom = UIEventSource.asFloat(
QueryParameters.GetQueryParameter(
"overpassMaxZoom",
"" + layoutToUse?.overpassMaxZoom,
"" + theme?.overpassMaxZoom,
" point to switch between OSM-api and overpass"
)
)
@ -242,14 +237,14 @@ export default class FeatureSwitchState extends OsmConnectionFeatureSwitches {
this.osmApiTileSize = UIEventSource.asInt(
QueryParameters.GetQueryParameter(
"osmApiTileSize",
"" + layoutToUse?.osmApiTileSize,
"" + theme?.osmApiTileSize,
"Tilesize when the OSM-API is used to fetch data within a BBOX"
)
)
this.backgroundLayerId = QueryParameters.GetQueryParameter(
"background",
layoutToUse?.defaultBackgroundId,
theme?.defaultBackgroundId,
[
"When set, load this raster layer (or a layer of this category) as background layer instead of using the default background. This is as if the user opened the background selection menu and selected the layer with the given id or category.",
"Most raster layers are based on the [editor layer index](https://github.com/osmlab/editor-layer-index)",

View file

@ -8,7 +8,7 @@ import ThemeSearch from "../Search/ThemeSearch"
import OpenStreetMapIdSearch from "../Search/OpenStreetMapIdSearch"
import PhotonSearch from "../Search/PhotonSearch"
import ThemeViewState from "../../Models/ThemeViewState"
import type { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import type { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { Translation } from "../../UI/i18n/Translation"
import GeocodingFeatureSource from "../Search/GeocodingFeatureSource"
import LayerSearch from "../Search/LayerSearch"
@ -23,7 +23,7 @@ export default class SearchState {
public readonly searchIsFocused = new UIEventSource(false)
public readonly suggestions: Store<SearchResult[]>
public readonly filterSuggestions: Store<FilterSearchResult[]>
public readonly themeSuggestions: Store<MinimalLayoutInformation[]>
public readonly themeSuggestions: Store<MinimalThemeInformation[]>
public readonly layerSuggestions: Store<LayerConfig[]>
public readonly locationSearchers: ReadonlyArray<GeocodingProvider>
@ -64,7 +64,7 @@ export default class SearchState {
const themeSearch = new ThemeSearch(state)
this.themeSuggestions = this.searchTerm.mapD(query => themeSearch.search(query, 3))
const layerSearch = new LayerSearch(state.layout)
const layerSearch = new LayerSearch(state.theme)
this.layerSuggestions = this.searchTerm.mapD(query => layerSearch.search(query, 5))
const filterSearch = new FilterSearch(state)

View file

@ -1,4 +1,4 @@
import LayoutConfig, { MinimalLayoutInformation } from "../../Models/ThemeConfig/LayoutConfig"
import ThemeConfig, { MinimalThemeInformation } from "../../Models/ThemeConfig/ThemeConfig"
import { OsmConnection } from "../Osm/OsmConnection"
import { MangroveIdentity } from "../Web/MangroveReviews"
import { Store, Stores, UIEventSource } from "../UIEventSource"
@ -183,7 +183,7 @@ export default class UserRelatedState {
constructor(
osmConnection: OsmConnection,
layout?: LayoutConfig,
layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState,
mapProperties?: MapProperties,
) {
@ -277,14 +277,14 @@ export default class UserRelatedState {
*
* @param themeInfo note that themeInfo.id should be the URL where it was found
*/
public addUnofficialTheme(themeInfo: MinimalLayoutInformation) {
public addUnofficialTheme(themeInfo: MinimalThemeInformation) {
const pref = this.osmConnection.getPreference("unofficial-theme-" + themeInfo.id)
this.osmConnection.isLoggedIn.when(
() => pref.set(JSON.stringify(themeInfo))
)
}
public getUnofficialTheme(id: string): MinimalLayoutInformation | undefined {
public getUnofficialTheme(id: string): MinimalThemeInformation | undefined {
const pref = this.osmConnection.getPreference("unofficial-theme-" + id)
const str = pref.data
@ -307,7 +307,7 @@ export default class UserRelatedState {
}
}
public markLayoutAsVisited(layout: LayoutConfig) {
public markLayoutAsVisited(layout: ThemeConfig) {
if (!layout) {
console.error("Trying to mark a layout as visited, but ", layout, " got passed")
return
@ -399,7 +399,7 @@ export default class UserRelatedState {
* This is inherently a dirty and chaotic method, as it shoves many properties into this EventSource
* */
private initAmendedPrefs(
layout?: LayoutConfig,
layout?: ThemeConfig,
featureSwitches?: FeatureSwitchState,
): UIEventSource<Record<string, string>> {
const amendedPrefs = new UIEventSource<Record<string, string>>({

View file

@ -30,13 +30,16 @@ export class LocalStorageSource {
return cached
}
let saved = defaultValue
try {
saved = localStorage.getItem(key)
if (saved === "undefined") {
saved = undefined
if (!Utils.runningFromConsole) {
try {
saved = localStorage.getItem(key)
if (saved === "undefined") {
saved = undefined
}
} catch (e) {
console.error("Could not get value", key, "from local storage")
}
} catch (e) {
console.error("Could not get value", key, "from local storage")
}
const source = new UIEventSource<string>(saved ?? defaultValue, "localstorage:" + key)