From d45cd0d015eaeebbfed069fb63e035be8c071daf Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Fri, 19 Jan 2024 18:11:53 +0100 Subject: [PATCH 01/11] Git branching --- package.json | 291 ++++++++---------- src/constants/GeneralCommands.ts | 13 +- src/constants/settings.ts | 1 + .../components/Header/SyncButton.tsx | 6 +- src/dashboardWebView/models/Settings.ts | 2 +- src/helpers/DashboardSettings.ts | 6 +- src/helpers/PanelSettings.ts | 7 +- src/listeners/general/GitListener.ts | 163 +++++++++- src/localization/localization.enum.ts | 2 +- src/models/GitRepository.ts | 30 ++ src/models/GitSettings.ts | 1 + src/models/PanelSettings.ts | 2 +- src/models/index.ts | 1 + src/panelWebView/components/Git/GitAction.tsx | 54 +++- 14 files changed, 390 insertions(+), 189 deletions(-) create mode 100644 src/models/GitRepository.ts diff --git a/package.json b/package.json index 8957161e..bb9cf3a5 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,7 @@ "color": "#0e131f", "theme": "dark" }, - "badges": [ - { + "badges": [{ "description": "version", "url": "https://img.shields.io/github/package-json/v/estruyf/vscode-front-matter?color=green&label=vscode-front-matter&style=flat-square", "href": "https://github.com/estruyf/vscode-front-matter" @@ -71,8 +70,7 @@ "**/.frontmatter/config/*.json": "jsonc" } }, - "keybindings": [ - { + "keybindings": [{ "command": "frontMatter.dashboard", "key": "alt+d" }, @@ -90,23 +88,19 @@ } ], "viewsContainers": { - "activitybar": [ - { - "id": "frontmatter-explorer", - "title": "FM", - "icon": "$(fm-logo)" - } - ] + "activitybar": [{ + "id": "frontmatter-explorer", + "title": "FM", + "icon": "$(fm-logo)" + }] }, "views": { - "frontmatter-explorer": [ - { - "id": "frontMatter.explorer", - "name": "Front Matter", - "icon": "$(fm-logo)", - "type": "webview" - } - ] + "frontmatter-explorer": [{ + "id": "frontMatter.explorer", + "name": "Front Matter", + "icon": "$(fm-logo)", + "type": "webview" + }] }, "configuration": { "title": "%settings.configuration.title%", @@ -174,8 +168,7 @@ "frontMatter.content.defaultFileType": { "type": "string", "default": "md", - "oneOf": [ - { + "oneOf": [{ "enum": [ "md", "mdx" @@ -191,8 +184,7 @@ "frontMatter.content.defaultSorting": { "type": "string", "default": "", - "oneOf": [ - { + "oneOf": [{ "enum": [ "LastModifiedAsc", "LastModifiedDesc", @@ -544,8 +536,7 @@ "command": { "$id": "#scriptCommand", "type": "string", - "anyOf": [ - { + "anyOf": [{ "enum": [ "node", "bash", @@ -752,8 +743,7 @@ "title", "file" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -807,8 +797,7 @@ "id", "path" ], - "anyOf": [ - { + "anyOf": [{ "required": [ "schema" ] @@ -874,6 +863,14 @@ "markdownDescription": "%setting.frontMatter.git.commitMessage.markdownDescription%", "default": "Synced by Front Matter" }, + "frontMatter.git.disableOnBranches": { + "type": "array", + "markdownDescription": "%setting.frontMatter.git.disableOnBranches.markdownDescription%", + "default": [], + "items": { + "type": "string" + } + }, "frontMatter.git.submodule.pull": { "type": "boolean", "markdownDescription": "%setting.frontMatter.git.submodule.pull.markdownDescription%", @@ -1209,8 +1206,7 @@ "default": "", "description": "%setting.frontMatter.taxonomy.contentTypes.items.properties.fields.items.properties.taxonomyId.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1404,8 +1400,7 @@ "type", "name" ], - "allOf": [ - { + "allOf": [{ "if": { "properties": { "type": { @@ -1605,51 +1600,48 @@ "fields" ] }, - "default": [ - { - "name": "default", - "pageBundle": false, - "fields": [ - { - "title": "Title", - "name": "title", - "type": "string" - }, - { - "title": "Description", - "name": "description", - "type": "string" - }, - { - "title": "Publishing date", - "name": "date", - "type": "datetime", - "default": "{{now}}", - "isPublishDate": true - }, - { - "title": "Content preview", - "name": "preview", - "type": "image" - }, - { - "title": "Is in draft", - "name": "draft", - "type": "boolean" - }, - { - "title": "Tags", - "name": "tags", - "type": "tags" - }, - { - "title": "Categories", - "name": "categories", - "type": "categories" - } - ] - } - ], + "default": [{ + "name": "default", + "pageBundle": false, + "fields": [{ + "title": "Title", + "name": "title", + "type": "string" + }, + { + "title": "Description", + "name": "description", + "type": "string" + }, + { + "title": "Publishing date", + "name": "date", + "type": "datetime", + "default": "{{now}}", + "isPublishDate": true + }, + { + "title": "Content preview", + "name": "preview", + "type": "image" + }, + { + "title": "Is in draft", + "name": "draft", + "type": "boolean" + }, + { + "title": "Tags", + "name": "tags", + "type": "tags" + }, + { + "title": "Categories", + "name": "categories", + "type": "categories" + } + ] + }], "scope": "Taxonomy" }, "frontMatter.taxonomy.customTaxonomy": { @@ -1662,8 +1654,7 @@ "type": "string", "description": "%setting.frontMatter.taxonomy.customTaxonomy.items.properties.id.description%", "not": { - "anyOf": [ - { + "anyOf": [{ "const": "" }, { @@ -1855,8 +1846,7 @@ } } }, - "commands": [ - { + "commands": [{ "command": "frontMatter.project.switch", "title": "%command.frontMatter.project.switch%", "category": "Front Matter", @@ -2173,21 +2163,16 @@ "category": "Front Matter" } ], - "submenus": [ - { - "id": "frontmatter.submenu", - "label": "Front Matter" - } - ], + "submenus": [{ + "id": "frontmatter.submenu", + "label": "Front Matter" + }], "menus": { - "webview/context": [ - { - "command": "workbench.action.webview.openDeveloperTools", - "when": "frontMatter:isDevelopment" - } - ], - "editor/title": [ - { + "webview/context": [{ + "command": "workbench.action.webview.openDeveloperTools", + "when": "frontMatter:isDevelopment" + }], + "editor/title": [{ "command": "frontMatter.markup.heading", "group": "navigation@-133", "when": "frontMatter:file:isValid == true && frontMatter:markdown:wysiwyg" @@ -2268,14 +2253,11 @@ "when": "resourceFilename == 'frontmatter.json'" } ], - "explorer/context": [ - { - "submenu": "frontmatter.submenu", - "group": "frontmatter@1" - } - ], - "frontmatter.submenu": [ - { + "explorer/context": [{ + "submenu": "frontmatter.submenu", + "group": "frontmatter@1" + }], + "frontmatter.submenu": [{ "command": "frontMatter.createFromTemplate", "when": "explorerResourceIsFolder", "group": "frontmatter@1" @@ -2291,8 +2273,7 @@ "group": "frontmatter@3" } ], - "commandPalette": [ - { + "commandPalette": [{ "command": "frontMatter.init", "when": "frontMatterCanInit" }, @@ -2441,8 +2422,7 @@ "when": "frontMatter:file:isValid == true" } ], - "view/title": [ - { + "view/title": [{ "command": "frontMatter.chatbot", "group": "navigation@0", "when": "view == frontMatter.explorer" @@ -2474,57 +2454,52 @@ } ] }, - "grammars": [ - { - "path": "./syntaxes/hugo.tmLanguage.json", - "scopeName": "frontmatter.markdown.hugo", - "injectTo": [ - "text.html.markdown" - ] - } - ], - "walkthroughs": [ - { - "id": "frontmatter.welcome", - "title": "Get started with Front Matter", - "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", - "steps": [ - { - "id": "frontmatter.welcome.init", - "title": "Get started", - "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", - "media": { - "markdown": "assets/walkthrough/get-started.md" - }, - "completionEvents": [ - "onContext:frontMatterInitialized" - ] + "grammars": [{ + "path": "./syntaxes/hugo.tmLanguage.json", + "scopeName": "frontmatter.markdown.hugo", + "injectTo": [ + "text.html.markdown" + ] + }], + "walkthroughs": [{ + "id": "frontmatter.welcome", + "title": "Get started with Front Matter", + "description": "Discover the features of Front Matter and learn how to use the CMS for your SSG or static site.", + "steps": [{ + "id": "frontmatter.welcome.init", + "title": "Get started", + "description": "Initial steps to get started.\n[Open dashboard](command:frontMatter.dashboard)", + "media": { + "markdown": "assets/walkthrough/get-started.md" }, - { - "id": "frontmatter.welcome.documentation", - "title": "Documentation", - "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", - "media": { - "markdown": "assets/walkthrough/documentation.md" - }, - "completionEvents": [ - "onLink:https://frontmatter.codes/docs" - ] + "completionEvents": [ + "onContext:frontMatterInitialized" + ] + }, + { + "id": "frontmatter.welcome.documentation", + "title": "Documentation", + "description": "Check out the documentation for Front Matter.\n[View our documentation](https://frontmatter.codes/docs)", + "media": { + "markdown": "assets/walkthrough/documentation.md" }, - { - "id": "frontmatter.welcome.supporter", - "title": "Support the project", - "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", - "media": { - "markdown": "assets/walkthrough/support-the-project.md" - }, - "completionEvents": [ - "onLink:https://github.com/sponsors/estruyf" - ] - } - ] - } - ] + "completionEvents": [ + "onLink:https://frontmatter.codes/docs" + ] + }, + { + "id": "frontmatter.welcome.supporter", + "title": "Support the project", + "description": "Become a supporter.\n[Support the project](https://github.com/sponsors/estruyf)", + "media": { + "markdown": "assets/walkthrough/support-the-project.md" + }, + "completionEvents": [ + "onLink:https://github.com/sponsors/estruyf" + ] + } + ] + }] }, "scripts": { "dev:ext": "npm run clean && npm run localization:generate && npm-run-all --parallel watch:*", @@ -2661,4 +2636,4 @@ "vsce": { "dependencies": false } -} +} \ No newline at end of file diff --git a/src/constants/GeneralCommands.ts b/src/constants/GeneralCommands.ts index ccb77129..0087cccf 100644 --- a/src/constants/GeneralCommands.ts +++ b/src/constants/GeneralCommands.ts @@ -1,13 +1,20 @@ export const GeneralCommands = { toWebview: { setMode: 'setMode', - gitSyncingStart: 'gitSyncingStart', - gitSyncingEnd: 'gitSyncingEnd', + git: { + syncingStart: 'gitSyncingStart', + syncingEnd: 'gitSyncingEnd', + branchInfo: 'gitBranchInfo' + }, setLocalization: 'setLocalization' }, toVSCode: { openLink: 'openLink', - gitSync: 'gitSync', + git: { + sync: 'gitSync', + getBranch: 'getBranch', + selectBranch: 'gitSelectBranch' + }, getLocalization: 'getLocalization', openOnWebsite: 'openOnWebsite' } diff --git a/src/constants/settings.ts b/src/constants/settings.ts index ff9b7398..328c57b5 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -98,6 +98,7 @@ export const SETTING_FRAMEWORK_START = 'framework.startCommand'; export const SETTING_SITE_BASEURL = 'site.baseURL'; export const SETTING_GIT_ENABLED = 'git.enabled'; +export const SETTING_GIT_DISABLED_BRANCHES = 'git.disableOnBranches'; export const SETTING_GIT_COMMIT_MSG = 'git.commitMessage'; export const SETTING_GIT_SUBMODULE_PULL = 'git.submodule.pull'; export const SETTING_GIT_SUBMODULE_PUSH = 'git.submodule.push'; diff --git a/src/dashboardWebView/components/Header/SyncButton.tsx b/src/dashboardWebView/components/Header/SyncButton.tsx index 791132cf..9df81574 100644 --- a/src/dashboardWebView/components/Header/SyncButton.tsx +++ b/src/dashboardWebView/components/Header/SyncButton.tsx @@ -20,15 +20,15 @@ export const SyncButton: React.FunctionComponent = ( const { getColors } = useThemeColors(); const pull = () => { - Messenger.send(GeneralCommands.toVSCode.gitSync); + Messenger.send(GeneralCommands.toVSCode.git.sync); }; const messageListener = (message: MessageEvent>) => { const { command } = message.data; - if (command === GeneralCommands.toWebview.gitSyncingStart) { + if (command === GeneralCommands.toWebview.git.syncingStart) { setIsSyncing(true); - } else if (command === GeneralCommands.toWebview.gitSyncingEnd) { + } else if (command === GeneralCommands.toWebview.git.syncingEnd) { setIsSyncing(false); } }; diff --git a/src/dashboardWebView/models/Settings.ts b/src/dashboardWebView/models/Settings.ts index f5ff7b8d..14194fb3 100644 --- a/src/dashboardWebView/models/Settings.ts +++ b/src/dashboardWebView/models/Settings.ts @@ -19,7 +19,7 @@ import { DataFile } from '../../models/DataFile'; export interface Settings { projects: Project[]; project: Project; - git: GitSettings; + git: GitSettings | undefined; beta: boolean; initialized: boolean; wsFolder: string; diff --git a/src/helpers/DashboardSettings.ts b/src/helpers/DashboardSettings.ts index fc277e2c..1f7ca5e0 100644 --- a/src/helpers/DashboardSettings.ts +++ b/src/helpers/DashboardSettings.ts @@ -79,16 +79,12 @@ export class DashboardSettings { const ext = Extension.getInstance(); const wsFolder = Folders.getWorkspaceFolder(); const isInitialized = await Project.isInitialized(); - const gitActions = Settings.get(SETTING_GIT_ENABLED); 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 - }, + git: await GitListener.getSettings(), beta: ext.isBetaVersion(), wsFolder: wsFolder ? wsFolder.fsPath : '', staticFolder: Folders.getStaticFolderRelativePath(), diff --git a/src/helpers/PanelSettings.ts b/src/helpers/PanelSettings.ts index 121b4760..f022bbb0 100644 --- a/src/helpers/PanelSettings.ts +++ b/src/helpers/PanelSettings.ts @@ -49,14 +49,9 @@ import { Folders } from '../commands'; export class PanelSettings { public static async get(): Promise { - const gitActions = Settings.get(SETTING_GIT_ENABLED); - return { aiEnabled: Settings.get(SETTING_SPONSORS_AI_ENABLED) || false, - git: { - isGitRepo: gitActions ? await GitListener.isGitRepository() : false, - actions: gitActions || false - }, + git: await GitListener.getSettings(), seo: { title: (Settings.get(SETTING_SEO_TITLE_LENGTH) as number) || -1, slug: (Settings.get(SETTING_SEO_SLUG_LENGTH) as number) || -1, diff --git a/src/listeners/general/GitListener.ts b/src/listeners/general/GitListener.ts index e4e76537..6b9eabae 100644 --- a/src/listeners/general/GitListener.ts +++ b/src/listeners/general/GitListener.ts @@ -1,4 +1,5 @@ import { + SETTING_GIT_DISABLED_BRANCHES, SETTING_GIT_SUBMODULE_BRANCH, SETTING_GIT_SUBMODULE_FOLDER, SETTING_GIT_SUBMODULE_PULL, @@ -12,6 +13,7 @@ import { Extension, Logger, Notifications, + parseWinPath, processKnownPlaceholders, Telemetry } from '../../helpers'; @@ -26,15 +28,38 @@ import { TelemetryEvent } from '../../constants'; import { Folders } from '../../commands/Folders'; -import { commands } from 'vscode'; -import { PostMessageData } from '../../models'; +import { commands, extensions } from 'vscode'; +import { GitRepository, GitRepositoryState, PostMessageData } from '../../models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; export class GitListener { + private static gitAPI: { + onDidChangeState: (repo: any) => void; + onDidOpenRepository: (repo: any) => void; + onDidCloseRepository: (repo: any) => void; + getAPI: (version: number) => any; + repositories: GitRepository[]; + } | null = null; private static isRegistered: boolean = false; private static client: SimpleGit | null = null; private static subClient: SimpleGit | null = null; + private static repository: GitRepository | null = null; + + public static async getSettings() { + const gitActions = Settings.get(SETTING_GIT_ENABLED); + if (gitActions) { + return { + isGitRepo: gitActions ? await GitListener.isGitRepository() : false, + actions: gitActions || false, + disabledBranches: gitActions + ? Settings.get(SETTING_GIT_DISABLED_BRANCHES) || [] + : [] + }; + } + + return; + } /** * Initialize the listener @@ -65,28 +90,39 @@ export class GitListener { */ public static process(msg: PostMessageData) { switch (msg.command) { - case GeneralCommands.toVSCode.gitSync: + case GeneralCommands.toVSCode.git.sync: this.sync(); break; + case GeneralCommands.toVSCode.git.getBranch: + this.getBranch(msg.command, msg.requestId); + break; + case GeneralCommands.toVSCode.git.selectBranch: + this.selectBranch(); + break; } } + public static async selectBranch() { + const workspaceFolder = Folders.getWorkspaceFolder(); + await commands.executeCommand('git.checkout', workspaceFolder); + } + /** * Run the sync */ public static async sync() { try { - this.sendMsg(GeneralCommands.toWebview.gitSyncingStart, {}); + this.sendMsg(GeneralCommands.toWebview.git.syncingStart, {}); Telemetry.send(TelemetryEvent.gitSync); await this.pull(); await this.push(); - this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {}); + this.sendMsg(GeneralCommands.toWebview.git.syncingEnd, {}); } catch (e) { Logger.error((e as Error).message); - this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {}); + this.sendMsg(GeneralCommands.toWebview.git.syncingEnd, {}); } } @@ -106,6 +142,8 @@ export class GitListener { Logger.warning(`Current workspace is not a GIT repository`); } + GitListener.vscodeGitProvider(); + return isRepo; } @@ -257,6 +295,100 @@ export class GitListener { } } + private static async vscodeGitProvider() { + if (!GitListener.gitAPI) { + const wsFolder = Folders.getWorkspaceFolder(); + const extension = extensions.getExtension('vscode.git'); + + /** + * Logic from: https://github.com/microsoft/vscode/blob/main/extensions/github/src/extension.ts + * initializeGitExtension + */ + if (wsFolder && extension) { + const gitExtension = extension.isActive ? extension.exports : await extension.activate(); + + // Get version 1 of the API + GitListener.gitAPI = gitExtension.getAPI(1); + + if (!GitListener.gitAPI) { + return; + } + + GitListener.listenToRepo(GitListener.gitAPI?.repositories); + + GitListener.gitAPI.onDidChangeState(() => { + GitListener.listenToRepo(GitListener.gitAPI?.repositories); + }); + + GitListener.gitAPI?.onDidOpenRepository((repo: GitRepository) => { + GitListener.triggerBranchChange(repo); + + repo.state.onDidChange(() => { + GitListener.triggerBranchChange(repo); + }); + }); + + GitListener.gitAPI?.onDidCloseRepository((repo: any) => { + console.log(`Closed repo:`, repo); + }); + } + } + } + + private static async getBranch(command: string, requestId?: string) { + if (!command || !requestId) { + return; + } + + this.sendRequest(command, requestId, GitListener.repository?.state?.HEAD.name); + } + + private static listenToRepo(repositories: GitRepository[] | undefined) { + if (!repositories) { + return; + } + + if (repositories && repositories.length === 1) { + GitListener.repository = repositories[0]; + } else if (repositories && repositories.length > 1) { + const wsFolder = Folders.getWorkspaceFolder(); + if (wsFolder) { + const repo = repositories.find( + (repo) => parseWinPath(repo.rootUri.fsPath) === parseWinPath(wsFolder.fsPath) + ); + if (repo) { + GitListener.repository = repo; + } + } + } + + GitListener.repository?.state?.onDidChange(() => { + GitListener.triggerBranchChange(GitListener.repository); + }); + } + + /** + * Trigger the branch change + * @param repo + */ + private static async triggerBranchChange(repo: GitRepository | null) { + if (repo && repo.state) { + GitListener.repository = repo; + let branches = []; + + if (repo.repository.getBranches) { + const allBranches = await repo.repository.getBranches(); + if (allBranches && allBranches.length > 0) { + branches = allBranches.map((branch: any) => branch.name); + } + } + this.sendMsg(GeneralCommands.toWebview.git.branchInfo, { + crntBranch: GitListener.repository?.state?.HEAD.name, + branches + }); + } + } + /** * Send the message to the webview * @param command @@ -270,4 +402,23 @@ export class GitListener { Dashboard.postWebviewMessage({ command: command as any, payload }); } + + /** + * Sends a request to the webview panel. + * @param command - The command to send. + * @param requestId - The unique identifier for the request. + * @param payload - The payload to send with the request. + */ + private static sendRequest(command: string, requestId: string, payload: any) { + const extPath = Extension.getInstance().extensionPath; + const panel = PanelProvider.getInstance(extPath); + + panel.getWebview()?.postMessage({ + command, + requestId, + payload + }); + + Dashboard.postWebviewMessage({ command: command as any, requestId, payload }); + } } diff --git a/src/localization/localization.enum.ts b/src/localization/localization.enum.ts index 452cd2eb..54e508de 100644 --- a/src/localization/localization.enum.ts +++ b/src/localization/localization.enum.ts @@ -700,7 +700,7 @@ export enum LocalizationKey { */ dashboardMediaMediaFolderDefault = 'dashboard.media.media.folder.default', /** - * No media files to show. You can drag & drop new files by holding your [shift] key. + * No media files to show. You can drag&drop new files by holding your [shift] key. */ dashboardMediaMediaPlaceholder = 'dashboard.media.media.placeholder', /** diff --git a/src/models/GitRepository.ts b/src/models/GitRepository.ts new file mode 100644 index 00000000..178705a9 --- /dev/null +++ b/src/models/GitRepository.ts @@ -0,0 +1,30 @@ +export interface GitRepository { + state: GitRepositoryState; + rootUri: { + fsPath: string; + path: string; + }; + repository: { + getBranches: () => Promise; + }; +} + +export interface GitRepositoryState { + HEAD: GitBranch; + onDidChange: (listener: () => void) => void; +} + +export interface GitBranch { + type: number; + name: string; + upstream: Upstream; + commit: string; + ahead: number; + behind: number; +} + +export interface Upstream { + name: string; + remote: string; + commit: string; +} diff --git a/src/models/GitSettings.ts b/src/models/GitSettings.ts index e452ec54..e29a149b 100644 --- a/src/models/GitSettings.ts +++ b/src/models/GitSettings.ts @@ -1,4 +1,5 @@ export interface GitSettings { isGitRepo: boolean; actions: boolean; + disabledBranches: string[]; } diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 7b6bd2c9..f6839bb2 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -5,7 +5,7 @@ import { DashboardData } from './DashboardData'; import { DataType } from './DataType'; export interface PanelSettings { - git: GitSettings; + git: GitSettings | undefined; seo: SEO; slug: Slug; tags: string[]; diff --git a/src/models/index.ts b/src/models/index.ts index 68886aa7..2104d824 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -12,6 +12,7 @@ export * from './DataFolder'; export * from './DataType'; export * from './DraftField'; export * from './Framework'; +export * from './GitRepository'; export * from './GitSettings'; export * from './MediaPaths'; export * from './Mode'; diff --git a/src/panelWebView/components/Git/GitAction.tsx b/src/panelWebView/components/Git/GitAction.tsx index 1c6d4242..53ba5164 100644 --- a/src/panelWebView/components/Git/GitAction.tsx +++ b/src/panelWebView/components/Git/GitAction.tsx @@ -1,4 +1,4 @@ -import { Messenger } from '@estruyf/vscode/dist/client'; +import { Messenger, messageHandler } from '@estruyf/vscode/dist/client'; import { EventData } from '@estruyf/vscode/dist/models'; import { ArrowPathIcon } from '@heroicons/react/24/outline'; import * as React from 'react'; @@ -16,22 +16,43 @@ export interface IGitActionProps { export const GitAction: React.FunctionComponent = ({ settings }: React.PropsWithChildren) => { + const [crntBanch, setCrntBranch] = useState(undefined); + const [branches, setBranches] = useState(undefined); const [isSyncing, setIsSyncing] = useState(false); const pull = () => { - Messenger.send(GeneralCommands.toVSCode.gitSync); + Messenger.send(GeneralCommands.toVSCode.git.sync); }; + const selectBranch = () => { + messageHandler.send(GeneralCommands.toVSCode.git.selectBranch) + } + const messageListener = (message: MessageEvent>) => { - const { command } = message.data; + const { command, payload } = message.data; - if (command === GeneralCommands.toWebview.gitSyncingStart) { + if (command === GeneralCommands.toWebview.git.syncingStart) { setIsSyncing(true); - } else if (command === GeneralCommands.toWebview.gitSyncingEnd) { + } else if (command === GeneralCommands.toWebview.git.syncingEnd) { setIsSyncing(false); + } else if (command === GeneralCommands.toWebview.git.branchInfo) { + setCrntBranch(payload.crntBranch || undefined); + setBranches(payload.branches || undefined); } }; + const isSyncDisabled = React.useMemo(() => { + if (!settings?.git?.disabledBranches || settings.git.disabledBranches.length === 0) { + return false; + } + + if (!crntBanch) { + return true; + } + + return settings.git.disabledBranches.includes(crntBanch); + }, [settings?.git?.disabledBranches, crntBanch]) + useEffect(() => { Messenger.listen(messageListener); @@ -40,13 +61,36 @@ export const GitAction: React.FunctionComponent = ({ }; }, []); + useEffect(() => { + messageHandler.request(GeneralCommands.toVSCode.git.getBranch).then((branch) => { + setCrntBranch(branch); + }); + }, []); + if (!settings?.git?.actions || !settings?.git.isGitRepo) { return null; } return (
+

+ + Git Actions + + + +

+ From 6150a3454794afba24bd5e25fc56e721db41788f Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Sun, 21 Jan 2024 17:27:56 +0100 Subject: [PATCH 02/11] Update git API --- src/listeners/general/GitListener.ts | 54 ++++++++++++++-------------- src/models/GitRepository.ts | 6 +++- 2 files changed, 31 insertions(+), 29 deletions(-) diff --git a/src/listeners/general/GitListener.ts b/src/listeners/general/GitListener.ts index 6b9eabae..3379fb68 100644 --- a/src/listeners/general/GitListener.ts +++ b/src/listeners/general/GitListener.ts @@ -28,16 +28,16 @@ import { TelemetryEvent } from '../../constants'; import { Folders } from '../../commands/Folders'; -import { commands, extensions } from 'vscode'; -import { GitRepository, GitRepositoryState, PostMessageData } from '../../models'; +import { Event, commands, extensions } from 'vscode'; +import { GitAPIState, GitRepository, PostMessageData } from '../../models'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../localization'; export class GitListener { private static gitAPI: { - onDidChangeState: (repo: any) => void; - onDidOpenRepository: (repo: any) => void; - onDidCloseRepository: (repo: any) => void; + onDidChangeState: Event; + onDidOpenRepository: Event; + onDidCloseRepository: Event; getAPI: (version: number) => any; repositories: GitRepository[]; } | null = null; @@ -320,16 +320,12 @@ export class GitListener { GitListener.listenToRepo(GitListener.gitAPI?.repositories); }); - GitListener.gitAPI?.onDidOpenRepository((repo: GitRepository) => { + GitListener.gitAPI.onDidOpenRepository((repo: GitRepository) => { GitListener.triggerBranchChange(repo); - - repo.state.onDidChange(() => { - GitListener.triggerBranchChange(repo); - }); }); - GitListener.gitAPI?.onDidCloseRepository((repo: any) => { - console.log(`Closed repo:`, repo); + GitListener.gitAPI.onDidCloseRepository((repo: GitRepository) => { + Logger.info(`Closed repo: ${repo?.state?.HEAD?.name}`); }); } } @@ -349,7 +345,7 @@ export class GitListener { } if (repositories && repositories.length === 1) { - GitListener.repository = repositories[0]; + GitListener.triggerBranchChange(repositories[0]); } else if (repositories && repositories.length > 1) { const wsFolder = Folders.getWorkspaceFolder(); if (wsFolder) { @@ -357,14 +353,10 @@ export class GitListener { (repo) => parseWinPath(repo.rootUri.fsPath) === parseWinPath(wsFolder.fsPath) ); if (repo) { - GitListener.repository = repo; + GitListener.triggerBranchChange(repo); } } } - - GitListener.repository?.state?.onDidChange(() => { - GitListener.triggerBranchChange(GitListener.repository); - }); } /** @@ -373,19 +365,25 @@ export class GitListener { */ private static async triggerBranchChange(repo: GitRepository | null) { if (repo && repo.state) { - GitListener.repository = repo; - let branches = []; + if (repo.state.HEAD.name !== GitListener.repository?.state?.HEAD.name) { + GitListener.repository = repo; + let branches = []; - if (repo.repository.getBranches) { - const allBranches = await repo.repository.getBranches(); - if (allBranches && allBranches.length > 0) { - branches = allBranches.map((branch: any) => branch.name); + if (repo.repository.getBranches) { + const allBranches = await repo.repository.getBranches(); + if (allBranches && allBranches.length > 0) { + branches = allBranches.map((branch: any) => branch.name); + } } + this.sendMsg(GeneralCommands.toWebview.git.branchInfo, { + crntBranch: GitListener.repository?.state?.HEAD.name, + branches + }); + + repo.state.onDidChange(() => { + GitListener.triggerBranchChange(repo); + }); } - this.sendMsg(GeneralCommands.toWebview.git.branchInfo, { - crntBranch: GitListener.repository?.state?.HEAD.name, - branches - }); } } diff --git a/src/models/GitRepository.ts b/src/models/GitRepository.ts index 178705a9..0cb40ddc 100644 --- a/src/models/GitRepository.ts +++ b/src/models/GitRepository.ts @@ -1,3 +1,7 @@ +import { Event } from 'vscode'; + +export type GitAPIState = 'uninitialized' | 'initialized'; + export interface GitRepository { state: GitRepositoryState; rootUri: { @@ -11,7 +15,7 @@ export interface GitRepository { export interface GitRepositoryState { HEAD: GitBranch; - onDidChange: (listener: () => void) => void; + onDidChange: Event; } export interface GitBranch { From cf96923d96fb99a71736461a1c0afbe4e6529fbb Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Sun, 21 Jan 2024 17:33:58 +0100 Subject: [PATCH 03/11] Faster loading for panel --- src/panelWebView/ViewPanel.tsx | 51 +++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/panelWebView/ViewPanel.tsx b/src/panelWebView/ViewPanel.tsx index de78eb23..2b32ae52 100644 --- a/src/panelWebView/ViewPanel.tsx +++ b/src/panelWebView/ViewPanel.tsx @@ -83,7 +83,7 @@ export const ViewPanel: React.FunctionComponent = ( ); } - if (loading || !localeReady) { + if (loading && !localeReady) { return ; } @@ -116,37 +116,44 @@ export const ViewPanel: React.FunctionComponent = (
- + {!loading && ()} - {settings && settings.seo && ( - - - - )} - {settings && metadata && ( + { + !loading && settings && settings.seo && ( + + + + ) + } + + {!loading && settings && metadata && ( )} - - - + { + !loading && ( + + + + ) + } From d22ebfa6ce9504a3e289fc86179ff042b762d5f6 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 22 Jan 2024 11:45:11 +0100 Subject: [PATCH 04/11] Commit message input --- package.json | 8 ++ src/constants/GeneralCommands.ts | 3 +- src/constants/TelemetryEvent.ts | 3 +- src/constants/settings.ts | 1 + .../components/Header/Header.tsx | 2 +- src/helpers/PanelSettings.ts | 1 - src/listeners/general/GitListener.ts | 55 ++++---- src/models/GitSettings.ts | 1 + src/panelWebView/components/ActionButton.tsx | 2 +- .../components/Fields/TextField.tsx | 4 + src/panelWebView/components/Git/GitAction.tsx | 131 +++++++++++++----- .../components/Icons/BranchIcon.tsx | 15 ++ src/panelWebView/styles.css | 2 + 13 files changed, 163 insertions(+), 65 deletions(-) create mode 100644 src/panelWebView/components/Icons/BranchIcon.tsx diff --git a/package.json b/package.json index bb9cf3a5..9fc98088 100644 --- a/package.json +++ b/package.json @@ -871,6 +871,14 @@ "type": "string" } }, + "frontMatter.git.requiresCommitMessage": { + "type": "array", + "markdownDescription": "%setting.frontMatter.git.requiresCommitMessage.markdownDescription%", + "default": [], + "items": { + "type": "string" + } + }, "frontMatter.git.submodule.pull": { "type": "boolean", "markdownDescription": "%setting.frontMatter.git.submodule.pull.markdownDescription%", diff --git a/src/constants/GeneralCommands.ts b/src/constants/GeneralCommands.ts index 0087cccf..3deef171 100644 --- a/src/constants/GeneralCommands.ts +++ b/src/constants/GeneralCommands.ts @@ -4,7 +4,7 @@ export const GeneralCommands = { git: { syncingStart: 'gitSyncingStart', syncingEnd: 'gitSyncingEnd', - branchInfo: 'gitBranchInfo' + branchName: 'gitBranchName' }, setLocalization: 'setLocalization' }, @@ -12,6 +12,7 @@ export const GeneralCommands = { openLink: 'openLink', git: { sync: 'gitSync', + fetch: 'getFetch', getBranch: 'getBranch', selectBranch: 'gitSelectBranch' }, diff --git a/src/constants/TelemetryEvent.ts b/src/constants/TelemetryEvent.ts index e4a1dbc1..f717da6f 100644 --- a/src/constants/TelemetryEvent.ts +++ b/src/constants/TelemetryEvent.ts @@ -49,5 +49,6 @@ export const TelemetryEvent = { webviewTaxonomyDashboard: 'webviewTaxonomyDashboard', // Git - gitSync: 'gitSync' + gitSync: 'gitSync', + gitFetch: 'gitFetch' }; diff --git a/src/constants/settings.ts b/src/constants/settings.ts index 328c57b5..33d592cb 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -99,6 +99,7 @@ export const SETTING_SITE_BASEURL = 'site.baseURL'; export const SETTING_GIT_ENABLED = 'git.enabled'; export const SETTING_GIT_DISABLED_BRANCHES = 'git.disableOnBranches'; +export const SETTING_GIT_REQUIRES_COMMIT_MSG = 'git.requiresCommitMessage'; export const SETTING_GIT_COMMIT_MSG = 'git.commitMessage'; export const SETTING_GIT_SUBMODULE_PULL = 'git.submodule.pull'; export const SETTING_GIT_SUBMODULE_PUSH = 'git.submodule.push'; diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index bd606d42..38689280 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -173,7 +173,7 @@ export const Header: React.FunctionComponent = ({
- + {/* */} (SETTING_GIT_ENABLED); @@ -54,6 +56,9 @@ export class GitListener { actions: gitActions || false, disabledBranches: gitActions ? Settings.get(SETTING_GIT_DISABLED_BRANCHES) || [] + : [], + requiresCommitMessage: gitActions + ? Settings.get(SETTING_GIT_REQUIRES_COMMIT_MSG) || [] : [] }; } @@ -91,7 +96,10 @@ export class GitListener { public static process(msg: PostMessageData) { switch (msg.command) { case GeneralCommands.toVSCode.git.sync: - this.sync(); + this.sync(msg.payload); + break; + case GeneralCommands.toVSCode.git.fetch: + this.sync(undefined, false); break; case GeneralCommands.toVSCode.git.getBranch: this.getBranch(msg.command, msg.requestId); @@ -108,16 +116,19 @@ export class GitListener { } /** - * Run the sync + * Run the sync/fetch */ - public static async sync() { + public static async sync(commitMsg?: string, isSync: boolean = true) { try { - this.sendMsg(GeneralCommands.toWebview.git.syncingStart, {}); + this.sendMsg(GeneralCommands.toWebview.git.syncingStart, isSync ? 'syncing' : 'fetching'); - Telemetry.send(TelemetryEvent.gitSync); + Telemetry.send(isSync ? TelemetryEvent.gitSync : TelemetryEvent.gitFetch); await this.pull(); - await this.push(); + + if (isSync) { + await this.push(commitMsg); + } this.sendMsg(GeneralCommands.toWebview.git.syncingEnd, {}); } catch (e) { @@ -187,8 +198,9 @@ export class GitListener { * Push the changes to the remote * @returns */ - private static async push() { - let commitMsg = Settings.get(SETTING_GIT_COMMIT_MSG); + private static async push(commitMsg?: string) { + commitMsg = + commitMsg || Settings.get(SETTING_GIT_COMMIT_MSG) || 'Synced by Front Matter'; if (commitMsg) { const dateFormat = Settings.get(SETTING_DATE_FORMAT) as string; @@ -213,7 +225,7 @@ export class GitListener { // Check if anything changed if (status.files.length > 0) { await subGit.raw(['add', '.', '-A']); - await subGit.commit(commitMsg || 'Synced by Front Matter'); + await subGit.commit(commitMsg); } await subGit.push(); } catch (e) { @@ -235,13 +247,7 @@ export class GitListener { // First line is the submodule folder name if (lines.length > 1) { await git.subModule(['foreach', 'git', 'add', '.', '-A']); - await git.subModule([ - 'foreach', - 'git', - 'commit', - '-m', - commitMsg || 'Synced by Front Matter' - ]); + await git.subModule(['foreach', 'git', 'commit', '-m', commitMsg]); await git.subModule(['foreach', 'git', 'push']); } } catch (e) { @@ -260,7 +266,7 @@ export class GitListener { if (status.files.length > 0) { await git.raw(['add', '.', '-A']); - await git.commit(commitMsg || 'Synced by Front Matter'); + await git.commit(commitMsg); } await git.push(); @@ -365,20 +371,11 @@ export class GitListener { */ private static async triggerBranchChange(repo: GitRepository | null) { if (repo && repo.state) { - if (repo.state.HEAD.name !== GitListener.repository?.state?.HEAD.name) { + if (repo.state.HEAD.name !== GitListener.branchName) { + GitListener.branchName = repo.state.HEAD.name; GitListener.repository = repo; - let branches = []; - if (repo.repository.getBranches) { - const allBranches = await repo.repository.getBranches(); - if (allBranches && allBranches.length > 0) { - branches = allBranches.map((branch: any) => branch.name); - } - } - this.sendMsg(GeneralCommands.toWebview.git.branchInfo, { - crntBranch: GitListener.repository?.state?.HEAD.name, - branches - }); + this.sendMsg(GeneralCommands.toWebview.git.branchName, GitListener.branchName); repo.state.onDidChange(() => { GitListener.triggerBranchChange(repo); diff --git a/src/models/GitSettings.ts b/src/models/GitSettings.ts index e29a149b..f32c71d5 100644 --- a/src/models/GitSettings.ts +++ b/src/models/GitSettings.ts @@ -2,4 +2,5 @@ export interface GitSettings { isGitRepo: boolean; actions: boolean; disabledBranches: string[]; + requiresCommitMessage: string[]; } diff --git a/src/panelWebView/components/ActionButton.tsx b/src/panelWebView/components/ActionButton.tsx index 9acf2eef..71e7ee73 100644 --- a/src/panelWebView/components/ActionButton.tsx +++ b/src/panelWebView/components/ActionButton.tsx @@ -14,7 +14,7 @@ const ActionButton: React.FunctionComponent = ({ title }: React.PropsWithChildren) => { return ( -
+
diff --git a/src/panelWebView/components/Fields/TextField.tsx b/src/panelWebView/components/Fields/TextField.tsx index f55651f9..097228a4 100644 --- a/src/panelWebView/components/Fields/TextField.tsx +++ b/src/panelWebView/components/Fields/TextField.tsx @@ -20,6 +20,7 @@ export interface ITextFieldProps extends BaseFieldProps { limit: number | undefined; rows?: number; name: string; + placeholder?: string; settings: PanelSettings; onChange: (txtValue: string) => void; } @@ -27,6 +28,7 @@ export interface ITextFieldProps extends BaseFieldProps { const WysiwygField = React.lazy(() => import('./WysiwygField')); export const TextField: React.FunctionComponent = ({ + placeholder, singleLine, wysiwyg, limit, @@ -154,6 +156,7 @@ export const TextField: React.FunctionComponent = ({ className={`metadata_field__input`} value={text || ''} onChange={(e) => onTextChange(e.currentTarget.value)} + placeholder={placeholder} style={{ border }} @@ -164,6 +167,7 @@ export const TextField: React.FunctionComponent = ({ rows={rows || 2} value={text || ''} onChange={(e) => onTextChange(e.currentTarget.value)} + placeholder={placeholder} style={{ border }} diff --git a/src/panelWebView/components/Git/GitAction.tsx b/src/panelWebView/components/Git/GitAction.tsx index 53ba5164..1e3cefab 100644 --- a/src/panelWebView/components/Git/GitAction.tsx +++ b/src/panelWebView/components/Git/GitAction.tsx @@ -1,6 +1,6 @@ import { Messenger, messageHandler } from '@estruyf/vscode/dist/client'; import { EventData } from '@estruyf/vscode/dist/models'; -import { ArrowPathIcon } from '@heroicons/react/24/outline'; +import { ArrowDownTrayIcon, ArrowPathIcon } from '@heroicons/react/24/outline'; import * as React from 'react'; import { useEffect, useState } from 'react'; import { GeneralCommands } from '../../../constants'; @@ -8,6 +8,7 @@ import { PanelSettings } from '../../../models'; import { ActionButton } from '../ActionButton'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; +import { BranchIcon } from '../Icons/BranchIcon'; export interface IGitActionProps { settings: PanelSettings | undefined; @@ -16,12 +17,16 @@ export interface IGitActionProps { export const GitAction: React.FunctionComponent = ({ settings }: React.PropsWithChildren) => { + const [commitMessage, setCommitMessage] = useState(undefined); const [crntBanch, setCrntBranch] = useState(undefined); - const [branches, setBranches] = useState(undefined); - const [isSyncing, setIsSyncing] = useState(false); + const [isSyncing, setIsSyncing] = useState<"syncing" | "fetching" | "idle">("idle"); - const pull = () => { - Messenger.send(GeneralCommands.toVSCode.git.sync); + const sync = () => { + Messenger.send(GeneralCommands.toVSCode.git.sync, commitMessage); + }; + + const fetch = () => { + Messenger.send(GeneralCommands.toVSCode.git.fetch); }; const selectBranch = () => { @@ -32,26 +37,65 @@ export const GitAction: React.FunctionComponent = ({ const { command, payload } = message.data; if (command === GeneralCommands.toWebview.git.syncingStart) { - setIsSyncing(true); + setIsSyncing(payload || "syncing"); + } else if (command === GeneralCommands.toWebview.git.syncingStart) { + setIsSyncing("syncing"); } else if (command === GeneralCommands.toWebview.git.syncingEnd) { - setIsSyncing(false); - } else if (command === GeneralCommands.toWebview.git.branchInfo) { - setCrntBranch(payload.crntBranch || undefined); - setBranches(payload.branches || undefined); + setCommitMessage(undefined); + setIsSyncing("idle"); + } else if (command === GeneralCommands.toWebview.git.branchName) { + setCrntBranch(payload || undefined); } }; - const isSyncDisabled = React.useMemo(() => { - if (!settings?.git?.disabledBranches || settings.git.disabledBranches.length === 0) { - return false; + const isCommitRequired = React.useMemo(() => { + const requiresCommitMessage = settings?.git?.requiresCommitMessage || []; + + if (!crntBanch) { + return {}; } + if (requiresCommitMessage && requiresCommitMessage.includes(crntBanch) && !commitMessage) { + return { + border: '1px solid var(--vscode-inputValidation-errorBorder)' + }; + } + + return {}; + }, [settings?.git?.requiresCommitMessage, crntBanch, commitMessage]) + + const isCommitDisabed = React.useMemo(() => { + const disabledBranches = settings?.git?.disabledBranches || []; + if (!crntBanch) { return true; } - return settings.git.disabledBranches.includes(crntBanch); - }, [settings?.git?.disabledBranches, crntBanch]) + if (disabledBranches && disabledBranches.includes(crntBanch)) { + return true; + } + + return false; + }, [settings?.git?.disabledBranches, crntBanch, commitMessage]) + + const isSyncDisabled = React.useMemo(() => { + const disabledBranches = settings?.git?.disabledBranches || []; + const requiresCommitMessage = settings?.git?.requiresCommitMessage || []; + + if (!crntBanch) { + return true; + } + + if (disabledBranches && disabledBranches.includes(crntBanch)) { + return true; + } + + if (requiresCommitMessage && requiresCommitMessage.includes(crntBanch)) { + return !commitMessage; + } + + return false; + }, [settings?.git?.disabledBranches, settings?.git?.requiresCommitMessage, crntBanch, commitMessage]) useEffect(() => { Messenger.listen(messageListener); @@ -75,32 +119,57 @@ export const GitAction: React.FunctionComponent = ({

- Git Actions + Changes

- - - - {l10n.t(LocalizationKey.commonSync)} - -
- } - /> +
+ setCommitMessage(e.target.value)} + disabled={isCommitDisabed} + /> + + + + + {l10n.t(LocalizationKey.commonSync)} + +
+ } + /> + + + + + Fetch + +
+ } + /> +
); }; diff --git a/src/panelWebView/components/Icons/BranchIcon.tsx b/src/panelWebView/components/Icons/BranchIcon.tsx new file mode 100644 index 00000000..18a3c50e --- /dev/null +++ b/src/panelWebView/components/Icons/BranchIcon.tsx @@ -0,0 +1,15 @@ +import * as React from 'react'; + +export interface IBranchIconProps { + className?: string; +} + +export const BranchIcon: React.FunctionComponent = ({ + className +}: React.PropsWithChildren) => { + return ( + + + + ); +}; \ No newline at end of file diff --git a/src/panelWebView/styles.css b/src/panelWebView/styles.css index 163bec84..9d0a9a40 100644 --- a/src/panelWebView/styles.css +++ b/src/panelWebView/styles.css @@ -976,12 +976,14 @@ vscode-divider { } /* Git actions */ +.git_actions__fetch, .git_actions__sync { display: flex; align-items: center; justify-content: center; } +.git_actions__fetch svg, .git_actions__sync svg { height: 1.25rem; width: 1.25rem; From d70f983694031c4400984bbb991c7451cace0883 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Tue, 23 Jan 2024 15:21:04 +0100 Subject: [PATCH 05/11] Optimizations --- src/listeners/general/GitListener.ts | 66 +++++++++++++------ src/panelWebView/components/Git/GitAction.tsx | 20 +++--- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/listeners/general/GitListener.ts b/src/listeners/general/GitListener.ts index 9405b8e3..3c620ee8 100644 --- a/src/listeners/general/GitListener.ts +++ b/src/listeners/general/GitListener.ts @@ -48,6 +48,15 @@ export class GitListener { private static repository: GitRepository | null = null; private static branchName: string | null = null; + /** + * Retrieves the Git settings. + * @returns {Promise<{ + * isGitRepo: boolean, + * actions: boolean, + * disabledBranches: string[], + * requiresCommitMessage: string[] + * }>} The Git settings. + */ public static async getSettings() { const gitActions = Settings.get(SETTING_GIT_ENABLED); if (gitActions) { @@ -110,13 +119,19 @@ export class GitListener { } } - public static async selectBranch() { + /** + * Selects the current branch in the Git repository. + * @returns {Promise} A promise that resolves when the branch command has been executed. + */ + public static async selectBranch(): Promise { const workspaceFolder = Folders.getWorkspaceFolder(); await commands.executeCommand('git.checkout', workspaceFolder); } /** - * Run the sync/fetch + * Synchronizes the local repository with the remote repository. + * @param commitMsg The commit message for the push operation. + * @param isSync Determines whether to perform a sync operation (default: true) or a fetch operation. */ public static async sync(commitMsg?: string, isSync: boolean = true) { try { @@ -138,8 +153,8 @@ export class GitListener { } /** - * Check if the current workspace is a git repository - * @returns + * Checks if the current workspace is a Git repository. + * @returns A boolean indicating whether the current workspace is a Git repository. */ public static async isGitRepository() { const git = this.getClient(); @@ -159,8 +174,9 @@ export class GitListener { } /** - * Pull the changes from the remote - * @returns + * Pulls the latest changes from the remote repository. + * If submoduleFolder is specified, it checks out the submoduleBranch for the submodule located in that folder. + * If submodulePull is true, it also updates the submodules with the latest changes from the remote repository. */ private static async pull() { const git = this.getClient(); @@ -195,8 +211,10 @@ export class GitListener { } /** - * Push the changes to the remote - * @returns + * Pushes the changes to the remote repository. + * + * @param commitMsg The commit message to use. If not provided, it will use the default commit message or the one specified in the settings. + * @returns A promise that resolves when the push operation is completed. */ private static async push(commitMsg?: string) { commitMsg = @@ -273,9 +291,11 @@ export class GitListener { } /** - * Get the git client - * @param submoduleFolder - * @returns + * Retrieves the Git client instance based on the provided submodule folder. + * If no submodule folder is provided, it returns the main Git client instance. + * If a submodule folder is provided, it returns the submodule-specific Git client instance. + * @param submoduleFolder The path to the submodule folder. + * @returns The Git client instance or null if it cannot be retrieved. */ private static getClient(submoduleFolder: string = ''): SimpleGit | null { if (!submoduleFolder && this.client) { @@ -301,16 +321,19 @@ export class GitListener { } } - private static async vscodeGitProvider() { + /** + * Initializes the VS Code Git provider and sets up event listeners for repository changes. + * @returns {Promise} A promise that resolves when the Git provider is initialized. + */ + private static async vscodeGitProvider(): Promise { if (!GitListener.gitAPI) { - const wsFolder = Folders.getWorkspaceFolder(); const extension = extensions.getExtension('vscode.git'); /** * Logic from: https://github.com/microsoft/vscode/blob/main/extensions/github/src/extension.ts * initializeGitExtension */ - if (wsFolder && extension) { + if (extension) { const gitExtension = extension.isActive ? extension.exports : await extension.activate(); // Get version 1 of the API @@ -337,6 +360,11 @@ export class GitListener { } } + /** + * Retrieves the branch name and sends a request. + * @param command - The command to send. + * @param requestId - The ID of the request. + */ private static async getBranch(command: string, requestId?: string) { if (!command || !requestId) { return; @@ -366,8 +394,8 @@ export class GitListener { } /** - * Trigger the branch change - * @param repo + * Triggers a branch change event for the specified Git repository. + * @param repo The Git repository to monitor for branch changes. */ private static async triggerBranchChange(repo: GitRepository | null) { if (repo && repo.state) { @@ -385,9 +413,9 @@ export class GitListener { } /** - * Send the message to the webview - * @param command - * @param payload + * Sends a message to the panel and the dashboard. + * @param command - The command to send. + * @param payload - The payload to send with the command. */ private static sendMsg(command: string, payload: any) { const extPath = Extension.getInstance().extensionPath; diff --git a/src/panelWebView/components/Git/GitAction.tsx b/src/panelWebView/components/Git/GitAction.tsx index 1e3cefab..2838f882 100644 --- a/src/panelWebView/components/Git/GitAction.tsx +++ b/src/panelWebView/components/Git/GitAction.tsx @@ -33,7 +33,7 @@ export const GitAction: React.FunctionComponent = ({ messageHandler.send(GeneralCommands.toVSCode.git.selectBranch) } - const messageListener = (message: MessageEvent>) => { + const messageListener = React.useCallback((message: MessageEvent>) => { const { command, payload } = message.data; if (command === GeneralCommands.toWebview.git.syncingStart) { @@ -46,7 +46,7 @@ export const GitAction: React.FunctionComponent = ({ } else if (command === GeneralCommands.toWebview.git.branchName) { setCrntBranch(payload || undefined); } - }; + }, []); const isCommitRequired = React.useMemo(() => { const requiresCommitMessage = settings?.git?.requiresCommitMessage || []; @@ -95,7 +95,13 @@ export const GitAction: React.FunctionComponent = ({ } return false; - }, [settings?.git?.disabledBranches, settings?.git?.requiresCommitMessage, crntBanch, commitMessage]) + }, [settings?.git?.disabledBranches, settings?.git?.requiresCommitMessage, crntBanch, commitMessage]); + + const fetchBranch = React.useCallback(() => { + messageHandler.request(GeneralCommands.toVSCode.git.getBranch).then((branch) => { + setCrntBranch(branch); + }); + }, []); useEffect(() => { Messenger.listen(messageListener); @@ -103,13 +109,11 @@ export const GitAction: React.FunctionComponent = ({ return () => { Messenger.unlisten(messageListener); }; - }, []); + }, [messageListener]); useEffect(() => { - messageHandler.request(GeneralCommands.toVSCode.git.getBranch).then((branch) => { - setCrntBranch(branch); - }); - }, []); + fetchBranch(); + }, [fetchBranch]); if (!settings?.git?.actions || !settings?.git.isGitRepo) { return null; From b1380388b620bc3cf9e7a96866d4acd28c3496dc Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Tue, 23 Jan 2024 20:10:48 +0100 Subject: [PATCH 06/11] Issue: Open Preview button stops working #738 --- CHANGELOG.md | 1 + src/commands/Preview.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f0ecbb..f9c9c6d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - [#721](https://github.com/estruyf/vscode-front-matter/issues/721): Fix keywords regex to support unicode characters - [#725](https://github.com/estruyf/vscode-front-matter/issues/725): Fix for opening menu of pinned items - [#730](https://github.com/estruyf/vscode-front-matter/issues/730): Add debounce to the input fields +- [#738](https://github.com/estruyf/vscode-front-matter/issues/738): Fix when re-opening the preview after closing it ## [9.4.0] - 2023-12-12 - [Release notes](https://beta.frontmatter.codes/updates/v9.4.0) diff --git a/src/commands/Preview.ts b/src/commands/Preview.ts index 0f28e902..835b6030 100644 --- a/src/commands/Preview.ts +++ b/src/commands/Preview.ts @@ -97,6 +97,9 @@ export class Preview { const cspSource = webView.webview.cspSource; webView.onDidDispose(() => { + if (crntFilePath && this.webviews[crntFilePath]) { + delete this.webviews[crntFilePath]; + } webView.dispose(); }); From 6e8421745861101ba73c0cb08458abeeefd195f6 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 24 Jan 2024 09:53:09 +0100 Subject: [PATCH 07/11] #739 - localization keys + action button --- l10n/bundle.l10n.json | 4 ++ package.nls.json | 5 ++- src/localization/localization.enum.ts | 12 +++++ src/panelWebView/components/ActionButton.tsx | 9 ++-- .../Actions/OpenOnWebsiteAction.tsx | 4 +- src/panelWebView/components/CustomScript.tsx | 6 ++- src/panelWebView/components/Git/GitAction.tsx | 44 +++++++++---------- src/panelWebView/components/Preview.tsx | 6 ++- src/panelWebView/components/PublishAction.tsx | 4 +- src/panelWebView/components/SlugAction.tsx | 6 ++- 10 files changed, 68 insertions(+), 32 deletions(-) diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index 8a0bbae0..ab9f7d9e 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -329,6 +329,10 @@ "dashboard.configuration.astro.astroContentTypes.empty": "No Astro Content Collections found.", "dashboard.configuration.astro.astroContentTypes.description": "The following Astro Content Collections can be used to generate a content-type.", + "panel.git.gitAction.title": "Publish changes", + "panel.git.gitAction.input.placeholder": "Commit message", + "panel.git.gitAction.button.fetch": "Fetch", + "panel.contentType.contentTypeValidator.title": "Content-type", "panel.contentType.contentTypeValidator.hint": "We noticed field differences between the content-type and the front matter data. \n Would you like to create, update, or set the content-type for this content?", "panel.contentType.contentTypeValidator.button.create": "Create content-type", diff --git a/package.nls.json b/package.nls.json index de0ecc3a..f96cfdbe 100644 --- a/package.nls.json +++ b/package.nls.json @@ -250,5 +250,8 @@ "command.frontMatter.settings.refresh": "Refresh Front Matter Settings", "setting.frontMatter.config.dynamicFilePath.markdownDescription": "Specify the path to the dynamic config file (ex: [[workspace]]/config.js). [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.config.dynamicfilepath)", "setting.frontMatter.taxonomy.contentTypes.items.properties.allowAsSubContent.description": "Specify if the content type can be used as sub content.", - "setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "Specify if the content type is sub content." + "setting.frontMatter.taxonomy.contentTypes.items.properties.isSubContent.description": "Specify if the content type is sub content.", + + "setting.frontMatter.git.disableOnBranches.markdownDescription": "Specify the branches on which you want to disable the Git actions. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.disableonbranches)", + "setting.frontMatter.git.requiresCommitMessage.markdownDescription": "Specify if you want to require a commit message when publishing your changes for a specified branch. [Check in the docs](https://frontmatter.codes/docs/settings/overview#frontmatter.git.requirescommitmessage)" } \ No newline at end of file diff --git a/src/localization/localization.enum.ts b/src/localization/localization.enum.ts index 54e508de..0d379de2 100644 --- a/src/localization/localization.enum.ts +++ b/src/localization/localization.enum.ts @@ -1087,6 +1087,18 @@ export enum LocalizationKey { * The following Astro Content Collections can be used to generate a content-type. */ dashboardConfigurationAstroAstroContentTypesDescription = 'dashboard.configuration.astro.astroContentTypes.description', + /** + * Publish changes + */ + panelGitGitActionTitle = 'panel.git.gitAction.title', + /** + * Commit message + */ + panelGitGitActionInputPlaceholder = 'panel.git.gitAction.input.placeholder', + /** + * Fetch + */ + panelGitGitActionButtonFetch = 'panel.git.gitAction.button.fetch', /** * Content-type */ diff --git a/src/panelWebView/components/ActionButton.tsx b/src/panelWebView/components/ActionButton.tsx index 71e7ee73..9097ac03 100644 --- a/src/panelWebView/components/ActionButton.tsx +++ b/src/panelWebView/components/ActionButton.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; export interface IActionButtonProps { - title: JSX.Element | string; + title: string; className?: string; disabled?: boolean; onClick: (e: React.SyntheticEvent) => void; @@ -11,12 +11,13 @@ const ActionButton: React.FunctionComponent = ({ className, onClick, disabled, - title + title, + children }: React.PropsWithChildren) => { return (
-
); diff --git a/src/panelWebView/components/Actions/OpenOnWebsiteAction.tsx b/src/panelWebView/components/Actions/OpenOnWebsiteAction.tsx index 1d3dc3a0..c8d6b2c7 100644 --- a/src/panelWebView/components/Actions/OpenOnWebsiteAction.tsx +++ b/src/panelWebView/components/Actions/OpenOnWebsiteAction.tsx @@ -28,6 +28,8 @@ export const OpenOnWebsiteAction: React.FunctionComponent + onClick={open}> + {l10n.t(LocalizationKey.commonOpenOnWebsite)} + ); }; \ No newline at end of file diff --git a/src/panelWebView/components/CustomScript.tsx b/src/panelWebView/components/CustomScript.tsx index 11225982..a6c547d5 100644 --- a/src/panelWebView/components/CustomScript.tsx +++ b/src/panelWebView/components/CustomScript.tsx @@ -16,7 +16,11 @@ const CustomScript: React.FunctionComponent = ({ Messenger.send(CommandToCode.runCustomScript, { title, script }); }; - return ; + return ( + + {title} + + ); }; CustomScript.displayName = 'CustomScript'; diff --git a/src/panelWebView/components/Git/GitAction.tsx b/src/panelWebView/components/Git/GitAction.tsx index 2838f882..c8e71472 100644 --- a/src/panelWebView/components/Git/GitAction.tsx +++ b/src/panelWebView/components/Git/GitAction.tsx @@ -123,14 +123,14 @@ export const GitAction: React.FunctionComponent = ({

- Changes + {l10n.t(LocalizationKey.panelGitGitActionTitle)}

@@ -139,7 +139,7 @@ export const GitAction: React.FunctionComponent = ({ = ({ - - - {l10n.t(LocalizationKey.commonSync)} - -
- } - /> + title={l10n.t(LocalizationKey.commonSync)} + > +
+
+
- - - Fetch - -
- } - /> + title={l10n.t(LocalizationKey.panelGitGitActionButtonFetch)} + > +
+
+
); diff --git a/src/panelWebView/components/Preview.tsx b/src/panelWebView/components/Preview.tsx index 5b8629f1..60472e9e 100644 --- a/src/panelWebView/components/Preview.tsx +++ b/src/panelWebView/components/Preview.tsx @@ -12,7 +12,11 @@ const Preview: React.FunctionComponent = (_: React.PropsWithChild Messenger.send(CommandToCode.openPreview); }; - return ; + return ( + + {l10n.t(LocalizationKey.panelPreviewTitle)} + + ); }; Preview.displayName = 'Preview'; diff --git a/src/panelWebView/components/PublishAction.tsx b/src/panelWebView/components/PublishAction.tsx index d705363e..42f68fa0 100644 --- a/src/panelWebView/components/PublishAction.tsx +++ b/src/panelWebView/components/PublishAction.tsx @@ -23,7 +23,9 @@ const PublishAction: React.FunctionComponent = ( onClick={publish} className={`${draft ? '' : 'secondary'}`} title={draft ? l10n.t(LocalizationKey.panelPublishActionPublish) : l10n.t(LocalizationKey.panelPublishActionUnpublish)} - /> + > + {draft ? l10n.t(LocalizationKey.panelPublishActionPublish) : l10n.t(LocalizationKey.panelPublishActionUnpublish)} + ); }; diff --git a/src/panelWebView/components/SlugAction.tsx b/src/panelWebView/components/SlugAction.tsx index a7705a7a..880f8181 100644 --- a/src/panelWebView/components/SlugAction.tsx +++ b/src/panelWebView/components/SlugAction.tsx @@ -14,7 +14,11 @@ const SlugAction: React.FunctionComponent< Messenger.send(CommandToCode.updateSlug); }; - return ; + return ( + + {l10n.t(LocalizationKey.panelSlugActionTitle)} + + ); }; SlugAction.displayName = 'SlugAction'; From 7c4aa1d63ddee4be85d5172221db52885038d6ad Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 24 Jan 2024 09:57:08 +0100 Subject: [PATCH 08/11] Update changlog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9c9c6d6..91336ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### 🎨 Enhancements - [#727](https://github.com/estruyf/vscode-front-matter/pull/727): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387) +- [#739](https://github.com/estruyf/vscode-front-matter/pull/739): New Git settings to disable and require a commit message ### ⚡️ Optimizations From 3c29df54c195b1ae19af758746b919a92a366f4c Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 12 Feb 2024 12:36:08 +0100 Subject: [PATCH 09/11] Updated changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a4fff10..929fa79c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,8 +10,8 @@ ### 🎨 Enhancements - [#727](https://github.com/estruyf/vscode-front-matter/pull/727): Updated Japanese translations thanks to [mayumihara](https://github.com/mayumih387) -- [#739](https://github.com/estruyf/vscode-front-matter/pull/739): New Git settings to disable and require a commit message - [#737](https://github.com/estruyf/vscode-front-matter/issues/737): Optimize the grid layout of the content and media dashboards +- [#739](https://github.com/estruyf/vscode-front-matter/pull/739): New Git settings to disable and require a commit message - [#741](https://github.com/estruyf/vscode-front-matter/issues/741): Added message on the content dashboard when content is processed - [#747](https://github.com/estruyf/vscode-front-matter/issues/747): The `@frontmatter/extensibility` dependency now supports scripts for placeholders From 6cbf86f822a49bf6a66cacb6f3e5f7a5f698ede8 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 12 Feb 2024 13:02:39 +0100 Subject: [PATCH 10/11] Add is repo --- src/constants/GeneralCommands.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/constants/GeneralCommands.ts b/src/constants/GeneralCommands.ts index 3deef171..e761c335 100644 --- a/src/constants/GeneralCommands.ts +++ b/src/constants/GeneralCommands.ts @@ -11,6 +11,7 @@ export const GeneralCommands = { toVSCode: { openLink: 'openLink', git: { + isRepo: 'gitIsRepo', sync: 'gitSync', fetch: 'getFetch', getBranch: 'getBranch', From a29a6600ab62662468edf96fa78aa4b00ebfbc49 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Mon, 12 Feb 2024 13:05:26 +0100 Subject: [PATCH 11/11] Update welcome view --- .../components/Steps/StepsToGetStarted.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dashboardWebView/components/Steps/StepsToGetStarted.tsx b/src/dashboardWebView/components/Steps/StepsToGetStarted.tsx index 2e1c3fc5..2be2adb3 100644 --- a/src/dashboardWebView/components/Steps/StepsToGetStarted.tsx +++ b/src/dashboardWebView/components/Steps/StepsToGetStarted.tsx @@ -255,7 +255,7 @@ export const StepsToGetStarted: React.FunctionComponent ), show: isGitRepo, - status: settings.git.actions ? Status.Completed : Status.NotStarted + status: settings.git?.actions ? Status.Completed : Status.NotStarted }, { id: `welcome-import`, @@ -293,12 +293,12 @@ export const StepsToGetStarted: React.FunctionComponent }, [settings.crntFramework, settings.framework]); React.useEffect(() => { - messageHandler.request(GeneralCommands.toVSCode.gitIsRepo).then((result) => { + messageHandler.request(GeneralCommands.toVSCode.git.isRepo).then((result) => { setIsGitRepo(result); }); - setIsGitEnabled(settings.git.actions); - }, [settings.git.actions]); + setIsGitEnabled(settings.git?.actions || false); + }, [settings.git?.actions]); React.useEffect(() => { const fetchTemplates = async () => {