forked from MapComplete/MapComplete
		
	Fix: fix #1817, some more improvements to the loading screen
This commit is contained in:
		
							parent
							
								
									e36e594b89
								
							
						
					
					
						commit
						6394ee8e68
					
				
					 2 changed files with 545 additions and 523 deletions
				
			
		|  | @ -17,19 +17,38 @@ import * as eli_global from "../src/assets/global-raster-layers.json" | ||||||
| import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils" | import ValidationUtils from "../src/Models/ThemeConfig/Conversion/ValidationUtils" | ||||||
| import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" | import { LayerConfigJson } from "../src/Models/ThemeConfig/Json/LayerConfigJson" | ||||||
| import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" | import { QuestionableTagRenderingConfigJson } from "../src/Models/ThemeConfig/Json/QuestionableTagRenderingConfigJson" | ||||||
|  | import Script from "./Script" | ||||||
|  | import crypto from "crypto" | ||||||
| 
 | 
 | ||||||
| const sharp = require("sharp") | const sharp = require("sharp") | ||||||
| const template = readFileSync("theme.html", "utf8") |  | ||||||
| let codeTemplate = readFileSync("src/index_theme.ts.template", "utf8") |  | ||||||
| 
 | 
 | ||||||
| function enc(str: string): string { | class GenerateLayouts extends Script { | ||||||
|  |     private readonly template = readFileSync("theme.html", "utf8") | ||||||
|  |     private readonly codeTemplate = readFileSync("src/index_theme.ts.template", "utf8") | ||||||
|  |     private readonly removeOtherLanguages = readFileSync("src/UI/RemoveOtherLanguages.ts", "utf8") | ||||||
|  |         .split("\n") | ||||||
|  |         .slice(1) | ||||||
|  |         .map((s) => s.trim()) | ||||||
|  |         .filter((s) => s !== "") | ||||||
|  |         .join("\n") | ||||||
|  |     private readonly removeOtherLanguagesHash = | ||||||
|  |         "sha256-" + crypto.createHash("sha256").update(this.removeOtherLanguages).digest("base64") | ||||||
|  |     private previousSrc: Set<string> = new Set<string>() | ||||||
|  |     private eliUrlsCached: string[] | ||||||
|  |     private date = new Date().toISOString() | ||||||
|  | 
 | ||||||
|  |     constructor() { | ||||||
|  |         super("Generates an '<theme>.html' and 'index_<theme>.ts' for every theme") | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     enc(str: string): string { | ||||||
|         return encodeURIComponent(str.toLowerCase()) |         return encodeURIComponent(str.toLowerCase()) | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function createIcon(iconPath: string, size: number, alreadyWritten: string[]) { |     async createIcon(iconPath: string, size: number, alreadyWritten: string[]) { | ||||||
|         let name = iconPath.split(".").slice(0, -1).join(".") // drop svg suffix
 |         let name = iconPath.split(".").slice(0, -1).join(".") // drop svg suffix
 | ||||||
|         if (name.startsWith("./")) { |         if (name.startsWith("./")) { | ||||||
|         name = name.substr(2) |             name = name.substring(2) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const newname = `assets/generated/images/${name.replace(/\//g, "_")}${size}.png` |         const newname = `assets/generated/images/${name.replace(/\//g, "_")}${size}.png` | ||||||
|  | @ -57,9 +76,9 @@ async function createIcon(iconPath: string, size: number, alreadyWritten: string | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return newname |         return newname | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> { |     async createSocialImage(layout: LayoutConfig, template: "" | "Wide"): Promise<string> { | ||||||
|         if (!layout.icon.endsWith(".svg")) { |         if (!layout.icon.endsWith(".svg")) { | ||||||
|             console.warn( |             console.warn( | ||||||
|                 "Not creating a social image for " + |                 "Not creating a social image for " + | ||||||
|  | @ -96,7 +115,9 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P | ||||||
|                 return { |                 return { | ||||||
|                     $: { |                     $: { | ||||||
|                         id: "icon", |                         id: "icon", | ||||||
|                     transform: `translate(${cx - r},${cy - r}) scale(${(r * 2) / Number(width)}) `, |                         transform: `translate(${cx - r},${cy - r}) scale(${ | ||||||
|  |                             (r * 2) / Number(width) | ||||||
|  |                         }) `,
 | ||||||
|                     }, |                     }, | ||||||
|                     g: [svg], |                     g: [svg], | ||||||
|                 } |                 } | ||||||
|  | @ -114,15 +135,15 @@ async function createSocialImage(layout: LayoutConfig, template: "" | "Wide"): P | ||||||
|         writeFileSync(path, xml) |         writeFileSync(path, xml) | ||||||
|         console.log("Created social image at ", path) |         console.log("Created social image at ", path) | ||||||
|         return path |         return path | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function createManifest( |     async createManifest( | ||||||
|         layout: LayoutConfig, |         layout: LayoutConfig, | ||||||
|         alreadyWritten: string[] |         alreadyWritten: string[] | ||||||
| ): Promise<{ |     ): Promise<{ | ||||||
|         manifest: any |         manifest: any | ||||||
|         whiteIcons: string[] |         whiteIcons: string[] | ||||||
| }> { |     }> { | ||||||
|         Translation.forcedLanguage = "en" |         Translation.forcedLanguage = "en" | ||||||
|         const icons = [] |         const icons = [] | ||||||
| 
 | 
 | ||||||
|  | @ -153,8 +174,8 @@ async function createManifest( | ||||||
| 
 | 
 | ||||||
|             const sizes = [72, 96, 120, 128, 144, 152, 180, 192, 384, 512] |             const sizes = [72, 96, 120, 128, 144, 152, 180, 192, 384, 512] | ||||||
|             for (const size of sizes) { |             for (const size of sizes) { | ||||||
|             const name = await createIcon(path, size, alreadyWritten) |                 const name = await this.createIcon(path, size, alreadyWritten) | ||||||
|             const whiteIcon = await createIcon(whiteBackgroundPath, size, alreadyWritten) |                 const whiteIcon = await this.createIcon(whiteBackgroundPath, size, alreadyWritten) | ||||||
|                 whiteIcons.push(whiteIcon) |                 whiteIcons.push(whiteIcon) | ||||||
|                 icons.push({ |                 icons.push({ | ||||||
|                     src: name, |                     src: name, | ||||||
|  | @ -196,9 +217,9 @@ async function createManifest( | ||||||
|             manifest, |             manifest, | ||||||
|             whiteIcons, |             whiteIcons, | ||||||
|         } |         } | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| function asLangSpan(t: Translation, tag = "span"): string { |     asLangSpan(t: Translation, tag = "span"): string { | ||||||
|         const values: string[] = [] |         const values: string[] = [] | ||||||
|         for (const lang in t.translations) { |         for (const lang in t.translations) { | ||||||
|             if (lang === "_context") { |             if (lang === "_context") { | ||||||
|  | @ -207,15 +228,11 @@ function asLangSpan(t: Translation, tag = "span"): string { | ||||||
|             values.push(`<${tag} lang="${lang}">${t.translations[lang]}</${tag}>`) |             values.push(`<${tag} lang="${lang}">${t.translations[lang]}</${tag}>`) | ||||||
|         } |         } | ||||||
|         return values.join("\n") |         return values.join("\n") | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| let previousSrc: Set<string> = new Set<string>() |     async eliUrls(): Promise<string[]> { | ||||||
| 
 |         if (this.eliUrlsCached) { | ||||||
| let eliUrlsCached: string[] |             return this.eliUrlsCached | ||||||
| 
 |  | ||||||
| async function eliUrls(): Promise<string[]> { |  | ||||||
|     if (eliUrlsCached) { |  | ||||||
|         return eliUrlsCached |  | ||||||
|         } |         } | ||||||
|         const urls: string[] = [] |         const urls: string[] = [] | ||||||
|         const regex = /{switch:([^}]+)}/ |         const regex = /{switch:([^}]+)}/ | ||||||
|  | @ -260,17 +277,17 @@ async function eliUrls(): Promise<string[]> { | ||||||
|                 urls.push(styleSpec["glyphs"]) |                 urls.push(styleSpec["glyphs"]) | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     eliUrlsCached = urls |         this.eliUrlsCached = urls | ||||||
|         return Utils.NoNull(urls).sort() |         return Utils.NoNull(urls).sort() | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function generateCsp( |     async generateCsp( | ||||||
|         layout: LayoutConfig, |         layout: LayoutConfig, | ||||||
|         layoutJson: LayoutConfigJson, |         layoutJson: LayoutConfigJson, | ||||||
|         options: { |         options: { | ||||||
|             scriptSrcs: string[] |             scriptSrcs: string[] | ||||||
|         } |         } | ||||||
| ): Promise<string> { |     ): Promise<string> { | ||||||
|         const apiUrls: string[] = [ |         const apiUrls: string[] = [ | ||||||
|             ...Constants.defaultOverpassUrls, |             ...Constants.defaultOverpassUrls, | ||||||
|             Constants.countryCoderEndpoint, |             Constants.countryCoderEndpoint, | ||||||
|  | @ -279,7 +296,7 @@ async function generateCsp( | ||||||
|             "https://api.openstreetmap.org", |             "https://api.openstreetmap.org", | ||||||
|             "https://pietervdvn.goatcounter.com", |             "https://pietervdvn.goatcounter.com", | ||||||
|             "https://cache.mapcomplete.org", |             "https://cache.mapcomplete.org", | ||||||
|     ].concat(...(await eliUrls())) |         ].concat(...(await this.eliUrls())) | ||||||
| 
 | 
 | ||||||
|         SpecialVisualizations.specialVisualizations.forEach((sv) => { |         SpecialVisualizations.specialVisualizations.forEach((sv) => { | ||||||
|             if (typeof sv.needsUrls === "function") { |             if (typeof sv.needsUrls === "function") { | ||||||
|  | @ -339,17 +356,16 @@ async function generateCsp( | ||||||
| 
 | 
 | ||||||
|         const connectSrc = Array.from(hosts).sort() |         const connectSrc = Array.from(hosts).sort() | ||||||
| 
 | 
 | ||||||
|     const newSrcs = connectSrc.filter((newItem) => !previousSrc.has(newItem)) |         const newSrcs = connectSrc.filter((newItem) => !this.previousSrc.has(newItem)) | ||||||
| 
 | 
 | ||||||
|         console.log( |         console.log( | ||||||
|             "Got", |             "Got", | ||||||
|             hosts.size, |             hosts.size, | ||||||
|             "connect-src items for theme", |             "connect-src items for theme", | ||||||
|             layout.id, |             layout.id, | ||||||
|         "(extra sources: ", |             newSrcs.length > 0 ? "(extra sources: " + newSrcs.join(" ") + ")" : "" | ||||||
|         newSrcs.join(" ") + ")" |  | ||||||
|         ) |         ) | ||||||
|     previousSrc = hosts |         this.previousSrc = hosts | ||||||
| 
 | 
 | ||||||
|         const csp: Record<string, string> = { |         const csp: Record<string, string> = { | ||||||
|             "default-src": "'self'", |             "default-src": "'self'", | ||||||
|  | @ -359,9 +375,11 @@ async function generateCsp( | ||||||
|             "report-to": "https://report.mapcomplete.org/csp", |             "report-to": "https://report.mapcomplete.org/csp", | ||||||
|             "worker-src": "'self' blob:", // Vite somehow loads the worker via a 'blob'
 |             "worker-src": "'self' blob:", // Vite somehow loads the worker via a 'blob'
 | ||||||
|             "style-src": "'self' 'unsafe-inline'", // unsafe-inline is needed to change the default background pin colours
 |             "style-src": "'self' 'unsafe-inline'", // unsafe-inline is needed to change the default background pin colours
 | ||||||
|         "script-src": ["'self'", "https://gc.zgo.at/count.js", ...(options?.scriptSrcs ?? [])].join( |             "script-src": [ | ||||||
|             " " |                 "'self'", | ||||||
|         ), |                 "https://gc.zgo.at/count.js", | ||||||
|  |                 ...(options?.scriptSrcs?.map((s) => "'" + s + "'") ?? []), | ||||||
|  |             ].join(" "), | ||||||
|         } |         } | ||||||
|         const content = Object.keys(csp) |         const content = Object.keys(csp) | ||||||
|             .map((k) => k + " " + csp[k]) |             .map((k) => k + " " + csp[k]) | ||||||
|  | @ -371,15 +389,14 @@ async function generateCsp( | ||||||
|             `<meta http-equiv ="Report-To" content='{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}'>`, |             `<meta http-equiv ="Report-To" content='{"group":"csp-endpoint", "max_age": 86400,"endpoints": [\{"url": "https://report.mapcomplete.org/csp"}], "include_subdomains": true}'>`, | ||||||
|             `<meta http-equiv="Content-Security-Policy" content="${content}">`, |             `<meta http-equiv="Content-Security-Policy" content="${content}">`, | ||||||
|         ].join("\n") |         ].join("\n") | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function createLandingPage( |     async createLandingPage( | ||||||
|         layout: LayoutConfig, |         layout: LayoutConfig, | ||||||
|         layoutJson: LayoutConfigJson, |         layoutJson: LayoutConfigJson, | ||||||
|     manifest, |  | ||||||
|         whiteIcons, |         whiteIcons, | ||||||
|         alreadyWritten |         alreadyWritten | ||||||
| ) { |     ) { | ||||||
|         Locale.language.setData(layout.language[0]) |         Locale.language.setData(layout.language[0]) | ||||||
|         const targetLanguage = layout.language[0] |         const targetLanguage = layout.language[0] | ||||||
|         const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"') |         const ogTitle = Translations.T(layout.title).textFor(targetLanguage).replace(/"/g, '\\"') | ||||||
|  | @ -391,16 +408,16 @@ async function createLandingPage( | ||||||
|         let ogImage = layout.socialImage |         let ogImage = layout.socialImage | ||||||
|         let twitterImage = ogImage |         let twitterImage = ogImage | ||||||
|         if (ogImage === LayoutConfig.defaultSocialImage && layout.official) { |         if (ogImage === LayoutConfig.defaultSocialImage && layout.official) { | ||||||
|         ogImage = (await createSocialImage(layout, "")) ?? layout.socialImage |             ogImage = (await this.createSocialImage(layout, "")) ?? layout.socialImage | ||||||
|         twitterImage = (await createSocialImage(layout, "Wide")) ?? layout.socialImage |             twitterImage = (await this.createSocialImage(layout, "Wide")) ?? layout.socialImage | ||||||
|         } |         } | ||||||
|         if (twitterImage.endsWith(".svg")) { |         if (twitterImage.endsWith(".svg")) { | ||||||
|             // svgs are badly supported as social image, we use a generated svg instead
 |             // svgs are badly supported as social image, we use a generated svg instead
 | ||||||
|         twitterImage = await createIcon(twitterImage, 512, alreadyWritten) |             twitterImage = await this.createIcon(twitterImage, 512, alreadyWritten) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (ogImage.endsWith(".svg")) { |         if (ogImage.endsWith(".svg")) { | ||||||
|         ogImage = await createIcon(ogImage, 512, alreadyWritten) |             ogImage = await this.createIcon(ogImage, 512, alreadyWritten) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         let customCss = "" |         let customCss = "" | ||||||
|  | @ -409,7 +426,7 @@ async function createLandingPage( | ||||||
|                 const cssContent = readFileSync(layout.customCss) |                 const cssContent = readFileSync(layout.customCss) | ||||||
|                 customCss = "<style>" + cssContent + "</style>" |                 customCss = "<style>" + cssContent + "</style>" | ||||||
|             } catch (e) { |             } catch (e) { | ||||||
|             customCss = `<link rel='stylesheet' href="${layout.customCss}"/>` |                 customCss = `<link rel="stylesheet" href="${layout.customCss}"/>` | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -442,7 +459,7 @@ async function createLandingPage( | ||||||
| 
 | 
 | ||||||
|         let themeSpecific = [ |         let themeSpecific = [ | ||||||
|             `<title>${ogTitle}</title>`, |             `<title>${ogTitle}</title>`, | ||||||
|         `<link rel="manifest" href="${enc(layout.id)}.webmanifest">`, |             `<link rel="manifest" href="${this.enc(layout.id)}.webmanifest">`, | ||||||
|             og, |             og, | ||||||
|             customCss, |             customCss, | ||||||
|             `<link rel="icon" href="${icon}" sizes="any" type="image/svg+xml">`, |             `<link rel="icon" href="${icon}" sizes="any" type="image/svg+xml">`, | ||||||
|  | @ -450,9 +467,10 @@ async function createLandingPage( | ||||||
|         ].join("\n") |         ].join("\n") | ||||||
| 
 | 
 | ||||||
|         const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title }) |         const loadingText = Translations.t.general.loadingTheme.Subs({ theme: layout.title }) | ||||||
|     const templateLines = template.split("\n") |         // const templateLines: string[] = this.template.split("\n").slice(1) // Slice to remove the 'export {}'-line
 | ||||||
|     let output = template | 
 | ||||||
|         .replace("Loading MapComplete, hang on...", asLangSpan(loadingText, "h1")) |         return this.template | ||||||
|  |             .replace("Loading MapComplete, hang on...", this.asLangSpan(loadingText, "h1")) | ||||||
|             .replace( |             .replace( | ||||||
|                 "Made with OpenStreetMap", |                 "Made with OpenStreetMap", | ||||||
|                 Translations.t.general.poweredByOsm.textFor(targetLanguage) |                 Translations.t.general.poweredByOsm.textFor(targetLanguage) | ||||||
|  | @ -460,29 +478,31 @@ async function createLandingPage( | ||||||
|             .replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific) |             .replace(/<!-- THEME-SPECIFIC -->.*<!-- THEME-SPECIFIC-END-->/s, themeSpecific) | ||||||
|             .replace( |             .replace( | ||||||
|                 /<!-- CSP -->/, |                 /<!-- CSP -->/, | ||||||
|             await generateCsp(layout, layoutJson, { |                 await this.generateCsp(layout, layoutJson, { | ||||||
|                 scriptSrcs: [], |                     scriptSrcs: [this.removeOtherLanguagesHash], | ||||||
|                 }) |                 }) | ||||||
|             ) |             ) | ||||||
|             .replace( |             .replace( | ||||||
|                 /<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s, |                 /<!-- DESCRIPTION START -->.*<!-- DESCRIPTION END -->/s, | ||||||
|             asLangSpan(layout.shortDescription) |                 this.asLangSpan(layout.shortDescription) | ||||||
|             ) |             ) | ||||||
|             .replace( |             .replace( | ||||||
|                 /<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s, |                 /<!-- IMAGE-START -->.*<!-- IMAGE-END -->/s, | ||||||
|             "<img class='p-8 h-32 w-32 self-start' src='" + icon + "' />" |                 "<img class='p-4 h-32 w-32 self-start' src='" + icon + "' />" | ||||||
|             ) |             ) | ||||||
| 
 |  | ||||||
|             .replace( |             .replace( | ||||||
|                 /.*\/src\/index\.ts.*/, |                 /.*\/src\/index\.ts.*/, | ||||||
|                 `<script type="module" src="./index_${layout.id}.ts"></script>` |                 `<script type="module" src="./index_${layout.id}.ts"></script>` | ||||||
|             ) |             ) | ||||||
|         .replace("Version", Constants.vNumber) |  | ||||||
| 
 | 
 | ||||||
|     return output |             .replace( | ||||||
| } |                 /\n.*RemoveOtherLanguages.*\n/i, | ||||||
|  |                 "\n<script>" + this.removeOtherLanguages + "</script>\n" | ||||||
|  |             ) | ||||||
|  |             .replace("Version", `${Constants.vNumber} <div class='text-xs'>${this.date}</div>`) | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| async function createIndexFor(theme: LayoutConfig) { |     async createIndexFor(theme: LayoutConfig) { | ||||||
|         const filename = "index_" + theme.id + ".ts" |         const filename = "index_" + theme.id + ".ts" | ||||||
| 
 | 
 | ||||||
|         const imports = [ |         const imports = [ | ||||||
|  | @ -490,7 +510,9 @@ async function createIndexFor(theme: LayoutConfig) { | ||||||
|             `import { ThemeMetaTagging } from "./src/assets/generated/metatagging/${theme.id}"`, |             `import { ThemeMetaTagging } from "./src/assets/generated/metatagging/${theme.id}"`, | ||||||
|         ] |         ] | ||||||
|         for (const layerName of Constants.added_by_default) { |         for (const layerName of Constants.added_by_default) { | ||||||
|         imports.push(`import ${layerName} from "./src/assets/generated/layers/${layerName}.json"`) |             imports.push( | ||||||
|  |                 `import ${layerName} from "./src/assets/generated/layers/${layerName}.json"` | ||||||
|  |             ) | ||||||
|         } |         } | ||||||
|         writeFileSync(filename, imports.join("\n") + "\n") |         writeFileSync(filename, imports.join("\n") + "\n") | ||||||
| 
 | 
 | ||||||
|  | @ -500,22 +522,25 @@ async function createIndexFor(theme: LayoutConfig) { | ||||||
|             addLayers.push(`    layout.layers.push(<any> ${layerName})`) |             addLayers.push(`    layout.layers.push(<any> ${layerName})`) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     codeTemplate = codeTemplate.replace("    // LAYOUT.ADD_LAYERS", addLayers.join("\n")) |         let codeTemplate = this.codeTemplate.replace( | ||||||
|  |             "    // LAYOUT.ADD_LAYERS", | ||||||
|  |             addLayers.join("\n") | ||||||
|  |         ) | ||||||
| 
 | 
 | ||||||
|         appendFileSync(filename, codeTemplate) |         appendFileSync(filename, codeTemplate) | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| function createDir(path) { |     createDir(path) { | ||||||
|         if (!existsSync(path)) { |         if (!existsSync(path)) { | ||||||
|             mkdirSync(path) |             mkdirSync(path) | ||||||
|         } |         } | ||||||
| } |     } | ||||||
| 
 | 
 | ||||||
| async function main(): Promise<void> { |     async main(): Promise<void> { | ||||||
|         const alreadyWritten = [] |         const alreadyWritten = [] | ||||||
|     createDir("./public/assets/") |         this.createDir("./public/assets/") | ||||||
|     createDir("./public/assets/generated") |         this.createDir("./public/assets/generated") | ||||||
|     createDir("./public/assets/generated/images") |         this.createDir("./public/assets/generated/images") | ||||||
| 
 | 
 | ||||||
|         const blacklist = [ |         const blacklist = [ | ||||||
|             "", |             "", | ||||||
|  | @ -554,25 +579,24 @@ async function main(): Promise<void> { | ||||||
|                     console.log("Could not write manifest for ", layoutName, " because ", err) |                     console.log("Could not write manifest for ", layoutName, " because ", err) | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         const { manifest, whiteIcons } = await createManifest(layout, alreadyWritten) |             const { manifest, whiteIcons } = await this.createManifest(layout, alreadyWritten) | ||||||
|             const manif = JSON.stringify(manifest, undefined, 2) |             const manif = JSON.stringify(manifest, undefined, 2) | ||||||
|             const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest" |             const manifestLocation = encodeURIComponent(layout.id.toLowerCase()) + ".webmanifest" | ||||||
|             writeFile("public/" + manifestLocation, manif, err) |             writeFile("public/" + manifestLocation, manif, err) | ||||||
| 
 | 
 | ||||||
|             // Create a landing page for the given theme
 |             // Create a landing page for the given theme
 | ||||||
|         const landing = await createLandingPage( |             const landing = await this.createLandingPage( | ||||||
|                 layout, |                 layout, | ||||||
|                 layoutConfigJson, |                 layoutConfigJson, | ||||||
|             manifest, |  | ||||||
|                 whiteIcons, |                 whiteIcons, | ||||||
|                 alreadyWritten |                 alreadyWritten | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         writeFile(enc(layout.id) + ".html", landing, err) |             writeFile(this.enc(layout.id) + ".html", landing, err) | ||||||
|         await createIndexFor(layout) |             await this.createIndexFor(layout) | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     const { manifest } = await createManifest( |         const { manifest } = await this.createManifest( | ||||||
|             new LayoutConfig({ |             new LayoutConfig({ | ||||||
|                 icon: "./assets/svg/mapcomplete_logo.svg", |                 icon: "./assets/svg/mapcomplete_logo.svg", | ||||||
|                 id: "index", |                 id: "index", | ||||||
|  | @ -589,9 +613,7 @@ async function main(): Promise<void> { | ||||||
| 
 | 
 | ||||||
|         const manif = JSON.stringify(manifest, undefined, 2) |         const manif = JSON.stringify(manifest, undefined, 2) | ||||||
|         writeFileSync("public/index.webmanifest", manif) |         writeFileSync("public/index.webmanifest", manif) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ScriptUtils.fixUtils() | new GenerateLayouts().run() | ||||||
| main().then(() => { |  | ||||||
|     console.log("All done!") |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  | @ -59,12 +59,12 @@ | ||||||
|                 </p> |                 </p> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <div class="flex justify-between items-start w-full"> |             <div class="flex justify-between items-end w-full"> | ||||||
|                  |                  | ||||||
|             <!-- IMAGE-START --> |             <!-- IMAGE-START --> | ||||||
|             <img aria-hidden="true" class="p-8 h-32 w-32 self-start" src="./assets/svg/add.svg"> |             <img aria-hidden="true" class="p-4 h-32 w-32 self-start" src="./assets/svg/add.svg"> | ||||||
|             <!-- IMAGE-END --> |             <!-- IMAGE-END --> | ||||||
|                 <div class="h-min subtle">  |                 <div class="h-min subtle flex flex-col items-end">  | ||||||
|                 Version |                 Version | ||||||
|                 </div> |                 </div> | ||||||
|                  |                  | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue