#395: Media snippet enhancements

This commit is contained in:
Elio Struyf
2022-09-09 18:01:55 +02:00
parent 9b21e15c63
commit 9f37ff773e
8 changed files with 126 additions and 14 deletions
+1
View File
@@ -20,6 +20,7 @@
- [#388](https://github.com/estruyf/vscode-front-matter/issues/388): New stop server action has been added to the panel
- [#390](https://github.com/estruyf/vscode-front-matter/issues/390): Implement another JSON parser in order to be able to parse the `frontmatter.json` file better
- [#394](https://github.com/estruyf/vscode-front-matter/issues/394): Ordering of snippet fields is based on their field definition
- [#395](https://github.com/estruyf/vscode-front-matter/issues/395): Added support for custom snippet fields on media snippets
- [#402](https://github.com/estruyf/vscode-front-matter/issues/402): Custom sorting of content now supports `number` fields
### ⚡️ Optimizations
+3 -1
View File
@@ -364,6 +364,7 @@ export class Article {
const contentType = article && article.data ? ArticleHelper.getContentType(article.data) : DEFAULT_CONTENT_TYPE;
const position = editor.selection.active;
const selectionText = editor.document.getText(editor.selection);
await vscode.commands.executeCommand(COMMAND_NAME.dashboard, {
type: "media",
@@ -371,7 +372,8 @@ export class Article {
pageBundle: !!contentType.pageBundle,
filePath: editor.document.uri.fsPath,
fieldName: basename(editor.document.uri.fsPath),
position
position,
selection: selectionText
}
} as DashboardData);
+1
View File
@@ -22,6 +22,7 @@ export const TelemetryEvent = {
uploadMedia: 'uploadMedia',
refreshMedia: 'refreshMedia',
deleteMedia: 'deleteMedia',
insertContentSnippet: 'insertContentSnippet',
insertMediaToContent: 'insertMediaToContent',
insertFileToContent: 'insertFileToContent',
updateMediaMetadata: 'updateMediaMetadata',
+50 -2
View File
@@ -19,6 +19,7 @@ import { Alert } from '../Modals/Alert';
import { InfoDialog } from '../Modals/InfoDialog';
import { DetailsSlideOver } from './DetailsSlideOver';
import { usePopper } from 'react-popper';
import { MediaSnippetForm } from './MediaSnippetForm';
export interface IItemProps {
media: MediaInfo;
@@ -29,7 +30,10 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
const [ showAlert, setShowAlert ] = useState(false);
const [ showForm, setShowForm ] = useState(false);
const [ showSnippetSelection, setShowSnippetSelection ] = useState(false);
const [ snippet, setSnippet ] = useState<Snippet | undefined>(undefined);
const [ showDetails, setShowDetails ] = useState(false);
const [ showSnippetFormDialog, setShowSnippetFormDialog ] = useState(false);
const [ mediaData, setMediaData ] = useState<any | undefined>(undefined);
const [ caption, setCaption ] = useState(media.caption);
const [ alt, setAlt ] = useState(media.alt);
const [ filename, setFilename ] = useState<string | null>(null);
@@ -136,6 +140,9 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
}
}, [mediaSnippets]);
/**
* Process the snippet
*/
const processSnippet = useCallback((snippet: Snippet) => {
setShowSnippetSelection(false);
@@ -151,7 +158,24 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
mediaHeight: media?.dimensions?.height?.toString() || "",
};
const output = SnippetParser.render(snippet.body, fieldData, snippet?.openingTags, snippet?.closingTags);
if (snippet.fields.length === 0) {
setShowSnippetFormDialog(false);
setMediaData(undefined);
const output = SnippetParser.render(snippet.body, fieldData, snippet?.openingTags, snippet?.closingTags);
insertMediaSnippetToArticle(output);
} else {
setSnippet(snippet);
setShowSnippetFormDialog(true);
setMediaData(fieldData);
}
}, [alt, caption, media, settings, viewData, mediaSnippets]);
/**
* Insert the media snippet
*/
const insertMediaSnippetToArticle = useCallback((output: string) => {
const relPath = getRelPath();
Messenger.send(DashboardMessage.insertMedia, {
relPath: parseWinPath(relPath) || "",
@@ -160,7 +184,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
position: viewData?.data?.position || null,
snippet: output
});
}, [alt, caption, media, settings, viewData, mediaSnippets]);
}, [viewData]);
const deleteMedia = () => {
setShowAlert(true);
@@ -300,6 +324,12 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
return null;
}, [media]);
const clearFormData = () => {
setShowSnippetFormDialog(false);
setSnippet(undefined);
setMediaData(undefined);
}
useEffect(() => {
if (media.alt !== alt) {
setAlt(media.alt);
@@ -319,6 +349,12 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
}
}, [media.fsPath]);
useEffect(() => {
if (!viewData?.data?.filePath) {
clearFormData();
}
}, [viewData]);
return (
<>
<li className="group relative bg-gray-50 dark:bg-vulcan-200 shadow-md hover:shadow-xl dark:shadow-none dark:hover:bg-vulcan-100 border border-gray-200 dark:border-vulcan-50">
@@ -540,6 +576,18 @@ export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWi
trigger={confirmDeletion} />
)
}
{
(showSnippetFormDialog && snippet && mediaData) && (
<MediaSnippetForm
media={media}
mediaData={mediaData}
snippet={snippet}
onDismiss={clearFormData}
onInsert={insertMediaSnippetToArticle}
/>
)
}
</>
);
};
@@ -0,0 +1,45 @@
import * as React from 'react';
import { useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { MediaInfo, Snippet } from '../../../models';
import { ViewDataSelector } from '../../state';
import { FormDialog } from '../Modals/FormDialog';
import SnippetForm, { SnippetFormHandle } from '../SnippetsView/SnippetForm';
export interface IMediaSnippetFormProps {
media: MediaInfo;
snippet: Snippet;
mediaData: any;
onDismiss: () => void;
onInsert: (output: string) => void;
}
export const MediaSnippetForm: React.FunctionComponent<IMediaSnippetFormProps> = ({ media, snippet, mediaData, onDismiss, onInsert }: React.PropsWithChildren<IMediaSnippetFormProps>) => {
const viewData = useRecoilValue(ViewDataSelector);
const formRef = useRef<SnippetFormHandle>(null);
const insertToArticle = () => {
formRef.current?.onSave();
onDismiss();
};
return (
<FormDialog
title={`Insert media: ${media.title || media.filename}`}
description={`Insert the ${media.title || media.filename} media file into the current article`}
isSaveDisabled={false}
trigger={insertToArticle}
dismiss={onDismiss}
okBtnText='Insert'
cancelBtnText='Cancel'>
<SnippetForm
ref={formRef}
snippet={snippet}
mediaData={mediaData}
selection={viewData?.data?.selection}
onInsert={onInsert} />
</FormDialog>
);
};
@@ -7,7 +7,6 @@ import { FeatureFlag } from '../../../components/features/FeatureFlag';
import { FEATURE_FLAG } from '../../../constants';
import { SnippetParser } from '../../../helpers/SnippetParser';
import { Snippet, Snippets } from '../../../models';
import { FileIcon } from '../../../panelWebView/components/Icons/FileIcon';
import { DashboardMessage } from '../../DashboardMessage';
import { ModeAtom, SettingsSelector, ViewDataSelector } from '../../state';
import { QuickAction } from '../Menu';
@@ -13,13 +13,15 @@ import { SnippetInputField } from './SnippetInputField';
export interface ISnippetFormProps {
snippet: Snippet;
selection: string | undefined;
mediaData?: any;
onInsert?: (mediaData: any) => void;
}
export interface SnippetFormHandle {
onSave: () => void;
}
const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFormProps> = ({ snippet, selection }, ref) => {
const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFormProps> = ({ snippet, selection, mediaData, onInsert }, ref) => {
const viewData = useRecoilValue(ViewDataSelector);
const [ fields, setFields ] = useState<SnippetField[]>([]);
const settings = useRecoilValue(SettingsAtom);
@@ -38,6 +40,16 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
return value;
}, [selection]);
const insertValueFromMedia = useCallback((fieldName: string) => {
if (!mediaData) {
return "";
}
if (mediaData[fieldName]) {
return mediaData[fieldName];
}
}, [mediaData]);
const snippetBody = useMemo(() => {
let body = typeof snippet.body === "string" ? snippet.body : snippet.body.join(`\n`);
@@ -63,10 +75,14 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
return;
}
Messenger.send(DashboardMessage.insertSnippet, {
file: viewData?.data?.filePath,
snippet: snippetBody
});
if (!onInsert) {
Messenger.send(DashboardMessage.insertSnippet, {
file: viewData?.data?.filePath,
snippet: snippetBody
});
} else {
onInsert(snippetBody);
}
}
}));
@@ -99,7 +115,7 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
title: fieldName,
type: "string",
single: true,
value: ""
value: insertValueFromMedia(fieldName)
});
}
}
+4 -4
View File
@@ -1,10 +1,9 @@
import { EditorHelper } from "@estruyf/vscode";
import { Position, window } from "vscode";
import { window } from "vscode";
import { Dashboard } from "../../commands/Dashboard";
import { SETTING_CONTENT_SNIPPETS } from "../../constants";
import { SETTING_CONTENT_SNIPPETS, TelemetryEvent } from "../../constants";
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
import { Notifications, Settings } from "../../helpers";
import { Snippet } from "../../models";
import { Notifications, Settings, Telemetry } from "../../helpers";
import { BaseListener } from "./BaseListener";
import { SettingsListener } from "./SettingsListener";
@@ -22,6 +21,7 @@ export class SnippetListener extends BaseListener {
this.updateSnippet(msg.data);
break;
case DashboardMessage.insertSnippet:
Telemetry.send(TelemetryEvent.insertContentSnippet);
this.insertSnippet(msg.data);
break;
}