From c4aa88183fa9d8ff9b3d6b6c450448f298fd79db Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 6 Oct 2021 14:03:39 +0300 Subject: [PATCH] dock-tabs: create-resoure refactoring & fixes Signed-off-by: Roman --- .../components/dock/create-resource.store.ts | 93 +++++++++++++++++-- .../components/dock/create-resource.tsx | 68 ++++++++------ .../monaco-editor/monaco-editor.tsx | 8 +- 3 files changed, 127 insertions(+), 42 deletions(-) diff --git a/src/renderer/components/dock/create-resource.store.ts b/src/renderer/components/dock/create-resource.store.ts index 48a5ad9891..3196475be7 100644 --- a/src/renderer/components/dock/create-resource.store.ts +++ b/src/renderer/components/dock/create-resource.store.ts @@ -22,29 +22,102 @@ import fs from "fs-extra"; import path from "path"; import os from "os"; -import { autoBind } from "../../utils"; import { DockTabStore } from "./dock-tab.store"; import { dockStore, DockTabCreateSpecific, TabKind } from "./dock.store"; +import { action, makeObservable, observable } from "mobx"; + +export interface TemplatesGroup { + label: string; + templates: Record; +} export class CreateResourceStore extends DockTabStore { constructor() { - super({ - storageKey: "create_resource" - }); - autoBind(this); + super({ storageKey: "create_resource" }); + makeObservable(this); } - protected async init() { - super.init(); - await fs.ensureDir(this.userTemplatesFolder); + readonly templateGroups: Record = observable({ + [this.appTemplatesFolder]: { + label: "Lens Templates", + templates: {} as Record, + }, + [this.userTemplatesFolder]: { + label: "Custom templates", + templates: {}, + }, + }); + + get appTemplatesFolder(): string { + // production: declare files to copy in "package.json" -> "build.extraResources" + return path.resolve(__static, "../templates/create-resource"); } get userTemplatesFolder(): string { return path.resolve(os.homedir(), "~/.k8slens/templates"); } - async getTemplate(fileName: string, ext = "yaml") { - return import(`../../../../templates/create-resource/${fileName}.${ext}?raw`); + protected async init() { + super.init(); + + // scan all available templates immediately + await this.scanTemplates(this.appTemplatesFolder); + await this.scanTemplates(this.userTemplatesFolder); + } + + @action + private async scanTemplates(sourceFolder: string): Promise { + try { + const templatesGroup = this.templateGroups[sourceFolder]; + + if (!templatesGroup) return; // exit: unknown templates source folder + + await fs.ensureDir(sourceFolder); + const fileNames = await fs.readdir(sourceFolder); + + templatesGroup.templates = Object.fromEntries( + fileNames.map(fileName => [fileName, "" /* empty: content not loaded yet */]) + ); + } catch (error) { + console.error(`[CREATE-RESOURCE]: scanning template folders error: ${error}`); + } + } + + @action + async loadTemplate(init: { fileName: string, sourceFolder: string }): Promise { + const { fileName, sourceFolder } = init; + + const templatesGroup = this.templateGroups[sourceFolder]; + + if (!templatesGroup) { + return ""; // unknown group, exit + } + + const template = templatesGroup.templates[fileName]; + + if (template) return template; // return cache, preloaded already + + try { + const templatePath = path.resolve(sourceFolder, fileName); + const pathExists = await fs.pathExists(templatePath); + + if (!pathExists) return ""; // template file not exists, skip + + const textContent = await fs.readFile(templatePath, { encoding: "utf-8" }); + + templatesGroup.templates[fileName] = textContent; // save cache + + return textContent; + } catch (error) { + console.error(`[CREATE-RESOURCE]: reading "${sourceFolder}/${fileName}" has failed: ${error}`); + } + + return ""; + } + + // bundled app templates via webpack.renderer.ts, "?raw"-query loads content as text + async getBundledTemplate(fileName: string, ext = "yaml"): Promise { + return import(`${this.appTemplatesFolder}/${fileName}.${ext}?raw`); } } diff --git a/src/renderer/components/dock/create-resource.tsx b/src/renderer/components/dock/create-resource.tsx index 069910ff8b..faba70ff7c 100644 --- a/src/renderer/components/dock/create-resource.tsx +++ b/src/renderer/components/dock/create-resource.tsx @@ -21,7 +21,7 @@ import "./create-resource.scss"; import React from "react"; -import { Select, SelectOption } from "../select"; +import { GroupSelectOption, Select, SelectOption } from "../select"; import jsYaml from "js-yaml"; import { computed, makeObservable, observable } from "mobx"; import { observer } from "mobx-react"; @@ -39,34 +39,31 @@ interface Props { tab: DockTab; } +type SelectOptionTemplate = SelectOption; + +interface SelectOptionTemplateValue { + sourceFolder: string; + fileName: string; + content?: string; // available after loading (template selection) +} + @observer export class CreateResource extends React.Component { - @observable currentTemplates: Map = new Map(); - @observable error = ""; - @observable templates = observable.map(); - constructor(props: Props) { super(props); makeObservable(this); } - // TODO - @computed get templateOptions(): SelectOption[] { - return []; - } + @observable error = ""; get tabId() { return this.props.tab.id; } - get data() { + get draft() { return createResourceStore.getData(this.tabId); } - get selectedTemplate() { - return this.currentTemplates.get(this.tabId) ?? null; - } - onChange = (value: string) => { createResourceStore.setData(this.tabId, value); }; @@ -75,24 +72,14 @@ export class CreateResource extends React.Component { this.error = error; }; - // FIXME - onSelectTemplate = (item: SelectOption) => { - console.log(`SELECTED TEMPLATE: ${this.tabId}`, item); - // this.currentTemplates.set(this.tabId, item); - // - // fs.readFile(item.value, "utf8").then(templateFileContent => { - // createResourceStore.setData(this.tabId, templateFileContent); - // }); - }; - create = async () => { - if (this.error || !this.data.trim()) { + if (this.error || !this.draft) { // do not save when field is empty or there is an error return null; } // skip empty documents if "---" pasted at the beginning or end - const resources = jsYaml.safeLoadAll(this.data).filter(Boolean); + const resources = jsYaml.safeLoadAll(this.draft).filter(Boolean); const createdResources: string[] = []; const errors: string[] = []; @@ -125,20 +112,41 @@ export class CreateResource extends React.Component {