From bd09a10fd85c02b8a6fd09d1d184361279b9c317 Mon Sep 17 00:00:00 2001 From: Elio Struyf Date: Wed, 30 Mar 2022 11:47:25 +0200 Subject: [PATCH] #303 - Content card actions --- CHANGELOG.md | 1 + src/dashboardWebView/DashboardMessage.ts | 3 +- .../components/Contents/ContentActions.tsx | 100 ++++++++++++++++++ .../components/Contents/Item.tsx | 8 ++ .../components/Media/Item.tsx | 22 ++-- .../components/Media/MenuButton.tsx | 20 ---- .../components/Menu/ActionMenuButton.tsx | 18 ++++ .../components/Menu/MenuItem.tsx | 4 +- .../components/Menu/MenuItems.tsx | 2 +- .../{Media => Menu}/QuickAction.tsx | 2 +- src/dashboardWebView/components/Menu/index.ts | 2 + .../components/SnippetsView/Item.tsx | 32 +++--- src/helpers/CustomScript.ts | 34 ++++-- src/helpers/openFileInEditor.ts | 2 + src/listeners/dashboard/PagesListener.ts | 25 ++++- 15 files changed, 215 insertions(+), 60 deletions(-) create mode 100644 src/dashboardWebView/components/Contents/ContentActions.tsx delete mode 100644 src/dashboardWebView/components/Media/MenuButton.tsx create mode 100644 src/dashboardWebView/components/Menu/ActionMenuButton.tsx rename src/dashboardWebView/components/{Media => Menu}/QuickAction.tsx (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1b87aa..547796a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#293](https://github.com/estruyf/vscode-front-matter/issues/293): Support added for setting preview images in block fields - [#294](https://github.com/estruyf/vscode-front-matter/issues/294): Full-text search allows you to search through all your page content - [#301](https://github.com/estruyf/vscode-front-matter/issues/301): Show tags on the content cards +- [#303](https://github.com/estruyf/vscode-front-matter/issues/303): Content card actions to quickly view, delete, or run custom scripts ### ⚡️ Optimizations diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index 4ea582e7..d04001e5 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -10,9 +10,10 @@ export enum DashboardMessage { createByTemplate = 'createByTemplate', refreshPages = 'refreshPages', searchPages = 'searchPages', + openFile = 'openFile', + deleteFile = 'deleteFile', // Media Dashboard - openFile = 'openFile', getMedia = 'getMedia', copyToClipboard = 'copyToClipboard', refreshMedia = 'refreshMedia', diff --git a/src/dashboardWebView/components/Contents/ContentActions.tsx b/src/dashboardWebView/components/Contents/ContentActions.tsx new file mode 100644 index 00000000..95e231f7 --- /dev/null +++ b/src/dashboardWebView/components/Contents/ContentActions.tsx @@ -0,0 +1,100 @@ +import { Messenger } from '@estruyf/vscode/dist/client'; +import { Menu } from '@headlessui/react'; +import { EyeIcon, TrashIcon } from '@heroicons/react/outline'; +import * as React from 'react'; +import { CustomScript, ScriptType } from '../../../models'; +import { DashboardMessage } from '../../DashboardMessage'; +import { MenuItem, MenuItems, ActionMenuButton, QuickAction } from '../Menu'; +import { Alert } from '../Modals/Alert'; + +export interface IContentActionsProps { + title: string; + path: string; + scripts: CustomScript[] | undefined; + onOpen: () => void; +} + +export const ContentActions: React.FunctionComponent = ({ title, path, scripts, onOpen }: React.PropsWithChildren) => { + const [ showDeletionAlert, setShowDeletionAlert ] = React.useState(false); + + const onView = (e: React.MouseEvent) => { + e.stopPropagation(); + onOpen(); + }; + + const onDelete = (e: React.MouseEvent) => { + e.stopPropagation(); + setShowDeletionAlert(true); + }; + + const onDeleteConfirm = () => { + if (path) { + Messenger.send(DashboardMessage.deleteFile, path); + } + } + + const runCustomScript = React.useCallback((e: React.MouseEvent, script: CustomScript) => { + e.stopPropagation(); + Messenger.send(DashboardMessage.runCustomScript, {script, path}); + }, [path]); + + const customScriptActions = React.useMemo(() => { + return (scripts || []).filter(script => (script.type === undefined || script.type === ScriptType.Content) && !script.bulk).map(script => ( + runCustomScript(e, script)} /> + )) + }, [scripts]); + + return ( + <> +
+
+
+ + + + + +
+ + + + + + + + { customScriptActions } + + onDelete(e)} /> + + +
+
+ + + { + showDeletionAlert && ( + setShowDeletionAlert(false)} + trigger={onDeleteConfirm} /> + ) + } + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Contents/Item.tsx b/src/dashboardWebView/components/Contents/Item.tsx index 15fc1c40..27537538 100644 --- a/src/dashboardWebView/components/Contents/Item.tsx +++ b/src/dashboardWebView/components/Contents/Item.tsx @@ -8,6 +8,7 @@ import { DateField } from '../DateField'; import { Status } from '../Status'; import { Messenger } from '@estruyf/vscode/dist/client'; import { DashboardViewType } from '../../models'; +import { ContentActions } from './ContentActions'; export interface IItemProps extends Page {} @@ -35,6 +36,7 @@ export const Item: React.FunctionComponent = ({ fmFilePath, date, ti
  • + setShowInsertDialog(true)}> + ) } - + + - + setShowAlert(true)}> + diff --git a/src/helpers/CustomScript.ts b/src/helpers/CustomScript.ts index 719b3915..89f8025b 100644 --- a/src/helpers/CustomScript.ts +++ b/src/helpers/CustomScript.ts @@ -30,6 +30,9 @@ export class CustomScript { if (script.bulk) { // Run script on all files CustomScript.bulkRun(wsPath, script); + } else if (path) { + // Run script for provided path + CustomScript.singleRun(wsPath, script, path); } else { // Run script on current file. CustomScript.singleRun(wsPath, script); @@ -38,18 +41,31 @@ export class CustomScript { } } - private static async singleRun(wsPath: string, script: ICustomScript): Promise { - const editor = window.activeTextEditor; - if (!editor) return; + private static async singleRun(wsPath: string, script: ICustomScript, path: string | null = null): Promise { + let articlePath: string | null = path; + let article: ParsedFrontMatter | null = null; - const article = ArticleHelper.getFrontMatter(editor); + if (!path) { + const editor = window.activeTextEditor; + if (!editor) return; - if (article) { - const output = await CustomScript.runScript(wsPath, article, editor.document.uri.fsPath, script); - - CustomScript.showOutput(output, script); + articlePath = editor.document.uri.fsPath; + article = ArticleHelper.getFrontMatter(editor); } else { - Notifications.warning(`${script.title}: Current article couldn't be retrieved.`); + article = ArticleHelper.getFrontMatterByPath(path); + } + + if (articlePath && article) { + window.withProgress({ + location: ProgressLocation.Notification, + title: `Executing: ${script.title}`, + cancellable: false + }, async () => { + const output = await CustomScript.runScript(wsPath, article, articlePath as string, script); + CustomScript.showOutput(output, script); + }); + } else { + Notifications.warning(`${script.title}: Article couldn't be retrieved.`); } } diff --git a/src/helpers/openFileInEditor.ts b/src/helpers/openFileInEditor.ts index c0cc79e8..0545b2d0 100644 --- a/src/helpers/openFileInEditor.ts +++ b/src/helpers/openFileInEditor.ts @@ -1,4 +1,5 @@ import { Uri, workspace, window } from "vscode"; +import { Logger } from "./Logger"; import { Notifications } from "./Notifications"; export const openFileInEditor = async (filePath: string) => { @@ -8,6 +9,7 @@ export const openFileInEditor = async (filePath: string) => { await window.showTextDocument(doc, 1, false); } catch (e) { Notifications.error(`Couldn't open the file.`); + Logger.error(`${filePath}: ${(e as Error).message}`); } } }; \ No newline at end of file diff --git a/src/listeners/dashboard/PagesListener.ts b/src/listeners/dashboard/PagesListener.ts index 0f870c0a..ad9f23be 100644 --- a/src/listeners/dashboard/PagesListener.ts +++ b/src/listeners/dashboard/PagesListener.ts @@ -1,5 +1,5 @@ import { isValidFile } from '../../helpers/isValidFile'; -import { existsSync } from "fs"; +import { existsSync, unlinkSync } from "fs"; import { basename, dirname, join } from "path"; import { commands, FileSystemWatcher, RelativePattern, TextDocument, Uri, workspace } from "vscode"; import { Dashboard } from "../../commands/Dashboard"; @@ -48,6 +48,9 @@ export class PagesListener extends BaseListener { case DashboardMessage.searchPages: this.searchPages(msg.data); break; + case DashboardMessage.deleteFile: + this.deletePage(msg.data); + break; } } @@ -96,6 +99,26 @@ export class PagesListener extends BaseListener { } } + /** + * Delete a page + * @param path + */ + private static async deletePage(path: string) { + if (!path) { + return; + } + + Logger.info(`Deleting file: ${path}`) + + unlinkSync(path); + + this.lastPages = this.lastPages.filter(p => p.fmFilePath !== path); + this.sendPageData(this.lastPages); + + const ext = Extension.getInstance(); + await ext.setState(ExtensionState.Dashboard.Pages.Cache, this.lastPages, "workspace"); + } + /** * Watcher for processing page updates * @param file