2021-10-15 05:20:02 +02:00
import LayoutConfig from "../Models/ThemeConfig/LayoutConfig" ;
import { QueryParameters } from "./Web/QueryParameters" ;
import { AllKnownLayouts } from "../Customizations/AllKnownLayouts" ;
import { FixedUiElement } from "../UI/Base/FixedUiElement" ;
import { Utils } from "../Utils" ;
import Combine from "../UI/Base/Combine" ;
import { SubtleButton } from "../UI/Base/SubtleButton" ;
import BaseUIElement from "../UI/BaseUIElement" ;
import { UIEventSource } from "./UIEventSource" ;
import { LocalStorageSource } from "./Web/LocalStorageSource" ;
import LZString from "lz-string" ;
import * as personal from "../assets/themes/personal/personal.json" ;
2022-01-14 03:14:56 +01:00
import { FixLegacyTheme , PrepareTheme } from "../Models/ThemeConfig/Conversion/LegacyJsonConvert" ;
2021-12-21 18:35:31 +01:00
import { LayerConfigJson } from "../Models/ThemeConfig/Json/LayerConfigJson" ;
import SharedTagRenderings from "../Customizations/SharedTagRenderings" ;
2021-12-21 19:09:24 +01:00
import * as known_layers from "../assets/generated/known_layers.json"
2021-12-21 20:57:25 +01:00
import { LayoutConfigJson } from "../Models/ThemeConfig/Json/LayoutConfigJson" ;
2021-10-15 05:20:02 +02:00
export default class DetermineLayout {
/ * *
* Gets the correct layout for this website
* /
2021-12-21 19:56:04 +01:00
public static async GetLayout ( ) : Promise < LayoutConfig > {
2021-10-15 05:20:02 +02:00
const loadCustomThemeParam = QueryParameters . GetQueryParameter ( "userlayout" , "false" , "If not 'false', a custom (non-official) theme is loaded. This custom layout can be done in multiple ways: \n\n- The hash of the URL contains a base64-encoded .json-file containing the theme definition\n- The hash of the URL contains a lz-compressed .json-file, as generated by the custom theme generator\n- The parameter itself is an URL, in which case that URL will be downloaded. It should point to a .json of a theme" )
const layoutFromBase64 = decodeURIComponent ( loadCustomThemeParam . data ) ;
if ( layoutFromBase64 . startsWith ( "http" ) ) {
2021-12-21 19:56:04 +01:00
return await DetermineLayout . LoadRemoteTheme ( layoutFromBase64 )
2021-10-15 05:20:02 +02:00
}
if ( layoutFromBase64 !== "false" ) {
// We have to load something from the hash (or from disk)
2021-12-21 19:56:04 +01:00
return DetermineLayout . LoadLayoutFromHash ( loadCustomThemeParam )
2021-10-15 05:20:02 +02:00
}
let layoutId : string = undefined
if ( location . href . indexOf ( "buurtnatuur.be" ) >= 0 ) {
layoutId = "buurtnatuur"
}
const path = window . location . pathname . split ( "/" ) . slice ( - 1 ) [ 0 ] ;
2021-12-21 18:35:31 +01:00
if ( path !== "theme.html" && path !== "" ) {
2021-10-15 05:20:02 +02:00
layoutId = path ;
if ( path . endsWith ( ".html" ) ) {
layoutId = path . substr ( 0 , path . length - 5 ) ;
}
console . log ( "Using layout" , layoutId ) ;
}
layoutId = QueryParameters . GetQueryParameter ( "layout" , layoutId , "The layout to load into MapComplete" ) . data ;
const layoutToUse : LayoutConfig = AllKnownLayouts . allKnownLayouts . get ( layoutId ? . toLowerCase ( ) ) ;
if ( layoutToUse ? . id === personal . id ) {
layoutToUse . layers = AllKnownLayouts . AllPublicLayers ( )
for ( const layer of layoutToUse . layers ) {
layer . minzoomVisible = Math . max ( layer . minzoomVisible , layer . minzoom )
layer . minzoom = Math . max ( 16 , layer . minzoom )
}
}
2021-12-21 19:56:04 +01:00
return layoutToUse
2021-10-15 05:20:02 +02:00
}
2021-12-21 20:57:25 +01:00
private static prepCustomTheme ( json : any ) : LayoutConfigJson {
const knownLayersDict = new Map < string , LayerConfigJson > ( )
for ( const key in known_layers [ "default" ] ) {
const layer = known_layers [ "default" ] [ key ]
knownLayersDict . set ( layer . id , layer )
}
const converState = {
tagRenderings : SharedTagRenderings.SharedTagRenderingJson ,
sharedLayers : knownLayersDict
}
json = new FixLegacyTheme ( ) . convertStrict ( converState , json , "While loading a dynamic theme" )
json = new PrepareTheme ( ) . convertStrict ( converState , json , "While preparing a dynamic theme" )
console . log ( "The layoutconfig is " , json )
return json
}
2021-10-15 05:20:02 +02:00
public static LoadLayoutFromHash (
userLayoutParam : UIEventSource < string >
2021-12-21 19:56:04 +01:00
) : LayoutConfig | null {
2021-10-15 05:20:02 +02:00
let hash = location . hash . substr ( 1 ) ;
try {
// layoutFromBase64 contains the name of the theme. This is partly to do tracking with goat counter
const dedicatedHashFromLocalStorage = LocalStorageSource . Get (
2021-10-27 19:57:15 +02:00
"user-layout-" + userLayoutParam . data ? . replace ( " " , "_" )
2021-10-15 05:20:02 +02:00
) ;
if ( dedicatedHashFromLocalStorage . data ? . length < 10 ) {
dedicatedHashFromLocalStorage . setData ( undefined ) ;
}
const hashFromLocalStorage = LocalStorageSource . Get (
"last-loaded-user-layout"
) ;
if ( hash . length < 10 ) {
hash =
dedicatedHashFromLocalStorage . data ? ?
hashFromLocalStorage . data ;
} else {
console . log ( "Saving hash to local storage" ) ;
hashFromLocalStorage . setData ( hash ) ;
dedicatedHashFromLocalStorage . setData ( hash ) ;
}
let json : any ;
try {
json = JSON . parse ( atob ( hash ) ) ;
} catch ( e ) {
// We try to decode with lz-string
try {
json = JSON . parse ( Utils . UnMinify ( LZString . decompressFromBase64 ( hash ) ) )
} catch ( e ) {
2021-10-27 19:57:15 +02:00
console . error ( e )
2021-10-15 05:20:02 +02:00
DetermineLayout . ShowErrorOnCustomTheme ( "Could not decode the hash" , new FixedUiElement ( "Not a valid (LZ-compressed) JSON" ) )
return null ;
}
}
2021-12-21 20:57:25 +01:00
const layoutToUse = DetermineLayout . prepCustomTheme ( json )
2021-10-15 05:20:02 +02:00
userLayoutParam . setData ( layoutToUse . id ) ;
2021-12-21 20:57:25 +01:00
return new LayoutConfig ( layoutToUse , false ) ;
2021-10-15 05:20:02 +02:00
} catch ( e ) {
2021-10-27 19:57:15 +02:00
console . error ( e )
2021-10-15 05:20:02 +02:00
if ( hash === undefined || hash . length < 10 ) {
DetermineLayout . ShowErrorOnCustomTheme ( "Could not load a theme from the hash" , new FixedUiElement ( "Hash does not contain data" ) )
}
2021-11-14 16:13:09 +01:00
this . ShowErrorOnCustomTheme ( "Could not parse the hash" , new FixedUiElement ( e ) )
2021-10-15 05:20:02 +02:00
return null ;
}
}
public static ShowErrorOnCustomTheme (
intro : string = "Error: could not parse the custom layout:" ,
error : BaseUIElement ) {
new Combine ( [
intro ,
error . SetClass ( "alert" ) ,
new SubtleButton ( "./assets/svg/mapcomplete_logo.svg" ,
"Go back to the theme overview" ,
{ url : window.location.protocol + "//" + window . location . hostname + "/index.html" , newTab : false } )
] )
. SetClass ( "flex flex-col clickable" )
. AttachTo ( "centermessage" ) ;
}
2021-11-07 16:34:51 +01:00
private static async LoadRemoteTheme ( link : string ) : Promise < LayoutConfig | null > {
console . log ( "Downloading map theme from " , link ) ;
new FixedUiElement ( ` Downloading the theme from the <a href=" ${ link } ">link</a>... ` )
. AttachTo ( "centermessage" ) ;
try {
2021-12-21 18:35:31 +01:00
let parsed = await Utils . downloadJson ( link )
2021-11-07 16:34:51 +01:00
console . log ( "Got " , parsed )
2021-12-21 18:35:31 +01:00
parsed = new FixLegacyTheme ( ) . convertStrict ( {
tagRenderings : SharedTagRenderings.SharedTagRenderingJson ,
sharedLayers : new Map < string , LayerConfigJson > ( ) // FIXME: actually add the layers
} , parsed , "While loading a dynamic theme" )
2021-12-21 20:57:25 +01:00
2021-11-07 16:34:51 +01:00
parsed . id = link ;
2021-12-21 20:57:25 +01:00
try {
const layoutToUse = DetermineLayout . prepCustomTheme ( parsed )
return new LayoutConfig ( layoutToUse , false ) . patchImages ( link , JSON . stringify ( layoutToUse ) ) ;
2021-11-07 16:34:51 +01:00
} catch ( e ) {
console . error ( e )
DetermineLayout . ShowErrorOnCustomTheme (
` <a href=" ${ link } "> ${ link } </a> is invalid: ` ,
new FixedUiElement ( e )
)
return null ;
}
} catch ( e ) {
console . error ( e )
DetermineLayout . ShowErrorOnCustomTheme (
` <a href=" ${ link } "> ${ link } </a> is invalid - probably not found or invalid JSON: ` ,
new FixedUiElement ( e )
)
return null ;
}
}
2021-10-15 05:20:02 +02:00
}