diff --git a/src/commands/Folders.ts b/src/commands/Folders.ts index c812a119..a038e018 100644 --- a/src/commands/Folders.ts +++ b/src/commands/Folders.ts @@ -135,14 +135,18 @@ export class Folders { let fileStats: FileInfo[] = []; for (const file of files) { - const fileName = file[0]; - const filePath = Uri.file(join(folderPath.fsPath, fileName)); - const stats = await workspace.fs.stat(filePath); - fileStats.push({ - filePath: filePath.fsPath, - fileName, - ...stats - }); + try { + const fileName = file[0]; + const filePath = Uri.file(join(folderPath.fsPath, fileName)); + const stats = await workspace.fs.stat(filePath); + fileStats.push({ + filePath: filePath.fsPath, + fileName, + ...stats + }); + } catch (error) { + // Skip the file + } } fileStats = fileStats.sort((a, b) => b.mtime - a.mtime).slice(0, 10); diff --git a/src/extension.ts b/src/extension.ts index 005388b0..13d70417 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -108,6 +108,11 @@ export async function activate({ subscriptions, extensionUri, extensionPath }: v Template.init(); Preview.init(); Folders.updateVsCodeCtx(); + + const exView = ExplorerView.getInstance(); + exView.getSettings(); + exView.getFoldersAndFiles(); + MarkdownFoldingProvider.triggerHighlighting(); }); // Create the status bar @@ -131,13 +136,10 @@ export async function activate({ subscriptions, extensionUri, extensionPath }: v subscriptions.push(vscode.workspace.onDidSaveTextDocument((doc: vscode.TextDocument) => { if (doc.languageId === 'markdown') { // Optimize the list of recently changed files - ExplorerView.getInstance().getSettings(); + ExplorerView.getInstance().getFoldersAndFiles(); } })); - // Listener for setting changes - subscriptions.push(vscode.workspace.onDidChangeConfiguration(MarkdownFoldingProvider.triggerHighlighting)); - // Webview for preview Preview.init(); subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.preview, () => Preview.open(extensionPath) )); diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index 9f4d17e5..b40da5e4 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -9,7 +9,6 @@ export interface PanelSettings { scripts: CustomScript[]; isInitialized: boolean; modifiedDateUpdate: boolean; - contentInfo: FolderInfo[] | null; writingSettingsEnabled: boolean; fmHighlighting: boolean; preview: PreviewSettings; diff --git a/src/viewpanel/Command.ts b/src/viewpanel/Command.ts index 1ab79dbc..7e4145c1 100644 --- a/src/viewpanel/Command.ts +++ b/src/viewpanel/Command.ts @@ -5,4 +5,5 @@ export enum Command { focusOnTags = "focusOnTags", focusOnCategories = "focusOnCategories", closeSections = "closeSections", + folderInfo = "folderInfo", } \ No newline at end of file diff --git a/src/viewpanel/CommandToCode.ts b/src/viewpanel/CommandToCode.ts index 294195f2..71cc12cc 100644 --- a/src/viewpanel/CommandToCode.ts +++ b/src/viewpanel/CommandToCode.ts @@ -23,4 +23,5 @@ export enum CommandToCode { openPreview = "open-preview", updatePreviewUrl = "update-preview-url", openInEditor = "open-in-editor", + updateMetadata = "update-metadata", } \ No newline at end of file diff --git a/src/viewpanel/ViewPanel.tsx b/src/viewpanel/ViewPanel.tsx index 4973ac24..a3a42002 100644 --- a/src/viewpanel/ViewPanel.tsx +++ b/src/viewpanel/ViewPanel.tsx @@ -1,24 +1,21 @@ import * as React from 'react'; import { Actions } from './components/Actions'; import { BaseView } from './components/BaseView'; -import { Collapsible } from './components/Collapsible'; import { GlobalSettings } from './components/GlobalSettings'; -import { ListUnorderedIcon } from './components/Icons/ListUnorderedIcon'; -import { SymbolKeywordIcon } from './components/Icons/SymbolKeywordIcon'; -import { TagIcon } from './components/Icons/TagIcon'; import { OtherActions } from './components/OtherActions'; import { SeoStatus } from './components/SeoStatus'; import { Spinner } from './components/Spinner'; import { TagPicker } from './components/TagPicker'; -import { FileList } from './components/FileList'; import useMessages from './hooks/useMessages'; import { TagType } from './TagType'; +import { FolderAndFiles } from './components/FolderAndFiles'; +import { Metadata } from './components/Metadata'; export interface IViewPanelProps { } export const ViewPanel: React.FunctionComponent = (props: React.PropsWithChildren) => { - const { loading, metadata, settings, focusElm, unsetFocus } = useMessages(); + const { loading, metadata, settings, folderAndFiles, focusElm, unsetFocus } = useMessages(); if (loading) { return ( @@ -28,7 +25,7 @@ export const ViewPanel: React.FunctionComponent = (props: React if (!metadata || Object.keys(metadata).length === 0) { return ( - + ); } @@ -44,60 +41,13 @@ export const ViewPanel: React.FunctionComponent = (props: React settings && metadata && } - - { - } - crntSelected={metadata.keywords || []} - options={[]} - freeform={true} - focussed={focusElm === TagType.keywords} - unsetFocus={unsetFocus} - disableConfigurable /> - } - { - (settings && settings.tags && settings.tags.length > 0) && ( - } - crntSelected={metadata.tags || []} - options={settings.tags} - freeform={settings.freeform} - focussed={focusElm === TagType.tags} - unsetFocus={unsetFocus} /> - ) - } - { - (settings && settings.categories && settings.categories.length > 0) && ( - } - crntSelected={metadata.categories || []} - options={settings.categories} - freeform={settings.freeform} - focussed={focusElm === TagType.categories} - unsetFocus={unsetFocus} /> - ) - } - + - { - (settings?.contentInfo && settings?.contentInfo.length > 0) && ( - -
- { - settings.contentInfo.map(folder => ( - <> - {folder.lastModified && ( -
- -
- )} - - )) - } -
-
- ) - } + diff --git a/src/viewpanel/components/BaseView.tsx b/src/viewpanel/components/BaseView.tsx index 22a7ff57..4d4dcace 100644 --- a/src/viewpanel/components/BaseView.tsx +++ b/src/viewpanel/components/BaseView.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { PanelSettings } from '../../models'; +import { FolderInfo, PanelSettings } from '../../models'; import { CommandToCode } from '../CommandToCode'; import { MessageHelper } from '../helper/MessageHelper'; import { Collapsible } from './Collapsible'; @@ -7,12 +7,14 @@ import { GlobalSettings } from './GlobalSettings'; import { OtherActions } from './OtherActions'; import { FileList } from './FileList'; import { VsLabel } from './VscodeComponents'; +import { FolderAndFiles } from './FolderAndFiles'; export interface IBaseViewProps { settings: PanelSettings | undefined; + folderAndFiles: FolderInfo[] | undefined; } -export const BaseView: React.FunctionComponent = ({settings}: React.PropsWithChildren) => { +export const BaseView: React.FunctionComponent = ({settings, folderAndFiles}: React.PropsWithChildren) => { const initProject = () => { MessageHelper.sendMessage(CommandToCode.initProject); @@ -34,26 +36,8 @@ export const BaseView: React.FunctionComponent = ({settings}: Re - - { - settings?.contentInfo && ( - -
- { - settings.contentInfo.map(folder => ( -
- { - folder.lastModified ? : ( - {folder.title}: {folder.files} file{folder.files > 1 ? 's' : ''} - ) - } -
- )) - } -
-
- ) - } + + diff --git a/src/viewpanel/components/FolderAndFiles.tsx b/src/viewpanel/components/FolderAndFiles.tsx new file mode 100644 index 00000000..5ba1b602 --- /dev/null +++ b/src/viewpanel/components/FolderAndFiles.tsx @@ -0,0 +1,45 @@ +import * as React from 'react'; +import { FolderInfo } from '../../models'; +import { Collapsible } from './Collapsible'; +import { FileList } from './FileList'; +import { VsLabel } from './VscodeComponents'; + +export interface IFolderAndFilesProps { + data: FolderInfo[] | undefined; + isBase?: boolean; +} + +export const FolderAndFiles: React.FunctionComponent = ({data, isBase}: React.PropsWithChildren) => { + + if (!data) { + return null; + } + + return ( + <> + { + (data && data.length > 0) && ( + +
+ { + data.map((folder: FolderInfo) => ( + <> + {folder.lastModified ? ( +
+ +
+ ) : ( + isBase ? ( + {folder.title}: {folder.files} file{folder.files > 1 ? 's' : ''} + ) : null + )} + + )) + } +
+
+ ) + } + + ); +}; \ No newline at end of file diff --git a/src/viewpanel/components/Metadata.tsx b/src/viewpanel/components/Metadata.tsx new file mode 100644 index 00000000..4c3d9879 --- /dev/null +++ b/src/viewpanel/components/Metadata.tsx @@ -0,0 +1,64 @@ +import * as React from 'react'; +import { PanelSettings } from '../../models'; +import { CommandToCode } from '../CommandToCode'; +import { MessageHelper } from '../helper/MessageHelper'; +import { TagType } from '../TagType'; +import { Collapsible } from './Collapsible'; +import { ListUnorderedIcon } from './Icons/ListUnorderedIcon'; +import { SymbolKeywordIcon } from './Icons/SymbolKeywordIcon'; +import { TagIcon } from './Icons/TagIcon'; +import { TagPicker } from './TagPicker'; + +export interface IMetadataProps { + settings: PanelSettings | undefined; + metadata: { [prop: string]: string[] | null }; + focusElm: TagType | null; + unsetFocus: () => void; +} + +export const Metadata: React.FunctionComponent = ({settings, metadata, focusElm, unsetFocus}: React.PropsWithChildren) => { + + const sendUpdate = (field: string, value: string) => { + MessageHelper.sendMessage(CommandToCode.updateMetadata, { + field, + value + }); + }; + + return ( + + { + } + crntSelected={metadata.keywords || []} + options={[]} + freeform={true} + focussed={focusElm === TagType.keywords} + unsetFocus={unsetFocus} + disableConfigurable /> + } + { + (settings && settings.tags && settings.tags.length > 0) && ( + } + crntSelected={metadata.tags || []} + options={settings.tags} + freeform={settings.freeform} + focussed={focusElm === TagType.tags} + unsetFocus={unsetFocus} /> + ) + } + { + (settings && settings.categories && settings.categories.length > 0) && ( + } + crntSelected={metadata.categories || []} + options={settings.categories} + freeform={settings.freeform} + focussed={focusElm === TagType.categories} + unsetFocus={unsetFocus} /> + ) + } + + ); +}; \ No newline at end of file diff --git a/src/viewpanel/hooks/useMessages.tsx b/src/viewpanel/hooks/useMessages.tsx index 083c8a23..3a9f62c8 100644 --- a/src/viewpanel/hooks/useMessages.tsx +++ b/src/viewpanel/hooks/useMessages.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { PanelSettings } from '../../models/PanelSettings'; +import { FolderInfo, PanelSettings } from '../../models/PanelSettings'; import { Command } from '../Command'; import { CommandToCode } from '../CommandToCode'; import { MessageHelper } from '../helper/MessageHelper'; @@ -12,6 +12,7 @@ export default function useMessages() { const [settings, setSettings] = useState(); const [loading, setLoading] = useState(false); const [focusElm, setFocus] = useState(null); + const [folderAndFiles, setFolderAndFiles] = useState(undefined); window.addEventListener('message', event => { const message = event.data; @@ -25,6 +26,9 @@ export default function useMessages() { setSettings(message.data); setLoading(false); break; + case Command.folderInfo: + setFolderAndFiles(message.data); + break; case Command.loading: setLoading(message.data); break; @@ -45,6 +49,7 @@ export default function useMessages() { return { metadata, settings, + folderAndFiles, focusElm, loading, unsetFocus: () => { setFocus(null) } diff --git a/src/webview/ExplorerView.ts b/src/webview/ExplorerView.ts index 9c22f4eb..f29b0919 100644 --- a/src/webview/ExplorerView.ts +++ b/src/webview/ExplorerView.ts @@ -83,6 +83,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable { switch(msg.command) { case CommandToCode.getData: this.getSettings(); + this.getFoldersAndFiles(); this.getFileData(); break; case CommandToCode.updateSlug: @@ -308,7 +309,6 @@ export class ExplorerView implements WebviewViewProvider, Disposable { freeform: config.get(SETTING_PANEL_FREEFORM), scripts: config.get(SETTING_CUSTOM_SCRIPTS), isInitialized: await Template.isInitialized(), - contentInfo: await Folders.getInfo() || null, modifiedDateUpdate: config.get(SETTING_AUTO_UPDATE_DATE) || false, writingSettingsEnabled: this.isWritingSettingsEnabled() || false, fmHighlighting: config.get(SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT), @@ -317,6 +317,16 @@ export class ExplorerView implements WebviewViewProvider, Disposable { }); } + /** + * Retrieve the information about the registered folders and its files + */ + public async getFoldersAndFiles() { + this.postWebviewMessage({ + command: Command.folderInfo, + data: await Folders.getInfo() || null + }); + } + /** * Retrieve the file its front matter */