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" ;
2021-10-29 01:41:37 +02:00
import LegacyJsonConvert from "../Models/ThemeConfig/LegacyJsonConvert" ;
2021-10-15 05:20:02 +02:00
export default class DetermineLayout {
/ * *
* Gets the correct layout for this website
* /
public static async GetLayout ( ) : Promise < [ LayoutConfig , string ] > {
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" ) ) {
// The userLayout is actually an url
const layout = await DetermineLayout . LoadRemoteTheme ( layoutFromBase64 )
return [ layout , undefined ]
}
if ( layoutFromBase64 !== "false" ) {
// We have to load something from the hash (or from disk)
let loaded = DetermineLayout . LoadLayoutFromHash ( loadCustomThemeParam ) ;
if ( loaded === null ) {
return [ null , undefined ]
}
return loaded
}
let layoutId : string = undefined
if ( location . href . indexOf ( "buurtnatuur.be" ) >= 0 ) {
layoutId = "buurtnatuur"
}
const path = window . location . pathname . split ( "/" ) . slice ( - 1 ) [ 0 ] ;
if ( path !== "index.html" && path !== "" ) {
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 )
}
}
return [ layoutToUse , undefined ]
}
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-10-27 19:57:15 +02:00
const parsed = await Utils . downloadJson ( link )
console . log ( "Got " , parsed )
2021-10-29 01:41:37 +02:00
LegacyJsonConvert . fixThemeConfig ( parsed )
2021-10-15 05:20:02 +02:00
try {
parsed . id = link ;
2021-10-27 19:57:15 +02:00
return new LayoutConfig ( parsed , false ) . patchImages ( link , JSON . stringify ( parsed ) ) ;
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
DetermineLayout . ShowErrorOnCustomTheme (
` <a href=" ${ link } "> ${ link } </a> is invalid: ` ,
new FixedUiElement ( e )
)
return null ;
}
} catch ( e ) {
2021-10-27 20:19:45 +02:00
console . error ( e )
2021-10-15 05:20:02 +02:00
DetermineLayout . ShowErrorOnCustomTheme (
` <a href=" ${ link } "> ${ link } </a> is invalid - probably not found or invalid JSON: ` ,
new FixedUiElement ( e )
)
return null ;
}
}
public static LoadLayoutFromHash (
userLayoutParam : UIEventSource < string >
) : [ LayoutConfig , string ] | null {
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-10-29 01:41:37 +02:00
LegacyJsonConvert . fixThemeConfig ( json )
2021-10-15 05:20:02 +02:00
const layoutToUse = new LayoutConfig ( json , false ) ;
userLayoutParam . setData ( layoutToUse . id ) ;
return [ layoutToUse , btoa ( Utils . MinifyJSON ( JSON . stringify ( json ) ) ) ] ;
} 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" ) )
}
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" ) ;
}
}