diff --git a/CHANGELOG.md b/CHANGELOG.md index 44f70fd6..819fa0f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [#777](https://github.com/estruyf/vscode-front-matter/issues/777): Show an error in the metadata panel if something went wrong while parsing the front matter - [#778](https://github.com/estruyf/vscode-front-matter/issues/778): Added the ability to open a file or webpage when custom scripts is completed - [#783](https://github.com/estruyf/vscode-front-matter/issues/783): Always show the custom panel view +- [#785](https://github.com/estruyf/vscode-front-matter/issues/785): Adding common actions at the bottom of the content and media cards ### ⚡️ Optimizations diff --git a/src/dashboardWebView/components/Contents/ContentActions.tsx b/src/dashboardWebView/components/Contents/ContentActions.tsx index b96825e8..21c903cd 100644 --- a/src/dashboardWebView/components/Contents/ContentActions.tsx +++ b/src/dashboardWebView/components/Contents/ContentActions.tsx @@ -1,22 +1,20 @@ import { Messenger, messageHandler } from '@estruyf/vscode/dist/client'; -import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, EllipsisVerticalIcon, LanguageIcon } from '@heroicons/react/24/outline'; +import { EyeIcon, GlobeEuropeAfricaIcon, CommandLineIcon, TrashIcon, LanguageIcon, EllipsisHorizontalIcon } from '@heroicons/react/24/outline'; import * as React from 'react'; import { CustomScript, I18nConfig, ScriptType } from '../../../models'; import { DashboardMessage } from '../../DashboardMessage'; -import { QuickAction } from '../Menu'; -import { Alert } from '../Modals/Alert'; import * as l10n from '@vscode/l10n'; import { LocalizationKey } from '../../../localization'; import { useRecoilState, useRecoilValue } from 'recoil'; -import { SettingsSelector } from '../../state'; +import { SelectedItemActionAtom, SettingsSelector } from '../../state'; import { COMMAND_NAME, GeneralCommands } from '../../../constants'; import { PinIcon } from '../Icons/PinIcon'; import { PinnedItemsAtom } from '../../state/atom/PinnedItems'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuPortal, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger } from '../../../components/shadcn/Dropdown'; import { RenameIcon } from '../../../components/icons/RenameIcon'; +import { openFile, openOnWebsite } from '../../utils'; export interface IContentActionsProps { - title: string; path: string; relPath: string; scripts: CustomScript[] | undefined; @@ -33,7 +31,6 @@ export interface IContentActionsProps { } export const ContentActions: React.FunctionComponent = ({ - title, path, relPath, scripts, @@ -43,8 +40,8 @@ export const ContentActions: React.FunctionComponent = ({ translations, locale }: React.PropsWithChildren) => { + const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); const [pinnedItems, setPinnedItems] = useRecoilState(PinnedItemsAtom); - const [showDeletionAlert, setShowDeletionAlert] = React.useState(false); const settings = useRecoilValue(SettingsSelector); const onView = (e: React.MouseEvent) => { @@ -52,35 +49,19 @@ export const ContentActions: React.FunctionComponent = ({ onOpen(); }; - const onDelete = (e: React.MouseEvent) => { + const onDelete = React.useCallback((e: React.MouseEvent) => { e.stopPropagation(); - setShowDeletionAlert(true); - }; + setSelectedItemAction({ path, action: 'delete' }); + }, [path]); const onRename = React.useCallback((e: React.MouseEvent) => { e.stopPropagation(); messageHandler.send(DashboardMessage.rename, path); }, [path]) - const onDeleteConfirm = () => { - if (path) { - Messenger.send(DashboardMessage.deleteFile, path); - } - setShowDeletionAlert(false); - }; - - const onOpenFile = (filePath: string) => { - messageHandler.send(DashboardMessage.openFile, filePath); - } - - const openOnWebsite = React.useCallback((e: React.MouseEvent) => { + const onOpenWebsite = React.useCallback((e: React.MouseEvent) => { e.stopPropagation(); - if (settings?.websiteUrl && path) { - Messenger.send(GeneralCommands.toVSCode.openOnWebsite, { - websiteUrl: settings.websiteUrl, - filePath: path - }); - } + openOnWebsite(settings?.websiteUrl, path); }, [settings?.websiteUrl, path]); const pinItem = React.useCallback((e: React.MouseEvent) => { @@ -153,7 +134,7 @@ export const ContentActions: React.FunctionComponent = ({ - onOpenFile(crntLocale.path)}> + openFile(crntLocale.path)}> {crntLocale.locale.title || crntLocale.locale.locale} @@ -163,7 +144,7 @@ export const ContentActions: React.FunctionComponent = ({ otherLocales.map(([key, value]) => ( onOpenFile(value.path)} + onClick={() => openFile(value.path)} > {value.locale.title || value.locale.locale} @@ -178,41 +159,19 @@ export const ContentActions: React.FunctionComponent = ({ return ( <>
- {!listView && ( -
- - - - { - settings?.websiteUrl && ( - - - ) - } - - - -
- )} - - + {l10n.t(LocalizationKey.dashboardContentsContentActionsActionMenuButtonTitle)} - @@ -233,7 +192,7 @@ export const ContentActions: React.FunctionComponent = ({ { settings?.websiteUrl && ( - + {l10n.t(LocalizationKey.commonOpenOnWebsite)} @@ -263,17 +222,6 @@ export const ContentActions: React.FunctionComponent = ({
- - {showDeletionAlert && ( - setShowDeletionAlert(false)} - trigger={onDeleteConfirm} - /> - )} ); }; diff --git a/src/dashboardWebView/components/Contents/Contents.tsx b/src/dashboardWebView/components/Contents/Contents.tsx index 625e1af1..45a20f20 100644 --- a/src/dashboardWebView/components/Contents/Contents.tsx +++ b/src/dashboardWebView/components/Contents/Contents.tsx @@ -1,17 +1,21 @@ import * as React from 'react'; -import { useRecoilValue } from 'recoil'; +import * as l10n from '@vscode/l10n'; +import { useRecoilState, useRecoilValue } from 'recoil'; import { Page } from '../../models'; -import { LoadingAtom, SettingsSelector } from '../../state'; +import { LoadingAtom, SelectedItemActionAtom, SettingsSelector } from '../../state'; import { Overview } from './Overview'; import { Spinner } from '../Common/Spinner'; import { SponsorMsg } from '../Layout/SponsorMsg'; import usePages from '../../hooks/usePages'; -import { useEffect } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { Messenger } from '@estruyf/vscode/dist/client'; import { DashboardMessage } from '../../DashboardMessage'; import { TelemetryEvent } from '../../../constants'; import { PageLayout } from '../Layout/PageLayout'; import { FilesProvider } from '../../providers/FilesProvider'; +import { Alert } from '../Modals/Alert'; +import { LocalizationKey } from '../../../localization'; +import { deletePage } from '../../utils'; export interface IContentsProps { pages: Page[]; @@ -23,9 +27,38 @@ export const Contents: React.FunctionComponent = ({ const loading = useRecoilValue(LoadingAtom); const settings = useRecoilValue(SettingsSelector); const { pageItems } = usePages(pages); + const [showDeletionAlert, setShowDeletionAlert] = React.useState(false); + const [page, setPage] = useState(undefined); + const [selectedItemAction, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); const pageFolders = [...new Set(pageItems.map((page) => page.fmFolder))]; + const onDismiss = useCallback(() => { + setShowDeletionAlert(false); + setSelectedItemAction(undefined); + }, []); + + const onDeleteConfirm = useCallback(() => { + if (page) { + deletePage(page.fmFilePath); + } + setShowDeletionAlert(false); + setSelectedItemAction(undefined); + }, [page]); + + useEffect(() => { + if (selectedItemAction && selectedItemAction.path && selectedItemAction.action === 'delete') { + const page = pageItems.find((p) => p.fmFilePath === selectedItemAction.path); + + if (page) { + setPage(page); + setShowDeletionAlert(true); + } + + setSelectedItemAction(undefined); + } + }, [pageItems, selectedItemAction]); + useEffect(() => { Messenger.send(DashboardMessage.sendTelemetry, { event: TelemetryEvent.webviewContentsView @@ -46,6 +79,17 @@ export const Contents: React.FunctionComponent = ({ /> Content metrics + + {showDeletionAlert && page && ( + + )} ); diff --git a/src/dashboardWebView/components/Contents/FooterActions.tsx b/src/dashboardWebView/components/Contents/FooterActions.tsx new file mode 100644 index 00000000..26961933 --- /dev/null +++ b/src/dashboardWebView/components/Contents/FooterActions.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import * as l10n from '@vscode/l10n'; +import { QuickAction } from '../Menu'; +import { EyeIcon, GlobeEuropeAfricaIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { LocalizationKey } from '../../../localization'; +import { openFile, openOnWebsite } from '../../utils'; +import { useRecoilState } from 'recoil'; +import { SelectedItemActionAtom } from '../../state'; + +export interface IFooterActionsProps { + filePath: string; + websiteUrl?: string; +} + +export const FooterActions: React.FunctionComponent = ({ + filePath, + websiteUrl +}: React.PropsWithChildren) => { + const [, setSelectedItemAction] = useRecoilState(SelectedItemActionAtom); + + return ( +
+ openFile(filePath)}> + + + { + websiteUrl && ( + openOnWebsite(websiteUrl, filePath)}> + + ) + } + + setSelectedItemAction({ path: filePath, action: 'delete' })}> + +
+ ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Contents/Item.tsx b/src/dashboardWebView/components/Contents/Item.tsx index 89fc51d7..7bd72f5e 100644 --- a/src/dashboardWebView/components/Contents/Item.tsx +++ b/src/dashboardWebView/components/Contents/Item.tsx @@ -1,10 +1,8 @@ import { useRecoilValue } from 'recoil'; import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon'; -import { DashboardMessage } from '../../DashboardMessage'; import { Page } from '../../models/Page'; import { SettingsSelector, ViewSelector } from '../../state'; import { DateField } from '../Common/DateField'; -import { Messenger } from '@estruyf/vscode/dist/client'; import { DashboardViewType } from '../../models'; import { ContentActions } from './ContentActions'; import { useMemo } from 'react'; @@ -18,6 +16,8 @@ import { routePaths } from '../..'; import useCard from '../../hooks/useCard'; import { I18nLabel } from './I18nLabel'; import { ItemSelection } from '../Common/ItemSelection'; +import { openFile } from '../../utils'; +import { FooterActions } from './FooterActions'; export interface IItemProps extends Page { } @@ -41,9 +41,9 @@ export const Item: React.FunctionComponent = ({ pageData }); - const openFile = () => { - Messenger.send(DashboardMessage.openFile, pageData.fmFilePath); - }; + const onOpenFile = React.useCallback(() => { + openFile(pageData.fmFilePath); + }, [pageData.fmFilePath]); const tags: string[] | undefined = useMemo(() => { if (!settings?.dashboardState?.contents?.tags) { @@ -92,9 +92,9 @@ export const Item: React.FunctionComponent = ({ return ( dateHtml ? ( -
+
) : ( - cardFields?.date && pageData.date ? : null + cardFields?.date && pageData.date ? : null ) ) }, [dateHtml, cardFields?.date, pageData]); @@ -111,7 +111,7 @@ export const Item: React.FunctionComponent = ({ >
diff --git a/src/dashboardWebView/components/Contents/PinnedItem.tsx b/src/dashboardWebView/components/Contents/PinnedItem.tsx index d368f98e..98eddd2c 100644 --- a/src/dashboardWebView/components/Contents/PinnedItem.tsx +++ b/src/dashboardWebView/components/Contents/PinnedItem.tsx @@ -2,12 +2,11 @@ import * as React from 'react'; import { Page } from '../../models'; import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon'; import { ContentActions } from './ContentActions'; -import { DashboardMessage } from '../../DashboardMessage'; -import { messageHandler } from '@estruyf/vscode/dist/client'; import useCard from '../../hooks/useCard'; import { SettingsSelector } from '../../state'; import { useRecoilValue } from 'recoil'; import { ItemSelection } from '../Common/ItemSelection'; +import { openFile } from '../../utils'; export interface IPinnedItemProps extends Page { } @@ -17,13 +16,13 @@ export const PinnedItem: React.FunctionComponent = ({ const settings = useRecoilValue(SettingsSelector); const { escapedTitle } = useCard(pageData, settings?.dashboardState?.contents?.cardFields); - const openFile = React.useCallback(() => { - messageHandler.send(DashboardMessage.openFile, pageData.fmFilePath); + const onOpenFile = React.useCallback(() => { + openFile(pageData.fmFilePath); }, [pageData.fmFilePath]); return (
  • -