mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-09 10:22:55 +02:00
#377: Git sync actions added on panel and content dashboard
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [8.1.0] - 2022-xx-xx
|
||||
|
||||
### ✨ New features
|
||||
|
||||
- [#377](https://github.com/estruyf/vscode-front-matter/issues/377): Git sync actions added on panel and content dashboard (pull and push your changes to remote)
|
||||
|
||||
### 🎨 Enhancements
|
||||
|
||||
- [#352](https://github.com/estruyf/vscode-front-matter/issues/352): Custom placeholders now support scripting
|
||||
@@ -11,6 +15,8 @@
|
||||
|
||||
### ⚡️ Optimizations
|
||||
|
||||
- Internal post message optimizations to the webviews
|
||||
|
||||
### 🐞 Fixes
|
||||
|
||||
- [#378](https://github.com/estruyf/vscode-front-matter/issues/378): Fix last modified update only to content in content folders
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
}
|
||||
|
||||
.collapsible__body,
|
||||
.ext_settings {
|
||||
.ext_settings,
|
||||
.git_actions {
|
||||
padding: 1rem 1.25rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
Generated
+57
@@ -81,6 +81,7 @@
|
||||
"recoil": "^0.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.7",
|
||||
"simple-git": "^3.10.0",
|
||||
"style-loader": "2.0.0",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"tailwindcss-nested-groups": "^1.2.4",
|
||||
@@ -217,6 +218,21 @@
|
||||
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@kwsites/promise-deferred": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@microsoft/fast-element": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.7.0.tgz",
|
||||
@@ -8800,6 +8816,21 @@
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-git": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.10.0.tgz",
|
||||
"integrity": "sha512-2w35xrS5rVtAW0g67LqtxCZN5cdddz/woQRfS0OJXaljXEoTychZ4jnE+CQgra/wX4ZvHeiChTUMenCwfIYEYw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@kwsites/file-exists": "^1.1.1",
|
||||
"@kwsites/promise-deferred": "^1.1.1",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/steveukx/git-js?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
@@ -11023,6 +11054,21 @@
|
||||
"integrity": "sha512-FmuxfCuolpLl0AnQ2NHSzoUKWEJDFl63qXjzdoWBVyFCXzMGm1spBzk7LeHNoVCiWCF7mRVms9e6jEV9+MoPbg==",
|
||||
"dev": true
|
||||
},
|
||||
"@kwsites/file-exists": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
|
||||
"integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"@kwsites/promise-deferred": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz",
|
||||
"integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==",
|
||||
"dev": true
|
||||
},
|
||||
"@microsoft/fast-element": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.7.0.tgz",
|
||||
@@ -17608,6 +17654,17 @@
|
||||
"simple-concat": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"simple-git": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.10.0.tgz",
|
||||
"integrity": "sha512-2w35xrS5rVtAW0g67LqtxCZN5cdddz/woQRfS0OJXaljXEoTychZ4jnE+CQgra/wX4ZvHeiChTUMenCwfIYEYw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@kwsites/file-exists": "^1.1.1",
|
||||
"@kwsites/promise-deferred": "^1.1.1",
|
||||
"debug": "^4.3.4"
|
||||
}
|
||||
},
|
||||
"simple-swizzle": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||
|
||||
+12
-1
@@ -642,6 +642,16 @@
|
||||
"default": null,
|
||||
"markdownDescription": "Specify the command you want to use to start your static site generator or framework. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.framework.startcommand)"
|
||||
},
|
||||
"frontMatter.git.actions": {
|
||||
"type": "boolean",
|
||||
"markdownDescription": "Specify if you want to use the Git actions for your website. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.git.actions)",
|
||||
"default": false
|
||||
},
|
||||
"frontMatter.git.commitMsg": {
|
||||
"type": "string",
|
||||
"markdownDescription": "Specify the commit message you want to use for the sync. [Check in the docs](https://frontmatter.codes/docs/settings#frontmatter.git.commitmsg)",
|
||||
"default": "Synced by Front Matter"
|
||||
},
|
||||
"frontMatter.global.activeMode": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -2038,6 +2048,7 @@
|
||||
"recoil": "^0.4.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.7",
|
||||
"simple-git": "^3.10.0",
|
||||
"style-loader": "2.0.0",
|
||||
"tailwindcss": "^2.2.7",
|
||||
"tailwindcss-nested-groups": "^1.2.4",
|
||||
@@ -2061,4 +2072,4 @@
|
||||
"dependencies": {
|
||||
"node-fetch": "^2.6.7"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import { DashboardData } from '../models/DashboardData';
|
||||
import { MediaLibrary } from '../helpers/MediaLibrary';
|
||||
import { DashboardListener, MediaListener, SettingsListener, TelemetryListener, DataListener, PagesListener, ExtensionListener, SnippetListener, TaxonomyListener } from '../listeners/dashboard';
|
||||
import { MediaListener as PanelMediaListener } from '../listeners/panel'
|
||||
import { ModeListener } from '../listeners/general';
|
||||
import { GitListener, ModeListener } from '../listeners/general';
|
||||
|
||||
export class Dashboard {
|
||||
private static webview: WebviewPanel | null = null;
|
||||
@@ -146,6 +146,7 @@ export class Dashboard {
|
||||
TelemetryListener.process(msg);
|
||||
SnippetListener.process(msg);
|
||||
ModeListener.process(msg);
|
||||
GitListener.process(msg);
|
||||
TaxonomyListener.process(msg);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,8 +3,11 @@
|
||||
export const GeneralCommands = {
|
||||
toWebview: {
|
||||
setMode: "setMode",
|
||||
gitSyncingStart: "gitSyncingStart",
|
||||
gitSyncingEnd: "gitSyncingEnd",
|
||||
},
|
||||
toVSCode: {
|
||||
openLink: "openLink",
|
||||
gitSync: "gitSync",
|
||||
}
|
||||
};
|
||||
@@ -78,6 +78,9 @@ export const SETTING_FRAMEWORK_START = "framework.startCommand";
|
||||
|
||||
export const SETTING_SITE_BASEURL = "site.baseURL";
|
||||
|
||||
export const SETTING_GIT_ACTIONS = "git.actions";
|
||||
export const SETTING_GIT_COMMIT_MSG = "git.commitMsg";
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import { LightningBoltIcon, PlusIcon } from '@heroicons/react/outline';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { routePaths } from '../..';
|
||||
import { useEffect } from 'react';
|
||||
import { SyncButton } from './SyncButton';
|
||||
|
||||
export interface IHeaderProps {
|
||||
header?: React.ReactNode;
|
||||
@@ -108,6 +109,8 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({header, totalPage
|
||||
|
||||
<div className={`flex items-center justify-end space-x-4 flex-1`}>
|
||||
<Startup settings={settings} />
|
||||
|
||||
<SyncButton />
|
||||
|
||||
<ChoiceButton
|
||||
title={`Create content`}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { RefreshIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { GeneralCommands } from '../../../constants';
|
||||
import { SettingsSelector } from '../../state';
|
||||
|
||||
export interface ISyncButtonProps {}
|
||||
|
||||
export const SyncButton: React.FunctionComponent<ISyncButtonProps> = (props: React.PropsWithChildren<ISyncButtonProps>) => {
|
||||
const settings = useRecoilValue(SettingsSelector);
|
||||
const [ isSyncing, setIsSyncing ] = useState(false);
|
||||
|
||||
const pull = () => {
|
||||
Messenger.send(GeneralCommands.toVSCode.gitSync);
|
||||
};
|
||||
|
||||
const messageListener = (message: MessageEvent<EventData<any>>) => {
|
||||
const { command, data } = message.data;
|
||||
|
||||
if (command === GeneralCommands.toWebview.gitSyncingStart) {
|
||||
setIsSyncing(true);
|
||||
} else if (command === GeneralCommands.toWebview.gitSyncingEnd) {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!settings?.git?.actions || !settings?.git.isGitRepo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='git_actions'>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500"
|
||||
onClick={pull}
|
||||
disabled={isSyncing}
|
||||
>
|
||||
<RefreshIcon className={`w-4 h-4 mr-2 ${isSyncing ? 'animate-reverse-spin' : ''}`} aria-hidden="true" />
|
||||
<span>Sync</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -18,7 +18,7 @@ export default function useMessages() {
|
||||
const [, setView] = useRecoilState(DashboardViewAtom);
|
||||
const [, setSearchReady] = useRecoilState(SearchReadyAtom);
|
||||
|
||||
Messenger.listen((event: MessageEvent<EventData<any>>) => {
|
||||
const messageListener = (event: MessageEvent<EventData<any>>) => {
|
||||
const message = event.data;
|
||||
|
||||
switch (message.command) {
|
||||
@@ -53,14 +53,20 @@ export default function useMessages() {
|
||||
setMode(message.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
|
||||
setLoading(true);
|
||||
Messenger.send(DashboardMessage.getViewType);
|
||||
Messenger.send(DashboardMessage.getTheme);
|
||||
Messenger.send(DashboardMessage.getData);
|
||||
Messenger.send(DashboardMessage.getMode);
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
}
|
||||
}, ['']);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { DataType } from './../../models/DataType';
|
||||
import { VersionInfo } from '../../models/VersionInfo';
|
||||
import { ContentFolder } from '../../models/ContentFolder';
|
||||
import { ContentType, CustomScript, CustomTaxonomy, DraftField, Framework, Snippets, SortingSetting } from '../../models';
|
||||
import { ContentType, CustomScript, CustomTaxonomy, DraftField, Framework, GitSettings, Snippets, SortingSetting } from '../../models';
|
||||
import { SortingOption } from './SortingOption';
|
||||
import { DashboardViewType } from '.';
|
||||
import { DataFile } from '../../models/DataFile';
|
||||
|
||||
export interface Settings {
|
||||
git: GitSettings;
|
||||
beta: boolean;
|
||||
initialized: boolean;
|
||||
wsFolder: string;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { TagType } from '../panelWebView/TagType';
|
||||
import { WebviewHelper } from '@estruyf/vscode';
|
||||
import { Extension } from '../helpers/Extension';
|
||||
import { Telemetry } from '../helpers/Telemetry';
|
||||
import { ModeListener } from '../listeners/general';
|
||||
import { GitListener, ModeListener } from '../listeners/general';
|
||||
|
||||
export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
public static readonly viewType = "frontMatter.explorer";
|
||||
@@ -82,6 +82,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
SettingsListener.process(msg);
|
||||
TaxonomyListener.process(msg);
|
||||
ModeListener.process(msg);
|
||||
GitListener.process(msg);
|
||||
});
|
||||
|
||||
webviewView.onDidChangeVisibility(() => {
|
||||
|
||||
+1
-11
@@ -14,7 +14,7 @@ import { TagType } from './panelWebView/TagType';
|
||||
import { ExplorerView } from './explorerView/ExplorerView';
|
||||
import { Extension } from './helpers/Extension';
|
||||
import { DashboardData } from './models/DashboardData';
|
||||
import { Logger, Settings as SettingsHelper } from './helpers';
|
||||
import { debounceCallback, Logger, Settings as SettingsHelper } from './helpers';
|
||||
import { Content } from './commands/Content';
|
||||
import ContentProvider from './providers/ContentProvider';
|
||||
import { Wysiwyg } from './commands/Wysiwyg';
|
||||
@@ -297,14 +297,4 @@ const handleAutoDateUpdate = (e: vscode.TextDocumentWillSaveEvent) => {
|
||||
const triggerShowDraftStatus = (location: string) => {
|
||||
Logger.info(`Triggering draft status update: ${location}`);
|
||||
statusDebouncer(() => { StatusListener.verify(frontMatterStatusBar, collection); }, 1000);
|
||||
};
|
||||
|
||||
const debounceCallback = () => {
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
return (fnc: any, time: number) => {
|
||||
const functionCall = (...args: any[]) => fnc.apply(args);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(functionCall, time) as any;
|
||||
};
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
import { GitListener } from './../listeners/general/GitListener';
|
||||
import { basename, join } from "path";
|
||||
import { workspace } from "vscode";
|
||||
import { Folders } from "../commands/Folders";
|
||||
import { Project } from "../commands/Project";
|
||||
import { CONTEXT, ExtensionState, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_SORTING, SETTING_CONTENT_SORTING_DEFAULT, SETTING_DASHBOARD_OPENONSTART, SETTING_DATA_FILES, SETTING_DATA_FOLDERS, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, SETTING_DASHBOARD_CONTENT_TAGS, SETTING_MEDIA_SUPPORTED_MIMETYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TEMPLATES_ENABLED } from "../constants";
|
||||
import { CONTEXT, ExtensionState, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_SORTING, SETTING_CONTENT_SORTING_DEFAULT, SETTING_DASHBOARD_OPENONSTART, SETTING_DATA_FILES, SETTING_DATA_FOLDERS, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_CONTENT_SNIPPETS, SETTING_DATE_FORMAT, SETTING_DASHBOARD_CONTENT_TAGS, SETTING_MEDIA_SUPPORTED_MIMETYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TEMPLATES_ENABLED, SETTING_GIT_ACTIONS } from "../constants";
|
||||
import { DashboardViewType, SortingOption, Settings as ISettings } from "../dashboardWebView/models";
|
||||
import { CustomScript, DraftField, Snippets, SortingSetting, TaxonomyType } from "../models";
|
||||
import { DataFile } from "../models/DataFile";
|
||||
@@ -19,8 +20,13 @@ export class DashboardSettings {
|
||||
const ext = Extension.getInstance();
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
const isInitialized = Project.isInitialized();
|
||||
const gitActions = Settings.get<boolean>(SETTING_GIT_ACTIONS);
|
||||
|
||||
return {
|
||||
git: {
|
||||
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
|
||||
actions: gitActions || false
|
||||
},
|
||||
beta: ext.isBetaVersion(),
|
||||
wsFolder: wsFolder ? wsFolder.fsPath : '',
|
||||
staticFolder: Folders.getStaticFolderRelativePath(),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export const debounceCallback = () => {
|
||||
let timeout: NodeJS.Timeout;
|
||||
|
||||
return (fnc: any, time: number) => {
|
||||
const functionCall = (...args: any[]) => fnc.apply(args);
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(functionCall, time) as any;
|
||||
};
|
||||
};
|
||||
@@ -3,13 +3,20 @@ import { Extension, Settings } from "."
|
||||
import { Dashboard } from "../commands/Dashboard"
|
||||
import { Preview } from "../commands/Preview"
|
||||
import { Template } from "../commands/Template"
|
||||
import { CONTEXT, DefaultFields, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_TAGS } from "../constants"
|
||||
import { CONTEXT, DefaultFields, SETTING_CONTENT_DRAFT_FIELD, SETTING_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_DATA_TYPES, SETTING_FRAMEWORK_ID, SETTING_FRAMEWORK_START, SETTING_AUTO_UPDATE_DATE, SETTING_COMMA_SEPARATED_FIELDS, SETTING_CUSTOM_SCRIPTS, SETTING_DATE_FORMAT, SETTING_PANEL_FREEFORM, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_SLUG_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_CUSTOM, SETTING_TAXONOMY_FIELD_GROUPS, SETTING_TAXONOMY_TAGS, SETTING_GIT_ACTIONS } from "../constants"
|
||||
import { GitListener } from "../listeners/general"
|
||||
import { CustomScript, DataType, DraftField, FieldGroup, PanelSettings as IPanelSettings, ScriptType } from "../models"
|
||||
|
||||
export class PanelSettings {
|
||||
|
||||
public static async get(): Promise<IPanelSettings> {
|
||||
const gitActions = Settings.get<boolean>(SETTING_GIT_ACTIONS);
|
||||
|
||||
return {
|
||||
git: {
|
||||
isGitRepo: gitActions ? await GitListener.isGitRepository() : false,
|
||||
actions: gitActions || false
|
||||
},
|
||||
seo: {
|
||||
title: Settings.get(SETTING_SEO_TITLE_LENGTH) as number || -1,
|
||||
slug: Settings.get(SETTING_SEO_SLUG_LENGTH) as number || -1,
|
||||
|
||||
@@ -8,30 +8,22 @@ import { Folders } from '../commands/Folders';
|
||||
import { join, basename } from 'path';
|
||||
import { existsSync, readFileSync, watch, writeFileSync } from 'fs';
|
||||
import { Extension } from './Extension';
|
||||
import { debounceCallback } from './DebounceCallback';
|
||||
|
||||
export class Settings {
|
||||
public static globalFile = "frontmatter.json";
|
||||
private static config: vscode.WorkspaceConfiguration;
|
||||
private static globalConfig: any;
|
||||
|
||||
|
||||
public static init() {
|
||||
const fmConfig = Settings.projectConfigPath;
|
||||
if (fmConfig && existsSync(fmConfig)) {
|
||||
const localConfig = readFileSync(fmConfig, 'utf8');
|
||||
Settings.globalConfig = JSON.parse(localConfig);
|
||||
commands.executeCommand('setContext', CONTEXT.isEnabled, true);
|
||||
} else {
|
||||
Settings.globalConfig = undefined;
|
||||
}
|
||||
Settings.readConfig();
|
||||
|
||||
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
|
||||
Settings.onConfigChange((global?: any) => {
|
||||
if (global) {
|
||||
Settings.globalConfig = Object.assign({}, global);
|
||||
} else {
|
||||
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
}
|
||||
Settings.readConfig();
|
||||
Settings.config = vscode.workspace.getConfiguration(CONFIG_KEY);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -61,6 +53,7 @@ export class Settings {
|
||||
*/
|
||||
public static onConfigChange(callback: (global?: any) => void) {
|
||||
const projectConfig = Settings.projectConfigPath;
|
||||
const configDebouncer = debounceCallback();
|
||||
|
||||
workspace.onDidChangeConfiguration(() => {
|
||||
callback();
|
||||
@@ -68,8 +61,10 @@ export class Settings {
|
||||
|
||||
// Background listener for when it is not a user interaction
|
||||
if (projectConfig && existsSync(projectConfig)) {
|
||||
watch(projectConfig, () => {
|
||||
callback();
|
||||
let watcher = workspace.createFileSystemWatcher(projectConfig, true, false, true);
|
||||
watcher.onDidChange(async (uri: Uri) => {
|
||||
configDebouncer(() => callback(), 200);
|
||||
// callback()
|
||||
});
|
||||
}
|
||||
|
||||
@@ -81,7 +76,8 @@ export class Settings {
|
||||
if (file) {
|
||||
const fileContents = file.getText();
|
||||
const json = JSON.parse(fileContents);
|
||||
callback(json);
|
||||
configDebouncer(() => callback(json), 200);
|
||||
// callback(json)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -346,6 +342,19 @@ export class Settings {
|
||||
return hasSetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project config path
|
||||
* @returns
|
||||
*/
|
||||
public static get projectConfigPath() {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const fmConfig = join(wsFolder.fsPath, Settings.globalFile);
|
||||
return fmConfig;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if its the project config
|
||||
* @param filePath
|
||||
@@ -363,15 +372,16 @@ export class Settings {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the project config path
|
||||
* @returns
|
||||
* Read the global config file
|
||||
*/
|
||||
public static get projectConfigPath() {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
if (wsFolder) {
|
||||
const fmConfig = join(wsFolder.fsPath, Settings.globalFile);
|
||||
return fmConfig;
|
||||
private static readConfig() {
|
||||
const fmConfig = Settings.projectConfigPath;
|
||||
if (fmConfig && existsSync(fmConfig)) {
|
||||
const localConfig = readFileSync(fmConfig, 'utf8');
|
||||
Settings.globalConfig = JSON.parse(localConfig);
|
||||
commands.executeCommand('setContext', CONTEXT.isEnabled, true);
|
||||
} else {
|
||||
Settings.globalConfig = undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ export * from './CustomScript';
|
||||
export * from './DashboardSettings';
|
||||
export * from './DataFileHelper';
|
||||
export * from './DateHelper';
|
||||
export * from './DebounceCallback';
|
||||
export * from './Extension';
|
||||
export * from './FilesHelper';
|
||||
export * from './FrameworkDetector';
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { Settings } from './../../helpers/SettingsHelper';
|
||||
import { Dashboard } from '../../commands/Dashboard';
|
||||
import { ExplorerView } from '../../explorerView/ExplorerView';
|
||||
import { Extension, Logger } from '../../helpers';
|
||||
import { GeneralCommands } from './../../constants/GeneralCommands';
|
||||
import simpleGit, { SimpleGit } from 'simple-git';
|
||||
import { SETTING_GIT_COMMIT_MSG } from '../../constants';
|
||||
import { Folders } from '../../commands/Folders';
|
||||
|
||||
export class GitListener {
|
||||
private static client: SimpleGit | null = null;
|
||||
|
||||
/**
|
||||
* Process the messages
|
||||
* @param msg
|
||||
*/
|
||||
public static process(msg: { command: string, data: any }) {
|
||||
switch(msg.command) {
|
||||
case GeneralCommands.toVSCode.gitSync:
|
||||
this.sync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static async sync() {
|
||||
try {
|
||||
this.sendMsg(GeneralCommands.toWebview.gitSyncingStart, {});
|
||||
|
||||
await this.pull();
|
||||
await this.push();
|
||||
|
||||
this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {});
|
||||
} catch (e) {
|
||||
Logger.error((e as Error).message);
|
||||
this.sendMsg(GeneralCommands.toWebview.gitSyncingEnd, {});
|
||||
}
|
||||
}
|
||||
|
||||
public static async isGitRepository() {
|
||||
const git = this.getClient();
|
||||
if (!git) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isRepo = await git.checkIsRepo();
|
||||
|
||||
if (!isRepo) {
|
||||
Logger.warning(`Current workspace is not a GIT repository`);
|
||||
}
|
||||
|
||||
return isRepo;
|
||||
}
|
||||
|
||||
private static async pull() {
|
||||
const git = this.getClient();
|
||||
if (!git) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.info(`Pulling from remote`);
|
||||
await git.pull();
|
||||
}
|
||||
|
||||
private static async push() {
|
||||
const commitMsg = Settings.get<string>(SETTING_GIT_COMMIT_MSG);
|
||||
|
||||
const git = this.getClient();
|
||||
if (!git) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.info(`Pushing to remote`);
|
||||
|
||||
const status = await git.status();
|
||||
|
||||
for (const file of status.not_added) {
|
||||
await git.add(file);
|
||||
}
|
||||
for (const file of status.modified) {
|
||||
await git.add(file);
|
||||
}
|
||||
|
||||
await git.commit(commitMsg || "Synced by Front Matter")
|
||||
|
||||
await git.push();
|
||||
}
|
||||
|
||||
private static getClient() {
|
||||
const wsFolder = Folders.getWorkspaceFolder();
|
||||
|
||||
const options = {
|
||||
baseDir: wsFolder?.fsPath || "",
|
||||
binary: 'git',
|
||||
maxConcurrentProcesses: 6,
|
||||
}
|
||||
|
||||
return simpleGit(options);
|
||||
}
|
||||
|
||||
private static sendMsg(command: string, data: any) {
|
||||
|
||||
const extPath = Extension.getInstance().extensionPath;
|
||||
const panel = ExplorerView.getInstance(extPath);
|
||||
|
||||
panel.sendMessage({ command: command as any, data });
|
||||
|
||||
Dashboard.postWebviewMessage({ command: command as any, data });
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export * from './ModeListener';
|
||||
export * from './GitListener';
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
export interface GitSettings {
|
||||
isGitRepo: boolean;
|
||||
actions: boolean;
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
import { FileStat } from "vscode";
|
||||
import { DraftField } from ".";
|
||||
import { DraftField, GitSettings } from ".";
|
||||
import { Choice } from "./Choice";
|
||||
import { DashboardData } from "./DashboardData";
|
||||
import { DataType } from "./DataType";
|
||||
|
||||
export interface PanelSettings {
|
||||
git: GitSettings;
|
||||
seo: SEO;
|
||||
slug: Slug;
|
||||
tags: string[];
|
||||
|
||||
@@ -9,6 +9,7 @@ export * from './DataFolder';
|
||||
export * from './DataType';
|
||||
export * from './DraftField';
|
||||
export * from './Framework';
|
||||
export * from './GitSettings';
|
||||
export * from './MediaPaths';
|
||||
export * from './Mode';
|
||||
export * from './PanelSettings';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { SponsorMsg } from './components/SponsorMsg';
|
||||
import useMessages from './hooks/useMessages';
|
||||
import { FeatureFlag } from '../components/features/FeatureFlag';
|
||||
import { FEATURE_FLAG } from '../constants/Features';
|
||||
import { GitAction } from './components/Git/GitAction';
|
||||
|
||||
export interface IViewPanelProps {
|
||||
}
|
||||
@@ -41,6 +42,8 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<div className={`ext_actions`}>
|
||||
<GitAction settings={settings} />
|
||||
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.globalSettings}>
|
||||
<GlobalSettings settings={settings} />
|
||||
</FeatureFlag>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IActionButtonProps {
|
||||
title: string;
|
||||
title: JSX.Element | string;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
onClick: (e: React.SyntheticEvent<HTMLButtonElement>) => void;
|
||||
@@ -10,7 +10,9 @@ export interface IActionButtonProps {
|
||||
const ActionButton: React.FunctionComponent<IActionButtonProps> = ({className, onClick, disabled,title}: React.PropsWithChildren<IActionButtonProps>) => {
|
||||
return (
|
||||
<div className={`article__action`}>
|
||||
<button onClick={onClick} className={className || ""} disabled={disabled}>{title}</button>
|
||||
<button onClick={onClick} className={className || ""} disabled={disabled}>
|
||||
{title}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ import { StartServerButton } from './StartServerButton';
|
||||
import { FeatureFlag } from '../../components/features/FeatureFlag';
|
||||
import { FEATURE_FLAG } from '../../constants/Features';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { GitAction } from './Git/GitAction';
|
||||
|
||||
export interface IBaseViewProps {
|
||||
settings: PanelSettings | undefined;
|
||||
@@ -44,6 +45,8 @@ const BaseView: React.FunctionComponent<IBaseViewProps> = ({settings, folderAndF
|
||||
return (
|
||||
<div className="frontmatter">
|
||||
<div className={`ext_actions`}>
|
||||
<GitAction settings={settings} />
|
||||
|
||||
<FeatureFlag features={mode?.features || []} flag={FEATURE_FLAG.panel.globalSettings}>
|
||||
<GlobalSettings settings={settings} isBase />
|
||||
</FeatureFlag>
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
import { RefreshIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { GeneralCommands } from '../../../constants';
|
||||
import { PanelSettings } from '../../../models';
|
||||
import { ActionButton } from '../ActionButton';
|
||||
|
||||
export interface IGitActionProps {
|
||||
settings: PanelSettings | undefined
|
||||
}
|
||||
|
||||
export const GitAction: React.FunctionComponent<IGitActionProps> = ({ settings }: React.PropsWithChildren<IGitActionProps>) => {
|
||||
const [ isSyncing, setIsSyncing ] = useState(false);
|
||||
|
||||
const pull = () => {
|
||||
Messenger.send(GeneralCommands.toVSCode.gitSync);
|
||||
};
|
||||
|
||||
const messageListener = (message: MessageEvent<EventData<any>>) => {
|
||||
const { command, data } = message.data;
|
||||
|
||||
if (command === GeneralCommands.toWebview.gitSyncingStart) {
|
||||
setIsSyncing(true);
|
||||
} else if (command === GeneralCommands.toWebview.gitSyncingEnd) {
|
||||
setIsSyncing(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!settings?.git?.actions || !settings?.git.isGitRepo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='git_actions'>
|
||||
<ActionButton
|
||||
onClick={pull}
|
||||
title={(
|
||||
<div className='git_actions__sync'>
|
||||
<RefreshIcon className={isSyncing ? 'animate-spin' : ''} />
|
||||
<span>Sync</span>
|
||||
</div>
|
||||
)} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { Command } from '../Command';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { TagType } from '../TagType';
|
||||
import { Messenger } from '@estruyf/vscode/dist/client';
|
||||
import { EventData } from '@estruyf/vscode/dist/models';
|
||||
|
||||
export default function useMessages() {
|
||||
const [metadata, setMetadata] = useState<any>({});
|
||||
@@ -17,7 +18,7 @@ export default function useMessages() {
|
||||
const [mediaSelecting, setMediaSelecting] = useState<DashboardData | undefined>(undefined);
|
||||
const [mode, setMode] = useState<Mode | undefined>(undefined);
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const messageListener = (event: MessageEvent<EventData<any>>) => {
|
||||
const message = event.data;
|
||||
|
||||
switch (message.command) {
|
||||
@@ -48,7 +49,7 @@ export default function useMessages() {
|
||||
setMode(message.data);
|
||||
break;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
@@ -59,6 +60,7 @@ export default function useMessages() {
|
||||
}, [loading])
|
||||
|
||||
useEffect(() => {
|
||||
Messenger.listen(messageListener);
|
||||
setLoading(true);
|
||||
|
||||
// Show what you got after 5 seconds
|
||||
@@ -68,6 +70,10 @@ export default function useMessages() {
|
||||
|
||||
Messenger.send(CommandToCode.getData);
|
||||
Messenger.send(CommandToCode.getMode);
|
||||
|
||||
return () => {
|
||||
Messenger.unlisten(messageListener);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,4 +1,14 @@
|
||||
/* Animations */
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* CSS */
|
||||
.block_field__form,
|
||||
.list_field__form {
|
||||
background-color: transparent;
|
||||
@@ -508,6 +518,23 @@ vscode-divider {
|
||||
}
|
||||
}
|
||||
|
||||
/* Git actions */
|
||||
.git_actions__sync {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.git_actions__sync svg {
|
||||
height: 1.25rem;
|
||||
width: 1.25rem;
|
||||
margin-right: .5rem;
|
||||
}
|
||||
|
||||
.animate-spin {
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
/* Quill changes */
|
||||
.ql-toolbar.ql-snow,
|
||||
.ql-container.ql-snow {
|
||||
|
||||
@@ -6,6 +6,16 @@ module.exports = {
|
||||
darkMode: 'class', // or 'media' or 'class'
|
||||
theme: {
|
||||
extend: {
|
||||
animation: {
|
||||
'reverse-spin': 'reverse-spin 1s linear infinite'
|
||||
},
|
||||
keyframes: {
|
||||
'reverse-spin': {
|
||||
from: {
|
||||
transform: 'rotate(360deg)'
|
||||
},
|
||||
}
|
||||
},
|
||||
colors: {
|
||||
white: colors.white,
|
||||
gray: colors.trueGray,
|
||||
|
||||
Reference in New Issue
Block a user