2023-06-18 00:44:57 +02:00
import { ConfigMeta } from "./configMeta"
2023-06-20 01:32:24 +02:00
import { Store , UIEventSource } from "../../Logic/UIEventSource"
import { LayerConfigJson } from "../../Models/ThemeConfig/Json/LayerConfigJson"
2025-06-04 00:21:28 +02:00
import {
Conversion ,
ConversionMessage ,
DesugaringContext ,
Pipe ,
} from "../../Models/ThemeConfig/Conversion/Conversion"
2023-10-12 16:55:26 +02:00
import { PrepareLayer } from "../../Models/ThemeConfig/Conversion/PrepareLayer"
2024-08-11 12:03:24 +02:00
import { PrevalidateTheme , ValidateLayer } from "../../Models/ThemeConfig/Conversion/Validation"
2023-10-12 16:55:26 +02:00
import { AllSharedLayers } from "../../Customizations/AllSharedLayers"
import { QuestionableTagRenderingConfigJson } from "../../Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson"
2023-10-13 18:46:56 +02:00
import { TagUtils } from "../../Logic/Tags/TagUtils"
import StudioServer from "./StudioServer"
2023-10-17 00:32:54 +02:00
import { Utils } from "../../Utils"
2023-10-24 22:01:10 +02:00
import { OsmConnection } from "../../Logic/Osm/OsmConnection"
2023-10-25 00:03:51 +02:00
import { OsmTags } from "../../Models/OsmFeature"
import { Feature , Point } from "geojson"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
2024-10-17 04:06:03 +02:00
import { ThemeConfigJson } from "../../Models/ThemeConfig/Json/ThemeConfigJson"
2023-10-26 13:58:45 +02:00
import { PrepareTheme } from "../../Models/ThemeConfig/Conversion/PrepareTheme"
2023-11-05 12:05:00 +01:00
import { ConversionContext } from "../../Models/ThemeConfig/Conversion/ConversionContext"
2023-11-07 18:51:50 +01:00
import { LocalStorageSource } from "../../Logic/Web/LocalStorageSource"
2024-01-19 17:31:35 +01:00
import { TagRenderingConfigJson } from "../../Models/ThemeConfig/Json/TagRenderingConfigJson"
2024-08-11 12:03:24 +02:00
import { ValidateTheme } from "../../Models/ThemeConfig/Conversion/ValidateTheme"
2025-05-04 02:46:18 +02:00
import * as questions from "../../../public/assets/generated/layers/questions.json"
2023-06-16 02:36:11 +02:00
2023-10-25 00:03:51 +02:00
export interface HighlightedTagRendering {
path : ReadonlyArray < string | number >
schema : ConfigMeta
}
2023-10-26 13:58:45 +02:00
export abstract class EditJsonState < T > {
2023-06-18 00:44:57 +02:00
public readonly schema : ConfigMeta [ ]
2023-10-26 13:58:45 +02:00
public readonly category : "layers" | "themes"
public readonly server : StudioServer
2024-08-02 19:06:14 +02:00
public readonly osmConnection : OsmConnection
2023-11-07 18:51:50 +01:00
public readonly showIntro : UIEventSource < "no" | "intro" | "tagrenderings" > = < any > (
2024-10-17 02:10:25 +02:00
LocalStorageSource . get ( "studio-show-intro" , "intro" )
2023-11-07 18:51:50 +01:00
)
2023-06-18 00:44:57 +02:00
2023-11-07 02:13:16 +01:00
public readonly expertMode : UIEventSource < boolean >
2023-10-26 13:58:45 +02:00
public readonly configuration : UIEventSource < Partial < T > > = new UIEventSource < Partial < T > > ( { } )
2023-10-12 16:55:26 +02:00
public readonly messages : Store < ConversionMessage [ ] >
2023-10-25 00:03:51 +02:00
2024-04-28 00:23:20 +02:00
/ * *
* The tab in the UI that is selected , used for deeplinks
* /
public readonly selectedTab : UIEventSource < number > = new UIEventSource < number > ( 0 )
2023-10-25 00:03:51 +02:00
/ * *
* The EditLayerUI shows a 'schemaBasedInput' for this path to pop advanced questions out
* /
public readonly highlightedItem : UIEventSource < HighlightedTagRendering > = new UIEventSource (
2024-08-14 13:53:56 +02:00
undefined
2023-10-25 00:03:51 +02:00
)
2023-11-05 12:05:00 +01:00
private sendingUpdates = false
2023-10-24 22:01:10 +02:00
private readonly _stores = new Map < string , UIEventSource < any > > ( )
2023-06-20 01:32:24 +02:00
2023-11-07 02:13:16 +01:00
constructor (
schema : ConfigMeta [ ] ,
server : StudioServer ,
category : "layers" | "themes" ,
2024-08-02 19:06:14 +02:00
osmConnection : OsmConnection ,
2023-11-07 02:13:16 +01:00
options ? : {
expertMode? : UIEventSource < boolean >
2024-08-14 13:53:56 +02:00
}
2023-11-07 02:13:16 +01:00
) {
2024-08-02 19:06:14 +02:00
this . osmConnection = osmConnection
2023-06-18 00:44:57 +02:00
this . schema = schema
2023-10-13 18:46:56 +02:00
this . server = server
2023-10-26 13:58:45 +02:00
this . category = category
2023-11-07 02:13:16 +01:00
this . expertMode = options ? . expertMode ? ? new UIEventSource < boolean > ( false )
2023-10-20 19:04:55 +02:00
2023-10-26 13:58:45 +02:00
const layerId = this . getId ( )
this . configuration
2023-11-02 04:35:32 +01:00
. mapD ( ( config ) = > {
if ( ! this . sendingUpdates ) {
console . log ( "Not sending updates yet! Trigger 'startSendingUpdates' first" )
return undefined
}
return JSON . stringify ( config , null , " " )
} )
2023-10-26 13:58:45 +02:00
. stabilized ( 100 )
. addCallbackD ( async ( config ) = > {
const id = layerId . data
if ( id === undefined ) {
console . warn ( "No id found in layer, not updating" )
return
2023-10-25 00:03:51 +02:00
}
2023-11-02 04:35:32 +01:00
await this . server . update ( id , config , this . category )
2023-10-26 13:58:45 +02:00
} )
2024-04-23 15:35:18 +02:00
this . messages = this . createMessagesStore ( )
2023-06-20 01:32:24 +02:00
}
2023-11-02 04:35:32 +01:00
public startSavingUpdates ( enabled = true ) {
this . sendingUpdates = enabled
2024-09-25 11:40:45 +02:00
if ( ! this . server . isDirect ) {
this . register (
[ "credits" ] ,
this . osmConnection . userDetails . mapD ( ( u ) = > u . name ) ,
false
)
this . register (
[ "credits:uid" ] ,
this . osmConnection . userDetails . mapD ( ( u ) = > u . uid ) ,
false
)
}
2023-11-02 04:35:32 +01:00
if ( enabled ) {
this . configuration . ping ( )
}
}
2023-06-23 16:14:43 +02:00
public getCurrentValueFor ( path : ReadonlyArray < string | number > ) : any | undefined {
// Walk the path down to see if we find something
let entry = this . configuration . data
for ( let i = 0 ; i < path . length ; i ++ ) {
2024-05-14 19:01:05 +02:00
if ( entry === undefined || entry === null ) {
2023-06-23 16:14:43 +02:00
// We reached a dead end - no old vlaue
return undefined
2023-06-20 01:32:24 +02:00
}
2023-06-23 16:14:43 +02:00
const breadcrumb = path [ i ]
entry = entry [ breadcrumb ]
}
return entry
}
2023-12-19 22:08:00 +01:00
public async delete ( ) {
2023-12-02 00:24:55 +01:00
await this . server . delete ( this . getId ( ) . data , this . category )
}
2024-01-19 17:31:35 +01:00
2023-10-17 00:32:54 +02:00
public getStoreFor < T > ( path : ReadonlyArray < string | number > ) : UIEventSource < T | undefined > {
const key = path . join ( "." )
2023-08-23 11:11:53 +02:00
const store = new UIEventSource < any > ( this . getCurrentValueFor ( path ) )
store . addCallback ( ( v ) = > {
this . setValueAt ( path , v )
} )
2023-10-17 00:32:54 +02:00
this . _stores . set ( key , store )
2024-01-24 23:45:20 +01:00
this . configuration . addCallbackD ( ( ) = > {
2023-11-05 12:05:00 +01:00
store . setData ( this . getCurrentValueFor ( path ) )
} )
2023-08-23 11:11:53 +02:00
return store
}
2023-06-23 16:14:43 +02:00
public register (
path : ReadonlyArray < string | number > ,
value : Store < any > ,
2024-08-14 13:53:56 +02:00
noInitialSync : boolean = true
2023-06-23 16:14:43 +02:00
) : ( ) = > void {
2023-10-24 22:01:10 +02:00
const unsync = value . addCallback ( ( v ) = > {
this . setValueAt ( path , v )
} )
2023-06-23 16:14:43 +02:00
if ( ! noInitialSync ) {
2023-06-23 17:28:44 +02:00
this . setValueAt ( path , value . data )
2023-06-23 16:14:43 +02:00
}
return unsync
2023-06-16 02:36:11 +02:00
}
2023-06-18 00:44:57 +02:00
public getSchemaStartingWith ( path : string [ ] ) {
2024-09-25 11:40:45 +02:00
if ( path === undefined ) {
2024-09-14 22:43:09 +02:00
return undefined
}
2023-06-18 00:44:57 +02:00
return this . schema . filter (
( sch ) = >
2024-08-14 13:53:56 +02:00
! path . some ( ( part , i ) = > ! ( sch . path . length > path . length && sch . path [ i ] === part ) )
2023-06-18 00:44:57 +02:00
)
}
2023-06-21 17:13:09 +02:00
2023-06-30 13:36:02 +02:00
public getTranslationAt ( path : string [ ] ) : ConfigMeta {
const origConfig = this . getSchema ( path ) [ 0 ]
return {
path ,
type : "translation" ,
hints : {
2024-10-19 14:44:55 +02:00
typehint : "translation" ,
2023-06-30 13:36:02 +02:00
} ,
required : origConfig.required ? ? false ,
2024-10-19 14:44:55 +02:00
description : origConfig.description ? ? "A translatable object" ,
2023-06-30 13:36:02 +02:00
}
}
2023-09-15 01:16:33 +02:00
2024-08-11 19:04:44 +02:00
public getSchema ( path : ( string | number ) [ ] ) : ConfigMeta [ ] {
2024-08-14 13:53:56 +02:00
path = path . filter ( ( p ) = > typeof p === "string" )
2023-09-15 01:16:33 +02:00
const schemas = this . schema . filter (
2023-06-21 17:13:09 +02:00
( sch ) = >
2023-06-23 16:14:43 +02:00
sch !== undefined &&
2024-08-14 13:53:56 +02:00
! path . some ( ( part , i ) = > ! ( sch . path . length == path . length && sch . path [ i ] === part ) )
2023-06-21 17:13:09 +02:00
)
2023-09-15 01:16:33 +02:00
if ( schemas . length == 0 ) {
console . warn ( "No schemas found for path" , path . join ( "." ) )
}
return schemas
2023-06-21 17:13:09 +02:00
}
2023-06-23 16:14:43 +02:00
2023-06-23 17:28:44 +02:00
public setValueAt ( path : ReadonlyArray < string | number > , v : any ) {
2023-10-06 23:56:50 +02:00
let entry = this . configuration . data
2023-10-17 00:32:54 +02:00
const isUndefined =
2023-10-17 01:36:22 +02:00
v === undefined ||
v === null ||
v === "" ||
( typeof v === "object" && Object . keys ( v ) . length === 0 )
2023-10-17 00:32:54 +02:00
2023-10-06 23:56:50 +02:00
for ( let i = 0 ; i < path . length - 1 ; i ++ ) {
const breadcrumb = path [ i ]
2024-09-24 13:13:08 +02:00
if ( entry [ breadcrumb ] === undefined || entry [ breadcrumb ] === null ) {
2023-10-17 01:36:22 +02:00
if ( isUndefined ) {
// we have a dead end _and_ we do not need to set a value - we do an early return
return
}
2023-10-06 23:56:50 +02:00
entry [ breadcrumb ] = typeof path [ i + 1 ] === "number" ? [ ] : { }
2023-06-23 16:14:43 +02:00
}
2023-10-06 23:56:50 +02:00
entry = entry [ breadcrumb ]
}
2023-10-24 22:01:10 +02:00
2023-10-17 01:36:22 +02:00
const lastBreadcrumb = path . at ( - 1 )
2023-10-17 00:32:54 +02:00
if ( isUndefined ) {
2023-10-17 01:36:22 +02:00
if ( entry && entry [ lastBreadcrumb ] ) {
delete entry [ lastBreadcrumb ]
2023-10-24 22:01:10 +02:00
this . configuration . ping ( )
2023-10-17 01:36:22 +02:00
}
2023-10-24 22:01:10 +02:00
} else if ( entry [ lastBreadcrumb ] !== v ) {
2023-10-17 01:36:22 +02:00
entry [ lastBreadcrumb ] = v
2023-10-24 22:01:10 +02:00
this . configuration . ping ( )
2023-06-23 16:14:43 +02:00
}
2023-10-24 22:01:10 +02:00
}
public messagesFor ( path : ReadonlyArray < string | number > ) : Store < ConversionMessage [ ] > {
return this . messages . map ( ( msgs ) = > {
if ( ! msgs ) {
return [ ]
}
return msgs . filter ( ( msg ) = > {
2024-01-22 01:01:38 +01:00
if ( msg . level === "debug" || msg . level === "information" ) {
return false
}
2023-10-24 22:01:10 +02:00
const pth = msg . context . path
for ( let i = 0 ; i < Math . min ( pth . length , path . length ) ; i ++ ) {
if ( pth [ i ] !== path [ i ] ) {
return false
}
}
return true
} )
} )
2023-06-23 16:14:43 +02:00
}
2023-10-26 13:58:45 +02:00
protected abstract buildValidation ( state : DesugaringContext ) : Conversion < T , any >
protected abstract getId ( ) : Store < string >
2024-06-16 16:06:26 +02:00
protected abstract validate ( configuration : Partial < T > ) : Promise < ConversionMessage [ ] >
2024-04-23 15:35:18 +02:00
/ * *
* Creates a store that validates the configuration and which contains all relevant ( error ) - messages
* @private
* /
private createMessagesStore ( ) : Store < ConversionMessage [ ] > {
2024-06-16 16:06:26 +02:00
return this . configuration
. mapAsyncD ( async ( config ) = > {
if ( ! this . validate ) {
return [ ]
}
return await this . validate ( config )
} )
. map ( ( messages ) = > messages ? ? [ ] )
2023-10-26 13:58:45 +02:00
}
}
2024-01-19 17:31:35 +01:00
class ContextRewritingStep < T > extends Conversion < LayerConfigJson , T > {
private readonly _step : Conversion < LayerConfigJson , T >
private readonly _state : DesugaringContext
private readonly _getTagRenderings : ( t : T ) = > TagRenderingConfigJson [ ]
constructor (
state : DesugaringContext ,
step : Conversion < LayerConfigJson , T > ,
2024-08-14 13:53:56 +02:00
getTagRenderings : ( t : T ) = > TagRenderingConfigJson [ ]
2024-01-19 17:31:35 +01:00
) {
2025-06-04 00:21:28 +02:00
super (
"ContextRewritingStep" ,
"When validating a layer, the tagRenderings are first expanded. Some builtin tagRendering-calls (e.g. `contact`) will introduce _multiple_ tagRenderings, causing the count to be off. This class rewrites the error messages to fix this" ,
[ ]
)
2024-01-19 17:31:35 +01:00
this . _state = state
this . _step = step
this . _getTagRenderings = getTagRenderings
}
convert ( json : LayerConfigJson , context : ConversionContext ) : T {
const converted = this . _step . convert ( json , context )
const originalIds = json . tagRenderings ? . map (
2024-08-14 13:53:56 +02:00
( tr ) = > ( < QuestionableTagRenderingConfigJson > tr ) [ "id" ]
2024-01-19 17:31:35 +01:00
)
if ( ! originalIds ) {
return converted
}
let newTagRenderings : TagRenderingConfigJson [ ]
if ( converted === undefined ) {
const prepared = new PrepareLayer ( this . _state )
newTagRenderings = < TagRenderingConfigJson [ ] > (
prepared . convert ( json , context ) . tagRenderings
)
} else {
newTagRenderings = this . _getTagRenderings ( converted )
}
context . rewriteMessages ( ( path ) = > {
if ( path [ 0 ] !== "tagRenderings" ) {
return undefined
}
const newPath = [ . . . path ]
2024-07-26 18:13:26 +02:00
const idToSearch = newTagRenderings [ newPath [ 1 ] ] ? . id ? ? ""
2024-01-19 17:31:35 +01:00
const oldIndex = originalIds . indexOf ( idToSearch )
if ( oldIndex < 0 ) {
console . warn ( "Original ID was not found: " , idToSearch )
return undefined // We don't modify the message
}
newPath [ 1 ] = oldIndex
return newPath
} )
return converted
}
}
2023-10-26 13:58:45 +02:00
export default class EditLayerState extends EditJsonState < LayerConfigJson > {
// Needed for the special visualisations
public readonly imageUploadManager = {
getCountsFor() {
return 0
2024-10-19 14:44:55 +02:00
} ,
2023-10-26 13:58:45 +02:00
}
2024-10-17 04:06:03 +02:00
public readonly theme : { getMatchingLayer : ( key : any ) = > LayerConfig }
2023-10-26 13:58:45 +02:00
public readonly featureSwitches : {
featureSwitchIsDebugging : UIEventSource < boolean >
}
/ * *
* Used to preview and interact with the questions
* /
public readonly testTags = new UIEventSource < OsmTags > ( { id : "node/-12345" } )
public readonly exampleFeature : Feature < Point > = {
type : "Feature" ,
properties : this.testTags.data ,
geometry : {
type : "Point" ,
2024-10-19 14:44:55 +02:00
coordinates : [ 3.21 , 51.2 ] ,
} ,
2023-10-26 13:58:45 +02:00
}
2023-11-07 02:13:16 +01:00
constructor (
schema : ConfigMeta [ ] ,
server : StudioServer ,
osmConnection : OsmConnection ,
2024-08-14 13:53:56 +02:00
options : { expertMode : UIEventSource < boolean > }
2023-11-07 02:13:16 +01:00
) {
2024-08-02 19:06:14 +02:00
super ( schema , server , "layers" , osmConnection , options )
2024-10-17 04:06:03 +02:00
this . theme = {
2024-04-23 15:35:18 +02:00
getMatchingLayer : ( ) = > {
2023-10-26 13:58:45 +02:00
try {
return new LayerConfig ( < LayerConfigJson > this . configuration . data , "dynamic" )
} catch ( e ) {
return undefined
}
2024-10-19 14:44:55 +02:00
} ,
2023-10-26 13:58:45 +02:00
}
this . featureSwitches = {
2024-10-19 14:44:55 +02:00
featureSwitchIsDebugging : new UIEventSource < boolean > ( true ) ,
2023-10-26 13:58:45 +02:00
}
this . addMissingTagRenderingIds ( )
2023-11-02 04:35:32 +01:00
2023-12-19 22:08:00 +01:00
function cleanArray ( data : object , key : string ) : boolean {
if ( ! data ) {
2023-12-03 03:33:02 +01:00
return false
}
2023-12-03 03:01:43 +01:00
if ( data [ key ] ) {
2023-11-02 04:35:32 +01:00
// A bit of cleanup
2023-12-03 03:01:43 +01:00
const lBefore = data [ key ] . length
const cleaned = Utils . NoNull ( data [ key ] )
2023-11-02 04:35:32 +01:00
if ( cleaned . length != lBefore ) {
2023-12-03 03:01:43 +01:00
data [ key ] = cleaned
return true
2023-11-02 04:35:32 +01:00
}
}
2023-12-03 03:01:43 +01:00
return false
}
this . configuration . addCallbackAndRunD ( ( layer ) = > {
2023-12-03 03:33:02 +01:00
let changed = cleanArray ( layer , "tagRenderings" ) || cleanArray ( layer , "pointRenderings" )
for ( const tr of layer . tagRenderings ? ? [ ] ) {
2023-12-19 22:08:00 +01:00
if ( typeof tr === "string" ) {
2023-12-03 03:33:02 +01:00
continue
}
2023-12-19 22:08:00 +01:00
const qtr = < QuestionableTagRenderingConfigJson > tr
if ( qtr . freeform && Object . keys ( qtr . freeform ) . length === 0 ) {
2023-12-03 03:33:02 +01:00
delete qtr . freeform
changed = true
}
}
2023-12-19 22:08:00 +01:00
if ( changed ) {
2023-12-03 03:01:43 +01:00
this . configuration . ping ( )
}
2023-11-02 04:35:32 +01:00
} )
2023-10-26 13:58:45 +02:00
}
protected buildValidation ( state : DesugaringContext ) {
2024-01-19 17:31:35 +01:00
return new ContextRewritingStep (
state ,
new Pipe ( new PrepareLayer ( state ) , new ValidateLayer ( "dynamic" , false , undefined , true ) ) ,
2024-08-14 13:53:56 +02:00
( t ) = > < TagRenderingConfigJson [ ] > t . raw . tagRenderings
2023-10-26 13:58:45 +02:00
)
}
protected getId ( ) : Store < string > {
return this . configuration . mapD ( ( config ) = > config . id )
}
private addMissingTagRenderingIds() {
this . configuration . addCallbackD ( ( config ) = > {
const trs = Utils . NoNull ( config . tagRenderings ? ? [ ] )
for ( let i = 0 ; i < trs . length ; i ++ ) {
const tr = trs [ i ]
if ( typeof tr === "string" ) {
continue
}
if ( ! tr [ "id" ] && ! tr [ "override" ] ) {
const qtr = < QuestionableTagRenderingConfigJson > tr
2023-11-05 12:05:00 +01:00
let id = "" + i + "_" + Utils . randomString ( 5 )
2023-10-26 13:58:45 +02:00
if ( qtr ? . freeform ? . key ) {
id = qtr ? . freeform ? . key
} else if ( qtr . mappings ? . [ 0 ] ? . if ) {
id =
qtr . freeform ? . key ? ?
TagUtils . Tag ( qtr . mappings [ 0 ] . if ) . usedKeys ( ) ? . [ 0 ] ? ?
"" + i
}
qtr [ "id" ] = id
}
}
} )
}
2024-04-23 15:35:18 +02:00
2024-06-16 16:06:26 +02:00
protected async validate (
2024-08-14 13:53:56 +02:00
configuration : Partial < LayerConfigJson >
2024-06-16 16:06:26 +02:00
) : Promise < ConversionMessage [ ] > {
2024-04-23 15:35:18 +02:00
const layers = AllSharedLayers . getSharedLayersConfigs ( )
2025-05-03 23:44:08 +02:00
const questionsLayer = questions
2024-04-23 15:35:18 +02:00
const sharedQuestions = new Map < string , QuestionableTagRenderingConfigJson > ( )
2025-05-03 23:44:08 +02:00
for ( const question of questionsLayer . tagRenderings ) {
2025-05-03 23:48:35 +02:00
sharedQuestions . set (
question [ "id" ] ,
< QuestionableTagRenderingConfigJson > ( < unknown > question )
)
2024-04-23 15:35:18 +02:00
}
const state : DesugaringContext = {
tagRenderings : sharedQuestions ,
2024-10-17 04:06:03 +02:00
sharedLayers : layers ,
2024-10-19 14:44:55 +02:00
tagRenderingOrder : [ ] ,
2024-04-23 15:35:18 +02:00
}
const prepare = this . buildValidation ( state )
const context = ConversionContext . construct ( [ ] , [ "prepare" ] )
try {
prepare . convert ( < LayerConfigJson > configuration , context )
} catch ( e ) {
console . error ( e )
context . err ( e )
}
return context . messages
}
2023-10-26 13:58:45 +02:00
}
2024-10-17 04:06:03 +02:00
export class EditThemeState extends EditJsonState < ThemeConfigJson > {
2023-11-07 02:13:16 +01:00
constructor (
schema : ConfigMeta [ ] ,
server : StudioServer ,
2024-08-02 19:06:14 +02:00
osmConnection : OsmConnection ,
2024-08-14 13:53:56 +02:00
options : { expertMode : UIEventSource < boolean > }
2023-11-07 02:13:16 +01:00
) {
2024-08-02 19:06:14 +02:00
super ( schema , server , "themes" , osmConnection , options )
2024-04-23 15:35:18 +02:00
this . setupFixers ( )
2023-11-02 04:35:32 +01:00
}
2024-10-17 04:06:03 +02:00
protected buildValidation ( state : DesugaringContext ) : Conversion < ThemeConfigJson , any > {
2024-08-14 13:53:56 +02:00
return new Pipe (
new PrevalidateTheme ( ) ,
2024-08-02 19:06:14 +02:00
new Pipe (
new PrepareTheme ( state ) ,
2024-08-14 13:53:56 +02:00
new ValidateTheme ( undefined , "" , false , new Set ( state . tagRenderings . keys ( ) ) )
) ,
true
2023-10-26 13:58:45 +02:00
)
}
protected getId ( ) : Store < string > {
return this . configuration . mapD ( ( config ) = > config . id )
}
2024-04-23 15:35:18 +02:00
/ * * A p p l i e s a f e w b a n d a i d s t o g e t e v e r y t h i n g s m o o t h e d o u t i n c a s e o f e r r o r s ; a b i g b u n c h o f h a c k s b a s i c a l l y
* /
public setupFixers() {
2024-06-16 16:06:26 +02:00
this . configuration . addCallbackAndRunD ( ( config ) = > {
2024-04-23 15:35:18 +02:00
if ( config . layers ) {
// Remove 'null' and 'undefined' values from the layer array if any are found
for ( let i = config . layers . length ; i >= 0 ; i -- ) {
if ( ! config . layers [ i ] ) {
config . layers . splice ( i , 1 )
}
}
}
} )
}
2024-10-17 04:06:03 +02:00
protected async validate ( configuration : Partial < ThemeConfigJson > ) {
2024-04-23 15:35:18 +02:00
const layers = AllSharedLayers . getSharedLayersConfigs ( )
for ( const l of configuration . layers ? ? [ ] ) {
2024-06-16 16:06:26 +02:00
if ( typeof l !== "string" ) {
2024-04-23 15:35:18 +02:00
continue
}
if ( ! l . startsWith ( "https://" ) ) {
continue
}
2024-06-16 16:06:26 +02:00
const config = < LayerConfigJson > await Utils . downloadJsonCached ( l , 1000 * 60 * 10 )
2024-04-23 15:35:18 +02:00
layers . set ( l , config )
}
const sharedQuestions = new Map < string , QuestionableTagRenderingConfigJson > ( )
for ( const question of questions . tagRenderings ) {
2025-05-03 23:48:35 +02:00
sharedQuestions . set (
question [ "id" ] ,
< QuestionableTagRenderingConfigJson > ( < unknown > question )
)
2024-04-23 15:35:18 +02:00
}
const state : DesugaringContext = {
tagRenderings : sharedQuestions ,
2024-10-17 04:06:03 +02:00
sharedLayers : layers ,
2024-10-19 14:44:55 +02:00
tagRenderingOrder : [ ] ,
2024-04-23 15:35:18 +02:00
}
const prepare = this . buildValidation ( state )
const context = ConversionContext . construct ( [ ] , [ "prepare" ] )
2024-06-16 16:06:26 +02:00
if ( configuration . layers ) {
2024-04-27 23:43:39 +02:00
Utils . NoNullInplace ( configuration . layers )
}
2024-04-23 15:35:18 +02:00
try {
2024-10-17 04:06:03 +02:00
prepare . convert ( < ThemeConfigJson > configuration , context )
2024-04-23 15:35:18 +02:00
} catch ( e ) {
console . error ( e )
context . err ( e )
}
return context . messages
}
2023-06-16 02:36:11 +02:00
}