diff --git a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts index 7149cd5c72..2361eb1d43 100644 --- a/src/Models/ThemeConfig/Conversion/PrepareTheme.ts +++ b/src/Models/ThemeConfig/Conversion/PrepareTheme.ts @@ -617,6 +617,33 @@ class OrderTheme extends DesugaringStep{ return Utils.reorder(json, OrderTheme.themeAttributesOrder) } } + +class DeriveDescription extends DesugaringStep { + private readonly _key: string + private readonly _sourceKey: "name" + constructor(key: "icon" | "description" | "title", sourceKey?: "name") { + super("DeriveDescription", "If a single layer and no description is given, steal the description from the layer") + this._key = key + this._sourceKey = sourceKey + } + + convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson { + if (json[this._key]) { + return json + } + const msg = "This theme has no "+this._key+". If there is only a single layer, we can steal it from this layer though. Current layers are "+json.layers.map(l => l["id"]).join("; ") + if (json.layers.length !== 1) { + context.err(msg) + } + const l = json.layers[0] // Already expanded + context.info(`Added '${this._key}' to theme ${json.id} based on single layer`) + return { + ...json, + [this._key]: l[this._sourceKey ?? this._key], + } + } +} + export class PrepareTheme extends Fuse { private state: DesugaringContext @@ -632,6 +659,9 @@ export class PrepareTheme extends Fuse { new PreparePersonalTheme(state), new WarnForUnsubstitutedLayersInTheme(), new On("layers", new Concat(new SubstituteLayer(state))), + new DeriveDescription( "description"), + new DeriveDescription( "icon"), + new DeriveDescription( "title", "name"), new SetDefault("socialImage", "assets/SocialImage.png", true), // We expand all tagrenderings first... diff --git a/src/Models/ThemeConfig/Conversion/ValidateTheme.ts b/src/Models/ThemeConfig/Conversion/ValidateTheme.ts index c743efaac1..e24ce8c465 100644 --- a/src/Models/ThemeConfig/Conversion/ValidateTheme.ts +++ b/src/Models/ThemeConfig/Conversion/ValidateTheme.ts @@ -35,6 +35,9 @@ export class ValidateTheme extends DesugaringStep { } convert(json: ThemeConfigJson, context: ConversionContext): ThemeConfigJson { + if (!json.title) { + context.enter("title").err(`The theme ${json.id} does not have a title defined.`) + } const theme = new ThemeConfig(json, this._isBuiltin) { // Legacy format checks @@ -55,9 +58,7 @@ export class ValidateTheme extends DesugaringStep { } } } - if (!json.title) { - context.enter("title").err(`The theme ${json.id} does not have a title defined.`) - } + if (!json.icon) { context.enter("icon").err("A theme should have an icon") } diff --git a/src/Models/ThemeConfig/Json/ThemeConfigJson.ts b/src/Models/ThemeConfig/Json/ThemeConfigJson.ts index bebc25b82e..61f6cd388b 100644 --- a/src/Models/ThemeConfig/Json/ThemeConfigJson.ts +++ b/src/Models/ThemeConfig/Json/ThemeConfigJson.ts @@ -56,10 +56,11 @@ export interface ThemeConfigJson { /** * question: How would you describe this theme? * The description, as shown in the welcome message and the more-screen + * ifunset: reuse the description of the only layer * group: basic * */ - description: Translatable + description?: Translatable /** * A short description, showed as social description and in the 'more theme'-buttons. diff --git a/src/Models/ThemeConfig/ThemeConfig.ts b/src/Models/ThemeConfig/ThemeConfig.ts index bb96fe744e..a104f4f7ce 100644 --- a/src/Models/ThemeConfig/ThemeConfig.ts +++ b/src/Models/ThemeConfig/ThemeConfig.ts @@ -132,9 +132,7 @@ export default class ThemeConfig implements ThemeInformation { } const context = this.id this.credits = Array.isArray(json.credits) ? json.credits.join("; ") : json.credits - if (!json.title) { - throw `The theme ${json.id} does not have a title defined.` - } + this.language = json.mustHaveLanguage ?? Object.keys(json.title) {