Просмотр исходного кода

refactor metadata for themes handling

Yuki Takei 3 лет назад
Родитель
Сommit
680fa0c533

+ 16 - 29
packages/app/src/client/services/activate-plugin.ts

@@ -1,9 +1,9 @@
 import { readFileSync } from 'fs';
 import path from 'path';
 
-import { GrowiCustomThemeSummary, ViteManifest } from '@growi/core';
+import { GrowiThemeMetadata, ViteManifest } from '@growi/core';
 
-import { GrowiPlugin, GrowiPluginResourceType } from '~/interfaces/plugin';
+import { GrowiPlugin, GrowiPluginResourceType, GrowiThemePluginMeta } from '~/interfaces/plugin';
 import { initializeGrowiFacade } from '~/utils/growi-facade';
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
@@ -21,7 +21,6 @@ declare global {
 
 const logger = loggerFactory('growi:cli:ActivatePluginService');
 
-export type GrowiPluginThemeSummariesEntries = [growiPlugin: GrowiPlugin, summaries: GrowiCustomThemeSummary[]][];
 export type GrowiPluginManifestEntries = [growiPlugin: GrowiPlugin, manifest: ViteManifest][];
 
 
@@ -34,40 +33,28 @@ async function retrievePluginManifest(growiPlugin: GrowiPlugin): Promise<ViteMan
 export class ActivatePluginService {
 
   static async retrieveThemeHref(growiPlugins: GrowiPlugin[], theme: string): Promise<string | undefined> {
-    const entries = await this.retrievePluginThemeSummariesEntries(growiPlugins);
 
     let matchedPlugin: GrowiPlugin | undefined;
-    let matchedSummary: GrowiCustomThemeSummary | undefined;
-    for (const [growiPlugin, summaries] of entries) {
-      matchedSummary = summaries.find(s => s.name === theme);
-      if (matchedSummary == null) {
-        continue;
-      }
-
-      // found
-      matchedPlugin = growiPlugin;
-      break;
-    }
-
-    if (matchedPlugin != null && matchedSummary != null) {
-      const manifest = await retrievePluginManifest(matchedPlugin);
-      return '/static/plugins' // configured by express.static
-        + `/${matchedPlugin.installedPath}/dist/${manifest[matchedSummary.manifestKey].file}`;
-    }
-  }
-
-  static async retrievePluginThemeSummariesEntries(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginThemeSummariesEntries> {
-    const entries: GrowiPluginThemeSummariesEntries = [];
+    let matchedThemeMetadata: GrowiThemeMetadata | undefined;
 
     growiPlugins
       .filter(p => p.meta.types.includes(GrowiPluginResourceType.Theme))
       .forEach(async(growiPlugin) => {
-        const summaryPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'summary.json'));
-        const summaryStr: string = await readFileSync(summaryPath, 'utf-8');
-        entries.push([growiPlugin, JSON.parse(summaryStr)]);
+        const themeMetadatas = (growiPlugin.meta as GrowiThemePluginMeta).themes;
+        const themeMetadata = themeMetadatas.find(t => t.name === theme);
+
+        // found
+        if (themeMetadata != null) {
+          matchedPlugin = growiPlugin;
+          matchedThemeMetadata = themeMetadata;
+        }
       });
 
-    return entries;
+    if (matchedPlugin != null && matchedThemeMetadata != null) {
+      const manifest = await retrievePluginManifest(matchedPlugin);
+      return '/static/plugins' // configured by express.static
+        + `/${matchedPlugin.installedPath}/dist/${manifest[matchedThemeMetadata.manifestKey].file}`;
+    }
   }
 
   static async retrievePluginManifestEntries(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginManifestEntries> {

+ 4 - 6
packages/app/src/components/Admin/Customize/CustomizeThemeOptions.tsx

@@ -1,13 +1,13 @@
 import React, { useMemo } from 'react';
 
-import { GrowiThemeColorSummary, GrowiThemeSchemeType, isCustomTheme } from '@growi/core';
+import { GrowiThemeMetadata, GrowiThemeSchemeType } from '@growi/core';
 import { useTranslation } from 'next-i18next';
 
 import { ThemeColorBox } from './ThemeColorBox';
 
 
 type Props = {
-  availableThemes: GrowiThemeColorSummary[],
+  availableThemes: GrowiThemeMetadata[],
   selectedTheme?: string,
   onSelected?: (themeName: string) => void,
 };
@@ -35,9 +35,8 @@ const CustomizeThemeOptions = (props: Props): JSX.Element => {
               <ThemeColorBox
                 key={theme.name}
                 isSelected={selectedTheme != null && selectedTheme === theme.name}
-                isCustomTheme={isCustomTheme(theme)}
+                metadata={theme}
                 onSelected={() => onSelected?.(theme.name)}
-                {...theme}
               />
             );
           })}
