#412 - Allow title on snippets

This commit is contained in:
Elio Struyf
2022-11-07 17:28:40 +01:00
parent 1de14122c5
commit e098442eaa
7 changed files with 81 additions and 38 deletions
+4
View File
@@ -271,6 +271,10 @@
"type": "string"
}
},
"title": {
"description": "The snippet title.",
"type": "string"
},
"description": {
"description": "The snippet description.",
"type": "string"
@@ -65,8 +65,6 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
return [];
}, [settings, pageData]);
console.log(pageData[PREVIEW_IMAGE_FIELD])
if (view === DashboardViewType.Grid) {
return (
<li className="relative">
@@ -1,5 +1,5 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { CodeIcon, DocumentTextIcon, DotsHorizontalIcon, PencilIcon, PhotographIcon, PlusIcon, TrashIcon } from '@heroicons/react/outline';
import { CodeIcon, DocumentTextIcon, DotsHorizontalIcon, EyeIcon, PencilIcon, PhotographIcon, PlusIcon, TrashIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useCallback, useMemo, useRef, useState } from 'react';
import { useRecoilValue } from 'recoil';
@@ -16,11 +16,11 @@ import { NewForm } from './NewForm';
import SnippetForm, { SnippetFormHandle } from './SnippetForm';
export interface IItemProps {
title: string;
snippetKey: string;
snippet: Snippet;
}
export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: React.PropsWithChildren<IItemProps>) => {
export const Item: React.FunctionComponent<IItemProps> = ({ snippetKey, snippet }: React.PropsWithChildren<IItemProps>) => {
const viewData = useRecoilValue(ViewDataSelector);
const settings = useRecoilValue(SettingsSelector);
const mode = useRecoilValue(ModeAtom);
@@ -50,13 +50,17 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
setMediaSnippet(false);
};
const showFile = useCallback(() => {
Messenger.send(DashboardMessage.openFile, snippet.sourcePath);
}, [ snippet ]);
const onOpenEdit = useCallback(() => {
setSnippetTitle(title);
setSnippetTitle(snippet.title || snippetKey);
setSnippetDescription(snippet.description);
setSnippetOriginalBody(typeof snippet.body === "string" ? snippet.body : snippet.body.join(`\n`));
setShowEditDialog(true);
setMediaSnippet(!!snippet.isMediaSnippet);
}, [snippet]);
}, [snippet, snippetKey]);
const onSnippetUpdate = useCallback(() => {
if (!snippetTitle || !snippetOriginalBody) {
@@ -64,10 +68,10 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
return;
}
const snippets: Snippets = Object.assign({}, settings?.snippets || {});
let snippets: Snippets = Object.assign({}, settings?.snippets || {});
const snippetLines = snippetOriginalBody.split("\n");
const crntSnippet = Object.assign({}, snippets[title]);
const crntSnippet = Object.assign({}, snippets[snippetKey]);
const fields = SnippetParser.getFields(snippetLines, crntSnippet.fields || [], crntSnippet?.openingTags, crntSnippet?.closingTags);
@@ -83,27 +87,33 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
snippetContents.isMediaSnippet = true;
}
// Check if new or update
if (title === snippetTitle) {
snippets[title] = snippetContents;
// Check if there is a title set in the snippet
if (snippet.title) {
snippetContents.title = snippetTitle;
snippets[snippetKey] = snippetContents;
} else {
delete snippets[title];
snippets[snippetTitle] = snippetContents;
// Check if new or update
if (snippetKey === snippetTitle) {
snippets[snippetKey] = snippetContents;
} else {
delete snippets[snippetKey];
snippets[snippetTitle] = snippetContents;
}
}
Messenger.send(DashboardMessage.updateSnippet, { snippets });
reset();
}, [settings?.snippets, title, snippetTitle, snippetDescription, snippetOriginalBody, mediaSnippet]);
}, [settings?.snippets, snippetKey, snippetTitle, snippetDescription, snippetOriginalBody, mediaSnippet]);
const onDelete = useCallback(() => {
const snippets = Object.assign({}, settings?.snippets || {});
delete snippets[title];
delete snippets[snippetKey];
Messenger.send(DashboardMessage.updateSnippet, { snippets });
setShowAlert(false);
}, [settings?.snippets, title]);
}, [settings?.snippets, snippetKey]);
return (
<>
@@ -115,7 +125,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
<h2 className="mt-2 mb-2 font-bold flex items-center" title={snippet.isMediaSnippet ? "Media snippet" : "Content snippet"}>
{ snippet.isMediaSnippet ? <PhotographIcon className='w-5 h-5 mr-1' aria-hidden={true} /> : <DocumentTextIcon className='w-5 h-5 mr-1' aria-hidden={true} /> }
{title}
{snippet.title || snippetKey}
</h2>
<FeatureFlag
@@ -160,17 +170,29 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
)
}
<QuickAction
title={`Edit snippet`}
onClick={onOpenEdit}>
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
{
!snippet.sourcePath ? (
<>
<QuickAction
title={`Edit snippet`}
onClick={onOpenEdit}>
<PencilIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<QuickAction
title={`Delete snippet`}
onClick={() => setShowAlert(true)}>
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
<QuickAction
title={`Delete snippet`}
onClick={() => setShowAlert(true)}>
<TrashIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
</>
) : (
<QuickAction
title={`View snippet file`}
onClick={showFile}>
<EyeIcon className={`w-4 h-4`} aria-hidden="true" />
</QuickAction>
)
}
</div>
</div>
</div>
@@ -182,8 +204,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
{
showInsertDialog && (
<FormDialog
title={`Insert snippet: ${title}`}
description={`Insert the ${title.toLowerCase()} snippet into the current article`}
title={`Insert snippet: ${(snippet.title || snippetKey)}`}
description={`Insert the ${(snippet.title || snippetKey).toLowerCase()} snippet into the current article`}
isSaveDisabled={!insertToContent}
trigger={insertToArticle}
dismiss={() => setShowInsertDialog(false)}
@@ -202,8 +224,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
{
showEditDialog && (
<FormDialog
title={`Edit snippet: ${title}`}
description={`Edit the ${title.toLowerCase()} snippet`}
title={`Edit snippet: ${(snippet.title || snippetKey)}`}
description={`Edit the ${(snippet.title || snippetKey).toLowerCase()} snippet`}
isSaveDisabled={!snippetTitle || !snippetOriginalBody}
trigger={onSnippetUpdate}
dismiss={reset}
@@ -227,8 +249,8 @@ export const Item: React.FunctionComponent<IItemProps> = ({ title, snippet }: Re
{
showAlert && (
<Alert
title={`Delete snippet: ${title}`}
description={`Are you sure you want to delete the ${title.toLowerCase()} snippet?`}
title={`Delete snippet: ${(snippet.title || snippetKey)}`}
description={`Are you sure you want to delete the ${(snippet.title || snippetKey).toLowerCase()} snippet?`}
okBtnText={`Delete`}
cancelBtnText={`Cancel`}
dismiss={() => setShowAlert(false)}
@@ -120,7 +120,7 @@ export const Snippets: React.FunctionComponent<ISnippetsProps> = (props: React.P
snippetKeys.map((snippetKey: any, index: number) => (
<Item
key={index}
title={snippetKey}
snippetKey={snippetKey}
snippet={snippets[snippetKey]} />
))
}
+10 -2
View File
@@ -195,6 +195,9 @@ export class Settings {
await Settings.update(name, undefined);
}
// Make sure to reload the whole config + all the data files
await Settings.readConfig();
return;
}
} else {
@@ -516,7 +519,7 @@ export class Settings {
}
// Object settings
else if (Settings.isEqualOrStartsWith(relSettingName, SETTING_CONTENT_SNIPPETS)) {
Settings.updateGlobalConfigObjectByNameSetting(SETTING_CONTENT_SNIPPETS, configFilePath, configJson);
Settings.updateGlobalConfigObjectByNameSetting(SETTING_CONTENT_SNIPPETS, configFilePath, configJson, filePath);
}
} catch (e) {
Logger.error(`Error reading config file: ${configFile.fsPath}`);
@@ -561,12 +564,17 @@ export class Settings {
* @param fileNamepath
* @param configJson
*/
private static updateGlobalConfigObjectByNameSetting<T>(settingName: string, fileNamepath: string, configJson: any): void {
private static updateGlobalConfigObjectByNameSetting<T>(settingName: string, fileNamepath: string, configJson: any, absPath: string): void {
const crntValue = Settings.globalConfig[`${CONFIG_KEY}.${settingName}`] || {};
// Filename is the key
const fileName = parse(fileNamepath).name;
configJson = {
...configJson,
sourcePath: absPath
};
if (!crntValue[fileName]) {
crntValue[fileName] = configJson;
+10 -1
View File
@@ -4,6 +4,7 @@ import { Dashboard } from "../../commands/Dashboard";
import { SETTING_CONTENT_SNIPPETS, TelemetryEvent } from "../../constants";
import { DashboardMessage } from "../../dashboardWebView/DashboardMessage";
import { Notifications, Settings, Telemetry } from "../../helpers";
import { Snippets } from "../../models";
import { BaseListener } from "./BaseListener";
import { SettingsListener } from "./SettingsListener";
@@ -68,7 +69,15 @@ export class SnippetListener extends BaseListener {
return;
}
await Settings.update(SETTING_CONTENT_SNIPPETS, snippets, true);
// Filter out external data snippets
const snippetsToStore = Object.keys(snippets).reduce((acc, key) => {
if (!snippets[key].sourcePath) {
acc[key] = snippets[key];
}
return acc;
}, {} as Snippets);
await Settings.update(SETTING_CONTENT_SNIPPETS, snippetsToStore, true);
SettingsListener.getSettings(true);
}
+2
View File
@@ -5,12 +5,14 @@ export interface Snippets {
}
export interface Snippet {
title?: string;
description: string;
body: string[] | string;
fields: SnippetField[];
openingTags?: string;
closingTags?: string;
isMediaSnippet?: boolean;
sourcePath?: string;
}
export type SnippetSpecialPlaceholders = "FM_SELECTED_TEXT" | string;