From 6bb983154296eadfefee676913ff476dfc36a0e0 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Thu, 30 Mar 2023 18:28:04 +0200 Subject: [PATCH] Implementation of the project selector --- package.json | 23 ++++++ src/commands/Cache.ts | 6 +- src/constants/ExtensionState.ts | 4 + src/constants/settings.ts | 5 ++ src/dashboardWebView/DashboardMessage.ts | 3 + .../components/Header/Header.tsx | 5 +- .../components/Header/ProjectSwitcher.tsx | 58 +++++++++++++++ .../components/Menu/MenuButton.tsx | 28 ++++--- src/dashboardWebView/models/Settings.ts | 3 + src/helpers/DashboardSettings.ts | 2 + src/helpers/SettingsHelper.ts | 73 ++++++++++++++++++- src/listeners/dashboard/PagesListener.ts | 1 + src/listeners/dashboard/SettingsListener.ts | 30 ++++++++ src/models/Project.ts | 5 ++ src/models/index.ts | 1 + 15 files changed, 227 insertions(+), 20 deletions(-) create mode 100644 src/dashboardWebView/components/Header/ProjectSwitcher.tsx create mode 100644 src/models/Project.ts diff --git a/package.json b/package.json index a0e56189..d23eb791 100644 --- a/package.json +++ b/package.json @@ -94,8 +94,31 @@ }] }, "configuration": { + "$id": "#gobalconfiguration", "title": "Front Matter: use frontmatter.json for shared team settings", + "type": "object", "properties": { + "frontMatter.projects": { + "type": "array", + "markdownDescription": "Specify the list of projects to load in the Front Matter CMS. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.projects)", + "default": [], + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "markdownDescription": "Specify the name of the project." + }, + "default": { + "type": "boolean", + "markdownDescription": "Specify if this project is the default project to load." + }, + "configuration": { + "$ref": "#gobalconfiguration" + } + } + } + }, "frontMatter.sponsors.ai.enabled": { "type": "boolean", "default": false, diff --git a/src/commands/Cache.ts b/src/commands/Cache.ts index e2f2b55d..0490e1c6 100644 --- a/src/commands/Cache.ts +++ b/src/commands/Cache.ts @@ -20,13 +20,15 @@ export class Cache { await Extension.getInstance().setState(key, data, type); } - private static async clear() { + public static async clear(showNotification: boolean = true) { const ext = Extension.getInstance(); await ext.setState(ExtensionState.Dashboard.Pages.Cache, undefined, 'workspace', true); await ext.setState(ExtensionState.Dashboard.Pages.Index, undefined, 'workspace', true); await ext.setState(ExtensionState.Settings.Extends, undefined, 'workspace', true); - Notifications.info('Cache cleared'); + if (showNotification) { + Notifications.info('Cache cleared'); + } } } diff --git a/src/constants/ExtensionState.ts b/src/constants/ExtensionState.ts index 71ea4bf5..d3ae6c70 100644 --- a/src/constants/ExtensionState.ts +++ b/src/constants/ExtensionState.ts @@ -5,6 +5,10 @@ export const ExtensionState = { SettingPromoted: `frontMatter:Settings:Promoted`, MoveTemplatesFolder: `frontMatter:Templates:Move`, + Project: { + current: `frontMatter:Project:current` + }, + Dashboard: { Contents: { Sorting: `frontMatter:Dashboard:Contents:Sorting` diff --git a/src/constants/settings.ts b/src/constants/settings.ts index 3b6005e3..0e641015 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -99,6 +99,11 @@ export const SETTING_GIT_SUBMODULE_FOLDER = 'git.submodule.folder'; */ export const SETTING_SPONSORS_AI_ENABLED = 'sponsors.ai.enabled'; +/** + * Project override support + */ +export const SETTING_PROJECTS = 'projects'; + /** * @deprecated */ diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index 66539608..a1427074 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -5,6 +5,9 @@ export enum DashboardMessage { getMode = 'getMode', showWarning = 'showWarning', + // Project switching + switchProject = 'switchProject', + // Welcome view initializeProject = 'initializeProject', setFramework = 'setFramework', diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index 9aa05c58..eb9148ea 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -28,6 +28,7 @@ import { PaginationStatus } from './PaginationStatus'; import useThemeColors from '../../hooks/useThemeColors'; import { Startup } from './Startup'; import { Navigation } from './Navigation'; +import { ProjectSwitcher } from './ProjectSwitcher'; export interface IHeaderProps { header?: React.ReactNode; @@ -146,12 +147,14 @@ export const Header: React.FunctionComponent = ({ `bg-[var(--vscode-editor-background)] text-[var(--vscode-editor-foreground)]` ) }`}> -
+ +
{location.pathname === routePaths.contents && ( diff --git a/src/dashboardWebView/components/Header/ProjectSwitcher.tsx b/src/dashboardWebView/components/Header/ProjectSwitcher.tsx new file mode 100644 index 00000000..2b637d68 --- /dev/null +++ b/src/dashboardWebView/components/Header/ProjectSwitcher.tsx @@ -0,0 +1,58 @@ +import { messageHandler } from '@estruyf/vscode/dist/client'; +import { Menu } from '@headlessui/react'; +import { GlobeAltIcon } from '@heroicons/react/outline'; +import * as React from 'react'; +import { useRecoilValue } from 'recoil'; +import { DashboardMessage } from '../../DashboardMessage'; +import { SettingsSelector } from '../../state'; +import { MenuButton, MenuItem, MenuItems } from '../Menu'; + +export interface IProjectSwitcherProps { } + +export const ProjectSwitcher: React.FunctionComponent = (props: React.PropsWithChildren) => { + const [crntProject, setCrntProject] = React.useState(undefined); + const settings = useRecoilValue(SettingsSelector); + + const project = settings?.project; + const projects = settings?.projects || []; + + const setProject = (value: string) => { + setCrntProject(value); + messageHandler.send(DashboardMessage.switchProject, value) + } + + React.useEffect(() => { + setCrntProject(project?.name); + }, [project]); + + if (projects.length <= 1 || !crntProject) { + return null; + } + + return ( +
+ + + + project +
+ )} + title={crntProject} /> + + + {projects.map((p) => ( + setProject(p.name)} + /> + ))} + + + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Menu/MenuButton.tsx b/src/dashboardWebView/components/Menu/MenuButton.tsx index 24d73d2d..4f0b52f7 100644 --- a/src/dashboardWebView/components/Menu/MenuButton.tsx +++ b/src/dashboardWebView/components/Menu/MenuButton.tsx @@ -15,28 +15,26 @@ export const MenuButton: React.FunctionComponent = ({ disabled }: React.PropsWithChildren) => { const { getColors } = useThemeColors(); - + return ( -
- {label}: +
+
{label}:
{title} diff --git a/src/dashboardWebView/models/Settings.ts b/src/dashboardWebView/models/Settings.ts index 5117f173..978885e6 100644 --- a/src/dashboardWebView/models/Settings.ts +++ b/src/dashboardWebView/models/Settings.ts @@ -8,6 +8,7 @@ import { DraftField, Framework, GitSettings, + Project, Snippets, SortingSetting } from '../../models'; @@ -16,6 +17,8 @@ import { DashboardViewType } from '.'; import { DataFile } from '../../models/DataFile'; export interface Settings { + projects: Project[]; + project: Project; git: GitSettings; beta: boolean; initialized: boolean; diff --git a/src/helpers/DashboardSettings.ts b/src/helpers/DashboardSettings.ts index 438a1196..83299da5 100644 --- a/src/helpers/DashboardSettings.ts +++ b/src/helpers/DashboardSettings.ts @@ -59,6 +59,8 @@ export class DashboardSettings { const pagination = Settings.get(SETTING_DASHBOARD_CONTENT_PAGINATION); const settings = { + projects: Settings.getProjects(), + project: Settings.getProject(), git: { isGitRepo: gitActions ? await GitListener.isGitRepository() : false, actions: gitActions || false diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 680c44d6..b1c922c1 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -1,10 +1,10 @@ -import { SETTING_EXTENSIBILITY_SCRIPTS } from './../constants/settings'; +import { SETTING_EXTENSIBILITY_SCRIPTS, SETTING_PROJECTS } from './../constants/settings'; import { parseWinPath } from './parseWinPath'; import { Telemetry } from './Telemetry'; import { Notifications } from './Notifications'; import { commands, Uri, workspace, window } from 'vscode'; import * as vscode from 'vscode'; -import { ContentType, CustomTaxonomy, TaxonomyType } from '../models'; +import { ContentType, CustomTaxonomy, Project, TaxonomyType } from '../models'; import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, @@ -53,10 +53,32 @@ export class Settings { private static listeners: any[] = []; private static fileCreationWatcher: vscode.FileSystemWatcher | undefined; private static readConfigPromise: Promise | undefined = undefined; + private static project: Project | undefined = undefined; public static async init() { await Settings.readConfig(); + const projects = Settings.getProjects(); + const crntProject = await Extension.getInstance().getState( + ExtensionState.Project.current, + 'workspace' + ); + + if (projects.length > 0) { + // Get the default project + const defaultProject = projects.find((p) => { + if (crntProject) { + return p.name === crntProject; + } + return p.default; + }); + if (defaultProject) { + Settings.project = defaultProject; + } else { + Settings.project = projects[0]; + } + } + Settings.listeners = []; if (!Settings.isInitialized) { @@ -72,6 +94,39 @@ export class Settings { }); } + /** + * Get the current project + * @returns + */ + public static getProject() { + return Settings.project; + } + + /** + * Set the project + * @param value + */ + public static setProject(value: string) { + Extension.getInstance().setState(ExtensionState.Project.current, value, 'workspace'); + Settings.project = Settings.getProjects().find((p) => p.name === value); + console.log('setProject', Settings.project); + } + + /** + * Fetch all the projects + * @returns + */ + public static getProjects(): Project[] { + const settingKey = `${CONFIG_KEY}.${SETTING_PROJECTS}`; + + let projects = []; + if (Settings.globalConfig && typeof Settings.globalConfig[settingKey] !== 'undefined') { + projects = Settings.globalConfig[settingKey]; + } + + return projects; + } + /** * Check if the setting is present in the workspace and ask to promote them to the global settings */ @@ -195,6 +250,16 @@ export class Settings { let setting = undefined; const settingKey = `${CONFIG_KEY}.${name}`; + if (Settings.project) { + if ( + typeof Settings.project.configuration !== 'undefined' && + typeof Settings.project.configuration[settingKey] !== 'undefined' + ) { + setting = Settings.project.configuration[settingKey]; + return setting; + } + } + if (Settings.globalConfig && typeof Settings.globalConfig[settingKey] !== 'undefined') { setting = Settings.globalConfig[settingKey]; } @@ -699,6 +764,10 @@ export class Settings { else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_TAXONOMY_CUSTOM)) { Settings.updateGlobalConfigArraySetting(SETTING_TAXONOMY_CUSTOM, 'id', configJson); } + // Projects + else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_PROJECTS)) { + Settings.updateGlobalConfigArraySetting(SETTING_PROJECTS, 'name', configJson); + } // Snippets else if ( Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_SNIPPETS) && diff --git a/src/listeners/dashboard/PagesListener.ts b/src/listeners/dashboard/PagesListener.ts index b831c891..408dd905 100644 --- a/src/listeners/dashboard/PagesListener.ts +++ b/src/listeners/dashboard/PagesListener.ts @@ -176,6 +176,7 @@ export class PagesListener extends BaseListener { this.sendMsg(DashboardCommand.searchReady, true); await this.createSearchIndex(pages); + this.sendMsg(DashboardCommand.loading, false); }); } diff --git a/src/listeners/dashboard/SettingsListener.ts b/src/listeners/dashboard/SettingsListener.ts index 10bbbe77..6deb003c 100644 --- a/src/listeners/dashboard/SettingsListener.ts +++ b/src/listeners/dashboard/SettingsListener.ts @@ -13,6 +13,13 @@ import { DashboardSettings, Settings } from '../../helpers'; import { FrameworkDetector } from '../../helpers/FrameworkDetector'; import { Framework, PostMessageData } from '../../models'; import { BaseListener } from './BaseListener'; +import { Cache } from '../../commands/Cache'; +import { Preview } from '../../commands'; +import { GitListener } from '../general'; +import { DataListener } from '../panel'; +import { MarkdownFoldingProvider } from '../../providers/MarkdownFoldingProvider'; +import { ModeSwitch } from '../../services/ModeSwitch'; +import { PagesListener } from './PagesListener'; export class SettingsListener extends BaseListener { /** @@ -35,6 +42,29 @@ export class SettingsListener extends BaseListener { case DashboardMessage.addFolder: this.addFolder(msg?.payload); break; + case DashboardMessage.switchProject: + this.switchProject(msg.payload); + break; + } + } + + private static async switchProject(project: string) { + if (project) { + this.sendMsg(DashboardCommand.loading, true); + Settings.setProject(project); + await Cache.clear(false); + + Preview.init(); + GitListener.init(); + + SettingsListener.getSettings(true); + DataListener.getFoldersAndFiles(); + MarkdownFoldingProvider.triggerHighlighting(true); + ModeSwitch.register(); + + // Update pages + PagesListener.startWatchers(); + PagesListener.refresh(); } } diff --git a/src/models/Project.ts b/src/models/Project.ts new file mode 100644 index 00000000..b722d459 --- /dev/null +++ b/src/models/Project.ts @@ -0,0 +1,5 @@ +export interface Project { + name: string; + default?: boolean; + configuration: any; +} diff --git a/src/models/index.ts b/src/models/index.ts index 2f825d57..e066fc88 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -16,6 +16,7 @@ export * from './MediaPaths'; export * from './Mode'; export * from './PanelSettings'; export * from './PostMessageData'; +export * from './Project'; export * from './Snippets'; export * from './SortOrder'; export * from './SortType';