@@ -52,9 +51,8 @@ const CustomizeThemeOptions = (props: Props): JSX.Element => {
               <ThemeColorBox
                 key={theme.name}
                 isSelected={selectedTheme != null && selectedTheme === theme.name}
-                isCustomTheme={isCustomTheme(theme)}
+                metadata={theme}
                 onSelected={() => onSelected?.(theme.name)}
-                {...theme}
               />
             );
           })}

+ 3 - 3
packages/app/src/components/Admin/Customize/CustomizeThemeSetting.tsx

@@ -1,6 +1,6 @@
 import React, { useCallback, useEffect, useState } from 'react';
 
-import { PresetThemesSummaries } from '@growi/preset-themes';
+import { PresetThemesMetadatas } from '@growi/preset-themes';
 import { useTranslation } from 'next-i18next';
 
 import { apiv3Put } from '~/client/util/apiv3-client';
@@ -50,8 +50,8 @@ const CustomizeThemeSetting = (props: Props): JSX.Element => {
   }, [selectedTheme, t]);
 
   const availableThemes = data?.customThemeSummaries == null
-    ? PresetThemesSummaries
-    : PresetThemesSummaries.concat(data.customThemeSummaries);
+    ? PresetThemesMetadatas
+    : PresetThemesMetadatas.concat(data.customThemeSummaries);
 
   return (
     <div className="row">

+ 9 - 9
packages/app/src/components/Admin/Customize/ThemeColorBox.tsx

@@ -1,22 +1,22 @@
 import React from 'react';
 
+import type { GrowiThemeMetadata } from '@growi/core';
+
 
 type Props = {
   isSelected: boolean,
-  name: string,
-  bg: string,
-  topbar: string,
-  sidebar: string,
-  theme: string,
-  isCustomTheme?: boolean,
+  metadata: GrowiThemeMetadata,
   onSelected?: () => void,
 };
 
 export const ThemeColorBox = (props: Props): JSX.Element => {
 
   const {
-    isSelected, onSelected, name, bg, topbar, sidebar, theme, isCustomTheme,
+    isSelected, metadata, onSelected,
   } = props;
+  const {
+    name, bg, topbar, sidebar, accent, isPresetTheme,
+  } = metadata;
 
   return (
     <div
@@ -30,12 +30,12 @@ export const ThemeColorBox = (props: Props): JSX.Element => {
             <path d="M -1 -1 L65 -1 L65 65 L-1 65 L-1 -1 Z" fill={bg}></path>
             <path d="M -1 -1 L65 -1 L65 15 L-1 15 L-1 -1 Z" fill={topbar}></path>
             <path d="M -1 15 L15 15 L15 65 L-1 65 L-1 15 Z" fill={sidebar}></path>
-            <path d="M 65 45 L65 65 L45 65 L65 45 Z" fill={theme}></path>
+            <path d="M 65 45 L65 65 L45 65 L65 45 Z" fill={accent}></path>
           </g>
         </svg>
       </a>
       <span className="theme-option-name"><b>{ name }</b></span>
-      { isCustomTheme && <span className='theme-option-badge badge badge-primary mt-1'>Plugin</span> }
+      { !isPresetTheme && <span className='theme-option-badge badge badge-primary mt-1'>Plugin</span> }
     </div>
   );
 

+ 2 - 2
packages/app/src/interfaces/customize.ts

@@ -1,4 +1,4 @@
-import { GrowiCustomThemeSummary } from '@growi/core';
+import { GrowiThemeMetadata } from '@growi/core';
 
 export type IResLayoutSetting = {
   isContainerFluid: boolean,
@@ -6,5 +6,5 @@ export type IResLayoutSetting = {
 
 export type IResGrowiTheme = {
   currentTheme: string,
-  customThemeSummaries: GrowiCustomThemeSummary[],
+  customThemeSummaries: GrowiThemeMetadata[],
 }

+ 2 - 2
packages/app/src/interfaces/plugin.ts

@@ -1,4 +1,4 @@
-import { GrowiCustomThemeSummary } from '@growi/core';
+import { GrowiThemeMetadata } from '@growi/core';
 
 export const GrowiPluginResourceType = {
   Template: 'template',
@@ -29,5 +29,5 @@ export type GrowiPluginMeta = {
 }
 
 export type GrowiThemePluginMeta = GrowiPluginMeta & {
-  themes: GrowiCustomThemeSummary[]
+  themes: GrowiThemeMetadata[]
 }

+ 8 - 6
packages/app/src/pages/_document.page.tsx

@@ -1,8 +1,8 @@
 /* eslint-disable @next/next/google-font-display */
 import React from 'react';
 
-import type { GrowiCustomThemeSummary, ViteManifest, ViteManifestValue } from '@growi/core';
-import { getManifestKeyFromTheme } from '@growi/preset-themes';
+import type { ViteManifest } from '@growi/core';
+import { DefaultThemeMetadata, PresetThemesMetadatas } from '@growi/preset-themes';
 import mongoose from 'mongoose';
 import Document, {
   DocumentContext, DocumentInitialProps,
@@ -28,18 +28,20 @@ const HeadersForThemes = (props: HeadersForThemesProps): JSX.Element => {
 
   const elements: JSX.Element[] = [];
 
+  // when plugin theme is specified
   if (pluginThemeHref != null) {
     elements.push(
       <link rel="stylesheet" key={`link_custom-themes-${theme}`} href={pluginThemeHref} />,
     );
   }
+  // preset theme
   else {
-    let themeKey = getManifestKeyFromTheme(theme);
-    if (!(themeKey in presetThemesManifest)) {
+    const themeMetadata = PresetThemesMetadatas.find(p => p.name === theme);
+    const manifestKey = themeMetadata?.manifestKey ?? DefaultThemeMetadata.manifestKey;
+    if (themeMetadata == null || !(themeMetadata.manifestKey in presetThemesManifest)) {
       logger.warn(`Use default theme because the key for '${theme} does not exist in preset-themes manifest`);
-      themeKey = getManifestKeyFromTheme('default');
     }
-    const href = `/static/preset-themes/${presetThemesManifest[themeKey].file}`; // configured by express.static
+    const href = `/static/preset-themes/${presetThemesManifest[manifestKey].file}`; // configured by express.static
     elements.push(
       <link rel="stylesheet" key={`link_preset-themes-${theme}`} href={href} />,
     );

+ 4 - 4
packages/app/src/server/models/growi-plugin.ts

@@ -1,4 +1,4 @@
-import { GrowiCustomThemeSummary, GrowiThemeSchemeType } from '@growi/core';
+import { GrowiThemeMetadata, GrowiThemeSchemeType } from '@growi/core';
 import {
   Schema, Model, Document,
 } from 'mongoose';
@@ -16,7 +16,7 @@ export interface GrowiPluginModel extends Model<GrowiPluginDocument> {
   findEnabledPluginsIncludingTypes(includingTypes: GrowiPluginResourceType[]): Promise<GrowiPlugin[]>
 }
 
-const growiPluginMetaThemesSchema = new Schema<GrowiCustomThemeSummary>({
+const growiThemeMetadataSchema = new Schema<GrowiThemeMetadata>({
   name: { type: String, required: true },
   manifestKey: { type: String, required: true },
   schemeType: {
@@ -27,7 +27,7 @@ const growiPluginMetaThemesSchema = new Schema<GrowiCustomThemeSummary>({
   bg: { type: String, required: true },
   topbar: { type: String, required: true },
   sidebar: { type: String, required: true },
-  theme: { type: String, required: true },
+  accent: { type: String, required: true },
 });
 
 const growiPluginMetaSchema = new Schema<GrowiPluginMeta|GrowiThemePluginMeta>({
@@ -39,7 +39,7 @@ const growiPluginMetaSchema = new Schema<GrowiPluginMeta|GrowiThemePluginMeta>({
   },
   desc: { type: String },
   author: { type: String },
-  themes: [growiPluginMetaThemesSchema],
+  themes: [growiThemeMetadataSchema],
 });
 
 const growiPluginOriginSchema = new Schema<GrowiPluginOrigin>({

+ 1 - 1
packages/core/src/index.ts

@@ -16,7 +16,7 @@ export * from './plugin/interfaces/option-parser';
 export * from './interfaces/attachment';
 export * from './interfaces/common';
 export * from './interfaces/growi-facade';
-export * from './interfaces/growi-custom-theme-summary';
+export * from './interfaces/growi-theme-metadata';
 export * from './interfaces/has-object-id';
 export * from './interfaces/lang';
 export * from './interfaces/page';

+ 4 - 2
packages/preset-themes/src/interfaces/growi-theme-summary.ts → packages/core/src/interfaces/growi-theme-metadata.ts

@@ -5,11 +5,13 @@ export const GrowiThemeSchemeType = {
 } as const;
 export type GrowiThemeSchemeType = typeof GrowiThemeSchemeType[keyof typeof GrowiThemeSchemeType];
 
-export type GrowiThemeColorSummary = {
+export type GrowiThemeMetadata = {
   name: string,
+  manifestKey: string,
   schemeType: GrowiThemeSchemeType,
   bg: string,
   topbar: string,
   sidebar: string,
-  theme: string,
+  accent: string,
+  isPresetTheme?: boolean,
 };

+ 38 - 21
packages/preset-themes/src/consts/preset-themes.ts

@@ -1,4 +1,4 @@
-import { GrowiThemeColorSummary, GrowiThemeSchemeType } from '../interfaces/growi-theme-summary';
+import { GrowiThemeMetadata, GrowiThemeSchemeType } from '../interfaces/growi-theme-metadata';
 
 const { BOTH, LIGHT, DARK } = GrowiThemeSchemeType;
 
@@ -21,45 +21,62 @@ export const PresetThemes = {
 } as const;
 export type PresetThemes = typeof PresetThemes[keyof typeof PresetThemes];
 
+/* eslint-disable no-multi-spaces, */
 
-/* eslint-disable no-multi-spaces */
-export const PresetThemesSummaries: GrowiThemeColorSummary[] = [
+export const DefaultThemeMetadata: GrowiThemeMetadata = {
+  name: PresetThemes.DEFAULT,
+  manifestKey: `src/styles/${PresetThemes.DEFAULT}.scss`,
+  schemeType: BOTH,
+  bg: '#ffffff',
+  topbar: '#2a2929',
+  sidebar: '#122c55',
+  accent: '#209fd8',
+  isPresetTheme: true,
+};
+
+export const PresetThemesMetadatas: GrowiThemeMetadata[] = [
   // support both of light and dark
+  DefaultThemeMetadata,
   {
-    name: PresetThemes.DEFAULT,       schemeType: BOTH, bg: '#ffffff', topbar: '#2a2929', sidebar: '#122c55', theme: '#209fd8',
+    name: PresetThemes.MONO_BLUE,     schemeType: BOTH, bg: '#F7FBFD', topbar: '#2a2929', sidebar: '#00587A', accent: '#00587A',
   }, {
-    name: PresetThemes.MONO_BLUE,     schemeType: BOTH, bg: '#F7FBFD', topbar: '#2a2929', sidebar: '#00587A', theme: '#00587A',
+    name: PresetThemes.HUFFLEPUFF,    schemeType: BOTH, bg: '#EFE2CF', topbar: '#2a2929', sidebar: '#EAAB20', accent: '#993439',
   }, {
-    name: PresetThemes.HUFFLEPUFF,    schemeType: BOTH, bg: '#EFE2CF', topbar: '#2a2929', sidebar: '#EAAB20', theme: '#993439',
+    name: PresetThemes.FIRE_RED,      schemeType: BOTH, bg: '#FDFDFD', topbar: '#2c2c2c', sidebar: '#BFBFBF', accent: '#EA5532',
   }, {
-    name: PresetThemes.FIRE_RED,      schemeType: BOTH, bg: '#FDFDFD', topbar: '#2c2c2c', sidebar: '#BFBFBF', theme: '#EA5532',
-  }, {
-    name: PresetThemes.JADE_GREEN,    schemeType: BOTH, bg: '#FDFDFD', topbar: '#2c2c2c', sidebar: '#BFBFBF', theme: '#38B48B',
+    name: PresetThemes.JADE_GREEN,    schemeType: BOTH, bg: '#FDFDFD', topbar: '#2c2c2c', sidebar: '#BFBFBF', accent: '#38B48B',
   },
   // light only
   {
-    name: PresetThemes.NATURE,        schemeType: LIGHT, bg: '#f9fff3', topbar: '#234136', sidebar: '#118050', theme: '#460039',
+    name: PresetThemes.NATURE,        schemeType: LIGHT, bg: '#f9fff3', topbar: '#234136', sidebar: '#118050', accent: '#460039',
   }, {
-    name: PresetThemes.WOOD,          schemeType: LIGHT, bg: '#fffefb', topbar: '#2a2929', sidebar: '#aaa45f', theme: '#aaa45f',
+    name: PresetThemes.WOOD,          schemeType: LIGHT, bg: '#fffefb', topbar: '#2a2929', sidebar: '#aaa45f', accent: '#aaa45f',
   }, {
-    name: PresetThemes.ISLAND,        schemeType: LIGHT, bg: '#cef2ef', topbar: '#2a2929', sidebar: '#0c2a44', theme: 'rgba(183, 226, 219, 1)',
+    name: PresetThemes.ISLAND,        schemeType: LIGHT, bg: '#cef2ef', topbar: '#2a2929', sidebar: '#0c2a44', accent: 'rgba(183, 226, 219, 1)',
   }, {
-    name: PresetThemes.CHRISTMAS,     schemeType: LIGHT, bg: '#fffefb', topbar: '#b3000c', sidebar: '#30882c', theme: '#d3c665',
+    name: PresetThemes.CHRISTMAS,     schemeType: LIGHT, bg: '#fffefb', topbar: '#b3000c', sidebar: '#30882c', accent: '#d3c665',
   }, {
-    name: PresetThemes.ANTARCTIC,     schemeType: LIGHT, bg: '#ffffff', topbar: '#2a2929', sidebar: '#000080', theme: '#fa9913',
+    name: PresetThemes.ANTARCTIC,     schemeType: LIGHT, bg: '#ffffff', topbar: '#2a2929', sidebar: '#000080', accent: '#fa9913',
   }, {
-    name: PresetThemes.SPRING,        schemeType: LIGHT, bg: '#ffffff', topbar: '#d3687c', sidebar: '#ffb8c6', theme: '#67a856',
+    name: PresetThemes.SPRING,        schemeType: LIGHT, bg: '#ffffff', topbar: '#d3687c', sidebar: '#ffb8c6', accent: '#67a856',
   }, {
-    name: PresetThemes.KIBELA,        schemeType: LIGHT, bg: '#f4f5f6', topbar: '#1256a3', sidebar: '#5882fa', theme: '#b5cbf79c',
+    name: PresetThemes.KIBELA,        schemeType: LIGHT, bg: '#f4f5f6', topbar: '#1256a3', sidebar: '#5882fa', accent: '#b5cbf79c',
   },
   // dark only
   {
-    name: PresetThemes.FUTURE,       schemeType: DARK, bg: '#16282d', topbar: '#2a2929', sidebar: '#00b5b7', theme: '#00b5b7',
+    name: PresetThemes.FUTURE,        schemeType: DARK, bg: '#16282d', topbar: '#2a2929', sidebar: '#00b5b7', accent: '#00b5b7',
   }, {
-    name: PresetThemes.HALLOWEEN,    schemeType: DARK, bg: '#030003', topbar: '#aa4a04', sidebar: '#162b33', theme: '#e9af2b',
+    name: PresetThemes.HALLOWEEN,     schemeType: DARK, bg: '#030003', topbar: '#aa4a04', sidebar: '#162b33', accent: '#e9af2b',
   }, {
-    name: PresetThemes.BLACKBOARD,   schemeType: DARK, bg: '#223729', topbar: '#563E23', sidebar: '#7B5932', theme: '#DA8506',
+    name: PresetThemes.BLACKBOARD,    schemeType: DARK, bg: '#223729', topbar: '#563E23', sidebar: '#7B5932', accent: '#DA8506',
   },
-];
-
+]
+  // fill in missing information
+  .map((metadata) => {
+    return {
+      ...metadata,
+      isPresetTheme: true,
+      manifestKey: `src/styles/${metadata.name}.scss`,
+    };
+  });
 /* eslint-disable no-multi-spaces */

+ 2 - 1
packages/preset-themes/src/index.ts

@@ -1,2 +1,3 @@
 export * from './consts/preset-themes';
-export * from './utils';
+
+export const manifestPath = 'dist/themes/manifest.json';

+ 4 - 10
packages/core/src/interfaces/growi-custom-theme-summary.ts → packages/preset-themes/src/interfaces/growi-theme-metadata.ts

@@ -5,19 +5,13 @@ export const GrowiThemeSchemeType = {
 } as const;
 export type GrowiThemeSchemeType = typeof GrowiThemeSchemeType[keyof typeof GrowiThemeSchemeType];
 
-export type GrowiThemeColorSummary = {
+export type GrowiThemeMetadata = {
   name: string,
+  manifestKey: string,
   schemeType: GrowiThemeSchemeType,
   bg: string,
   topbar: string,
   sidebar: string,
-  theme: string,
-};
-
-export type GrowiCustomThemeSummary = GrowiThemeColorSummary & {
-  manifestKey: string,
-};
-
-export const isCustomTheme = (summary: GrowiThemeColorSummary): summary is GrowiCustomThemeSummary => {
-  return 'manifestKey' in summary;
+  accent: string,
+  isPresetTheme?: boolean,
 };

+ 0 - 5
packages/preset-themes/src/utils/index.ts

@@ -1,5 +0,0 @@
-export const manifestPath = 'dist/themes/manifest.json';
-
-export const getManifestKeyFromTheme = (theme: string): string => {
-  return `src/styles/${theme}.scss`;
-};