diff --git a/CHANGELOG.md b/CHANGELOG.md index 8566dcf8..55d2727f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### ✨ New features - [#113](https://github.com/estruyf/vscode-front-matter/issues/113): Integrating a local DB for media metadata (caption, alt) +- [#132](https://github.com/estruyf/vscode-front-matter/issues/132): Major changes to the media dashboard which allows you to navigate through all folders ### 🎨 Enhancements @@ -17,6 +18,7 @@ - [#126](https://github.com/estruyf/vscode-front-matter/issues/126): Create new content from the available content types - [#127](https://github.com/estruyf/vscode-front-matter/issues/127): Title bar action added to open the dashboard - [#128](https://github.com/estruyf/vscode-front-matter/issues/128): Support for multi-select on image fields added +- [#131](https://github.com/estruyf/vscode-front-matter/issues/131): Folder creation support added on media dashboard ### 🐞 Fixes diff --git a/src/commands/Dashboard.ts b/src/commands/Dashboard.ts index ee92dc83..c26d6cde 100644 --- a/src/commands/Dashboard.ts +++ b/src/commands/Dashboard.ts @@ -1,7 +1,7 @@ -import { SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES } from './../constants/settings'; +import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES } from './../constants/settings'; import { ArticleHelper } from './../helpers/ArticleHelper'; import { basename, dirname, extname, join } from "path"; -import { existsSync, statSync, unlinkSync, writeFileSync } from "fs"; +import { existsSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs"; import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env, Position } from "vscode"; import { Settings as SettingsHelper } from '../helpers'; import { TaxonomyType } from '../models'; @@ -164,7 +164,7 @@ export class Dashboard { Extension.getInstance().setState(EXTENSION_STATE_PAGES_VIEW, msg.data); break; case DashboardMessage.getMedia: - Dashboard.getMedia(msg?.data?.page, msg?.data?.folder) + Dashboard.getMedia(msg?.data?.page, msg?.data?.folder); break; case DashboardMessage.copyToClipboard: env.clipboard.writeText(msg.data); @@ -185,13 +185,24 @@ export class Dashboard { case DashboardMessage.updateMediaMetadata: Dashboard.updateMediaMetadata(msg?.data); break; + case DashboardMessage.createMediaFolder: + await commands.executeCommand(COMMAND_NAME.createFolder, msg?.data); + break; } }); } + /** + * Reset media array + */ public static resetMedia() { Dashboard.media = []; } + + public static switchFolder(folderPath: string) { + Dashboard.resetMedia(); + Dashboard.getMedia(0, folderPath); + } /** * Insert an image into the front matter or contents @@ -236,7 +247,7 @@ export class Dashboard { data: { beta: ext.isBetaVersion(), wsFolder: wsFolder ? wsFolder.fsPath : '', - staticFolder: SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDERS), + staticFolder: SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER), folders: Folders.get(), initialized: await Template.isInitialized(), tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag), @@ -246,6 +257,7 @@ export class Dashboard { pageViewType: await ext.getState(EXTENSION_STATE_PAGES_VIEW), mediaSnippet: SettingsHelper.get(SETTINGS_DASHBOARD_MEDIA_SNIPPET) || [], contentTypes: SettingsHelper.get(SETTING_TAXONOMY_CONTENT_TYPES) || [], + contentFolders: Folders.get().map(f => f.path), } as Settings }); } @@ -261,49 +273,58 @@ export class Dashboard { /** * Retrieve all media files */ - private static async getMedia(page: number = 0, folder: string = '') { + private static async getMedia(page: number = 0, selectedFolder: string = '') { const wsFolder = Folders.getWorkspaceFolder(); - const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDERS); + const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER); + const contentFolders = Folders.get(); - if (Dashboard.media.length === 0) { - const contentFolder = Folders.get(); - let allMedia: MediaInfo[] = []; + const relSelectedFolderPath = selectedFolder ? selectedFolder.substring((wsFolder?.fsPath || "").length + 1) : ''; + let allMedia: MediaInfo[] = []; + + if (relSelectedFolderPath) { + const files = await workspace.findFiles(join(relSelectedFolderPath, '/*')); + const media = Dashboard.filterMedia(files); + allMedia = [...media]; + } else { if (staticFolder) { - const files = await workspace.findFiles(`${staticFolder || ""}/**/*`); + const folderSearch = join(staticFolder || "", '/*'); + const files = await workspace.findFiles(folderSearch); const media = Dashboard.filterMedia(files); allMedia = [...media]; } - if (contentFolder && wsFolder) { - for (let i = 0; i < contentFolder.length; i++) { - const folder = contentFolder[i]; - const relFolderPath = folder.path.substring(wsFolder.fsPath.length + 1); - const files = await workspace.findFiles(`${relFolderPath}/**/*`); + if (contentFolders && wsFolder) { + for (let i = 0; i < contentFolders.length; i++) { + const contentFolder = contentFolders[i]; + const relFolderPath = contentFolder.path.substring(wsFolder.fsPath.length + 1); + const folderSearch = relSelectedFolderPath ? join(relSelectedFolderPath, '/*') : join(relFolderPath, '/*'); + const files = await workspace.findFiles(folderSearch); const media = Dashboard.filterMedia(files); allMedia = [...allMedia, ...media]; } } - - allMedia = allMedia.sort((a, b) => { - if (b.fsPath < a.fsPath) { - return -1; - } - if (b.fsPath > a.fsPath) { - return 1; - } - return 0; - }); - - Dashboard.media = Object.assign([], allMedia); } + allMedia = allMedia.sort((a, b) => { + if (b.fsPath < a.fsPath) { + return -1; + } + if (b.fsPath > a.fsPath) { + return 1; + } + return 0; + }); + + Dashboard.media = Object.assign([], allMedia); + // Filter the media let files: MediaInfo[] = Dashboard.media; - if (folder) { - files = files.filter(f => f.fsPath.includes(folder)); + if (relSelectedFolderPath) { + const folderPath = `${relSelectedFolderPath.startsWith("/") ? "" : "/"}${relSelectedFolderPath}${relSelectedFolderPath.endsWith("/") ? "" : "/"}` + files = files.filter(f => f.fsPath.includes(folderPath)); } // Retrieve the total after filtering and before the slicing happens @@ -327,23 +348,36 @@ export class Dashboard { }); files = files.filter(f => f.stats !== undefined); - const folders = [...new Set(Dashboard.media.map((file) => { - let relFolderPath = wsFolder ? file.fsPath.substring(wsFolder.fsPath.length + 1) : file.fsPath; - if (staticFolder && relFolderPath.startsWith(staticFolder)) { - relFolderPath = relFolderPath.substring(staticFolder.length); + // Retrieve all the folders + let allContentFolders: string[] = []; + let allFolders: string[] = []; + + if (selectedFolder) { + if (existsSync(selectedFolder)) { + allFolders = readdirSync(selectedFolder, { withFileTypes: true }).filter(dir => dir.isDirectory()).map(dir => join(selectedFolder, dir.name)); } - if (relFolderPath?.startsWith('/')) { - relFolderPath = relFolderPath.substring(1); + } else { + for (const contentFolder of contentFolders) { + const contentPath = contentFolder.path; + if (contentPath && existsSync(contentPath)) { + const subFolders = readdirSync(contentPath, { withFileTypes: true }).filter(dir => dir.isDirectory()).map(dir => join(contentPath, dir.name)); + allContentFolders = [...allContentFolders, ...subFolders]; + } } - return dirname(relFolderPath); - }))]; + + const staticPath = join(wsFolder?.fsPath || "", staticFolder || ""); + if (staticPath && existsSync(staticPath)) { + allFolders = readdirSync(staticPath, { withFileTypes: true }).filter(dir => dir.isDirectory()).map(dir => join(staticPath, dir.name)); + } + } Dashboard.postWebviewMessage({ command: DashboardCommand.media, data: { media: files, total: total, - folders + folders: [...allContentFolders, ...allFolders], + selectedFolder } as MediaPaths }); } @@ -356,7 +390,7 @@ export class Dashboard { const descriptionField = SettingsHelper.get(SETTING_SEO_DESCRIPTION_FIELD) as string || DefaultFields.Description; const dateField = SettingsHelper.get(SETTING_DATE_FIELD) as string || DefaultFields.PublishingDate; - const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDERS); + const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER); const folderInfo = await Folders.getInfo(); const pages: Page[] = []; @@ -457,7 +491,7 @@ export class Dashboard { private static async saveFile({fileName, contents, folder}: { fileName: string; contents: string; folder: string | null }) { if (fileName && contents) { const wsFolder = Folders.getWorkspaceFolder(); - const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDERS); + const staticFolder = SettingsHelper.get(SETTINGS_CONTENT_STATIC_FOLDER); const wsPath = wsFolder ? wsFolder.fsPath : ""; let absFolderPath = join(wsPath, staticFolder || "", folder || ""); diff --git a/src/commands/Folders.ts b/src/commands/Folders.ts index 609075bc..37f8b3e8 100644 --- a/src/commands/Folders.ts +++ b/src/commands/Folders.ts @@ -1,5 +1,5 @@ import { Questions } from './../helpers/Questions'; -import { SETTINGS_CONTENT_PAGE_FOLDERS } from './../constants/settings'; +import { SETTINGS_CONTENT_PAGE_FOLDERS, SETTINGS_CONTENT_STATIC_FOLDER } from './../constants/settings'; import { commands, Uri, workspace, window } from "vscode"; import { basename, join } from "path"; import { ContentFolder, FileInfo, FolderInfo } from "../models"; @@ -7,12 +7,64 @@ import uniqBy = require("lodash.uniqby"); import { Template } from "./Template"; import { Notifications } from "../helpers/Notifications"; import { Settings } from "../helpers"; -import { existsSync } from 'fs'; +import { existsSync, mkdirSync } from 'fs'; +import { format } from 'date-fns'; +import { Dashboard } from './Dashboard'; export const WORKSPACE_PLACEHOLDER = `[[workspace]]`; export class Folders { + /** + * Add a media folder + * @returns + */ + public static async addMediaFolder(data?: {selectedFolder?: string}) { + let wsFolder = Folders.getWorkspaceFolder(); + const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDER); + + let startPath = ""; + + if (data?.selectedFolder) { + startPath = data.selectedFolder.replace((wsFolder?.fsPath || ""), ""); + } else if (staticFolder) { + startPath = `/${staticFolder}`; + } + + if (startPath && !startPath.endsWith("/")) { + startPath += "/"; + } + + const folderName = await window.showInputBox({ + prompt: `Which name would you like to give to your folder (use "/" to create multi-level folders)?`, + value: startPath, + ignoreFocusOut: true, + placeHolder: `${format(new Date(), `yyyy/MM`)}` + }); + + if (!folderName) { + Notifications.warning(`No folder name was specified.`); + return; + } + + const folders = folderName.split("/").filter(f => f); + let parentFolders: string[] = []; + + for (const folder of folders) { + const folderPath = join(wsFolder?.fsPath || "", parentFolders.join("/"), folder); + + parentFolders.push(folder); + + if (!existsSync(folderPath)) { + mkdirSync(folderPath); + } + } + + if (Dashboard.isOpen) { + Dashboard.switchFolder(folderName); + } + } + /** * Create content in a registered folder * @returns diff --git a/src/constants/Extension.ts b/src/constants/Extension.ts index 316fc480..fec6ec46 100644 --- a/src/constants/Extension.ts +++ b/src/constants/Extension.ts @@ -33,4 +33,5 @@ export const COMMAND_NAME = { dashboard: getCommandName("dashboard"), promote: getCommandName("promoteSettings"), insertImage: getCommandName("insertImage"), + createFolder: getCommandName("createFolder"), }; \ No newline at end of file diff --git a/src/constants/settings.ts b/src/constants/settings.ts index 0d21d36c..fa0f92b2 100644 --- a/src/constants/settings.ts +++ b/src/constants/settings.ts @@ -36,7 +36,7 @@ export const SETTING_CUSTOM_SCRIPTS = "custom.scripts"; export const SETTING_AUTO_UPDATE_DATE = "content.autoUpdateDate"; export const SETTINGS_CONTENT_PAGE_FOLDERS = "content.pageFolders"; -export const SETTINGS_CONTENT_STATIC_FOLDERS = "content.publicFolder"; +export const SETTINGS_CONTENT_STATIC_FOLDER = "content.publicFolder"; export const SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT = "content.fmHighlight"; export const SETTINGS_DASHBOARD_OPENONSTART = "dashboard.openOnStart"; diff --git a/src/dashboardWebView/DashboardMessage.ts b/src/dashboardWebView/DashboardMessage.ts index 034c1bb2..f3944647 100644 --- a/src/dashboardWebView/DashboardMessage.ts +++ b/src/dashboardWebView/DashboardMessage.ts @@ -17,4 +17,5 @@ export enum DashboardMessage { deleteMedia = 'deleteMedia', insertPreviewImage = 'insertPreviewImage', updateMediaMetadata = 'updateMediaMetadata', + createMediaFolder = 'createMediaFolder', } \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/Breadcrumb.tsx b/src/dashboardWebView/components/Header/Breadcrumb.tsx new file mode 100644 index 00000000..414a0fe8 --- /dev/null +++ b/src/dashboardWebView/components/Header/Breadcrumb.tsx @@ -0,0 +1,96 @@ +import { HomeIcon } from '@heroicons/react/solid'; +import { basename, join } from 'path'; +import * as React from 'react'; +import { useRecoilState, useRecoilValue } from 'recoil'; +import { SelectedMediaFolderAtom, SettingsAtom } from '../../state'; + +export interface IBreadcrumbProps {} + +export const Breadcrumb: React.FunctionComponent = (props: React.PropsWithChildren) => { + const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom); + const settings = useRecoilValue(SettingsAtom); + const [ folders, setFolders ] = React.useState([]); + + if (!settings?.wsFolder) { + return null; + } + + React.useEffect(() => { + const { wsFolder, staticFolder, contentFolders } = settings; + + const isValid = (folderPath: string) => { + if (staticFolder) { + const staticPath = join(wsFolder, staticFolder); + const relPath = folderPath.replace(staticPath, ''); + if (relPath.length > 1 && folderPath.startsWith(staticPath)) { + return true; + } else if (relPath.length === 0) { + return false; + } + } + + for (let i = 0; i < contentFolders.length; i++) { + const contentFolder = contentFolders[i]; + const relContentPath = folderPath.replace(contentFolder, ''); + return relContentPath.length > 1 && folderPath.startsWith(contentFolder); + } + + return false; + }; + + if (!selectedFolder) { + setFolders([]); + } else { + const relPath = selectedFolder.replace(settings.wsFolder, ''); + const folderParts = relPath.split('/').filter(f => f); + const allFolders: string[] = []; + let previousFolder = settings.wsFolder; + + for (const part of folderParts) { + const folder = join(previousFolder, part); + if (isValid(folder)) { + allFolders.push(folder); + } + previousFolder = folder; + } + + setFolders(allFolders); + } + }, [selectedFolder]); + + return ( + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Header/Folders.tsx b/src/dashboardWebView/components/Header/Folders.tsx index 27440ad2..654c5db3 100644 --- a/src/dashboardWebView/components/Header/Folders.tsx +++ b/src/dashboardWebView/components/Header/Folders.tsx @@ -1,4 +1,4 @@ -import { Menu, Transition } from '@headlessui/react'; +import { Menu } from '@headlessui/react'; import * as React from 'react'; import { useRecoilState } from 'recoil'; import { FolderAtom } from '../../state'; diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index e78a57a6..34e13635 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -17,6 +17,7 @@ import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIco import { PhotographIcon } from '@heroicons/react/outline'; import { Pagination } from '../Media/Pagination'; import { ChoiceButton } from '../ChoiceButton'; +import { Breadcrumb } from './Breadcrumb'; export interface IHeaderProps { settings: Settings | null; @@ -113,7 +114,10 @@ export const Header: React.FunctionComponent = ({totalPages, folde { view === "media" && ( - + <> + + + ) } diff --git a/src/dashboardWebView/components/Media/FolderCreation.tsx b/src/dashboardWebView/components/Media/FolderCreation.tsx new file mode 100644 index 00000000..052ecc99 --- /dev/null +++ b/src/dashboardWebView/components/Media/FolderCreation.tsx @@ -0,0 +1,28 @@ +import * as React from 'react'; +import { FolderAddIcon } from '@heroicons/react/outline'; +import { useRecoilValue } from 'recoil'; +import { DashboardMessage } from '../../DashboardMessage'; +import { SelectedMediaFolderAtom } from '../../state'; +import { Messenger } from '@estruyf/vscode/dist/client'; + +export interface IFolderCreationProps {} + +export const FolderCreation: React.FunctionComponent = (props: React.PropsWithChildren) => { + const selectedFolder = useRecoilValue(SelectedMediaFolderAtom); + + const onFolderCreation = () => { + Messenger.send(DashboardMessage.createMediaFolder, { + selectedFolder + }); + }; + + return ( + + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Media/FolderItem.tsx b/src/dashboardWebView/components/Media/FolderItem.tsx new file mode 100644 index 00000000..17d9402a --- /dev/null +++ b/src/dashboardWebView/components/Media/FolderItem.tsx @@ -0,0 +1,29 @@ +import { FolderIcon } from '@heroicons/react/solid'; +import { basename } from 'path'; +import * as React from 'react'; +import { useRecoilState } from 'recoil'; +import { SelectedMediaFolderAtom } from '../../state'; + +export interface IFolderItemProps { + folder: string; + wsFolder?: string; + staticFolder?: string; +} + +export const FolderItem: React.FunctionComponent = ({ folder, wsFolder, staticFolder }: React.PropsWithChildren) => { + const [ , setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom); + + const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder; + + return ( +
  • + +
  • + ); +}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Media/FolderSelection.tsx b/src/dashboardWebView/components/Media/FolderSelection.tsx deleted file mode 100644 index 2281a011..00000000 --- a/src/dashboardWebView/components/Media/FolderSelection.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Menu } from '@headlessui/react'; -import { XIcon } from '@heroicons/react/outline'; -import Downshift from 'downshift'; -import * as React from 'react'; -import { useRecoilState, useRecoilValue } from 'recoil'; -import { MediaFoldersSelector, SelectedMediaFolderAtom } from '../../state'; - -export interface IFolderSelectionProps {} - -export const FolderSelection: React.FunctionComponent = (props: React.PropsWithChildren) => { - const folders = useRecoilValue(MediaFoldersSelector); - const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom); - const [ focus, setFocus ] = React.useState(false); - - let allFolders: string[] = Object.assign([], folders); - allFolders = allFolders.sort((a: string, b: string) => { - if (a.toLowerCase() < b.toLowerCase()) return -1; - if (a.toLowerCase() > b.toLowerCase()) return 1; - return 0; - }); - - return ( -
    - setFocus(false)} - onSelect={(selFolder) => { - setSelectedFolder(selFolder); - setFocus(false); - }}> - { - ({ - getInputProps, - getItemProps, - getMenuProps, - isOpen, - inputValue, - getRootProps - }) => ( -
    - - -
    - setFocus(true)} className={`ml-2 py-1 px-2 sm:text-sm bg-white dark:bg-vulcan-300 border border-gray-300 dark:border-vulcan-100 text-vulcan-500 dark:text-whisper-500 placeholder-gray-400 dark:placeholder-whisper-800 focus:outline-none`} {...getInputProps()} /> - - { - selectedFolder && ( - - ) - } -
    - -
    - {isOpen - ? allFolders - .filter((item: string) => !inputValue || item.includes(inputValue)) - .map((item, index) => ( -
    - {item} -
    - )) - : null} -
    -
    - ) - } -
    -
    - ); -}; \ No newline at end of file diff --git a/src/dashboardWebView/components/Media/Media.tsx b/src/dashboardWebView/components/Media/Media.tsx index 4069b10d..bf07b63f 100644 --- a/src/dashboardWebView/components/Media/Media.tsx +++ b/src/dashboardWebView/components/Media/Media.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { MediaInfo, MediaPaths } from '../../../models/MediaPaths'; import { DashboardCommand } from '../../DashboardCommand'; -import { LoadingAtom, MediaFoldersAtom, MediaTotalAtom, SelectedMediaFolderSelector, SettingsSelector, ViewDataSelector } from '../../state'; +import { LoadingAtom, MediaFoldersAtom, MediaTotalAtom, SelectedMediaFolderAtom, SettingsSelector, ViewDataSelector } from '../../state'; import { Header } from '../Header'; import { Spinner } from '../Spinner'; import { SponsorMsg } from '../SponsorMsg'; @@ -15,6 +15,10 @@ import { List } from './List'; import { useDropzone } from 'react-dropzone' import { useCallback } from 'react'; import { DashboardMessage } from '../../DashboardMessage'; +import { FrontMatterIcon } from '../../../panelWebView/components/Icons/FrontMatterIcon'; +import { FolderIcon } from '@heroicons/react/solid'; +import { basename } from 'path'; +import { FolderItem } from './FolderItem'; export interface IMediaProps {} @@ -22,10 +26,10 @@ export const LIMIT = 16; export const Media: React.FunctionComponent = (props: React.PropsWithChildren) => { const settings = useRecoilValue(SettingsSelector); - const selectedFolder = useRecoilValue(SelectedMediaFolderSelector); + const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom); const [ media, setMedia ] = React.useState([]); const [ , setTotal ] = useRecoilState(MediaTotalAtom); - const [ , setFolders ] = useRecoilState(MediaFoldersAtom); + const [ folders, setFolders ] = useRecoilState(MediaFoldersAtom); const [ loading, setLoading ] = useRecoilState(LoadingAtom); const viewData = useRecoilValue(ViewDataSelector); @@ -57,6 +61,7 @@ export const Media: React.FunctionComponent = (props: React.PropsWi setMedia(message.data.data.media); setTotal(message.data.data.total); setFolders(message.data.data.folders); + setSelectedFolder(message.data.data.selectedFolder); } }; @@ -95,6 +100,32 @@ export const Media: React.FunctionComponent = (props: React.PropsWi ) } + { + (media.length === 0 && folders.length === 0 && !loading) && ( +
    +
    + + +

    No media files to show. You can drag & drop new files.

    +
    +
    + ) + } + + { + folders && folders.length > 0 && ( +
    + + { + folders && folders.map((folder) => ( + + )) + } + +
    + ) + } + { media.map((file) => ( diff --git a/src/dashboardWebView/components/Media/Pagination.tsx b/src/dashboardWebView/components/Media/Pagination.tsx index df121999..dffc92ca 100644 --- a/src/dashboardWebView/components/Media/Pagination.tsx +++ b/src/dashboardWebView/components/Media/Pagination.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { useRecoilState, useRecoilValue } from 'recoil'; import { DashboardMessage } from '../../DashboardMessage'; import { LoadingAtom, MediaTotalSelector, PageAtom, SelectedMediaFolderSelector } from '../../state'; -import { FolderSelection } from './FolderSelection'; +import { FolderCreation } from './FolderCreation'; import { LIMIT } from './Media'; import { PaginationButton } from './PaginationButton'; @@ -82,7 +82,7 @@ export const Pagination: React.FunctionComponent = ({}: React.

    - +
    (SETTING_COMMA_SEPARATED_FIELDS); - const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDERS); const contentTypes = Settings.get(SETTING_TAXONOMY_CONTENT_TYPES); const articleDetails = this.getArticleDetails(); diff --git a/src/extension.ts b/src/extension.ts index c1c90fe7..e40ebf06 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -106,6 +106,8 @@ export async function activate(context: vscode.ExtensionContext) { const unregisterFolder = vscode.commands.registerCommand(COMMAND_NAME.unregisterFolder, Folders.unregister); + const createFolder = vscode.commands.registerCommand(COMMAND_NAME.createFolder, Folders.addMediaFolder); + const createByContentType = vscode.commands.registerCommand(COMMAND_NAME.createByContentType, ContentType.createContent); const createByTemplate = vscode.commands.registerCommand(COMMAND_NAME.createByTemplate, Folders.create); const createContent = vscode.commands.registerCommand(COMMAND_NAME.createContent, Content.create); @@ -191,7 +193,8 @@ export async function activate(context: vscode.ExtensionContext) { createByContentType, createByTemplate, projectInit, - collapseAll + collapseAll, + createFolder ); } diff --git a/src/helpers/ImageHelper.ts b/src/helpers/ImageHelper.ts index fa1e78b6..b56eeee7 100644 --- a/src/helpers/ImageHelper.ts +++ b/src/helpers/ImageHelper.ts @@ -4,7 +4,7 @@ import { Field } from '../models'; import { existsSync } from 'fs'; import { Folders } from '../commands/Folders'; import { Settings } from './SettingsHelper'; -import { SETTINGS_CONTENT_STATIC_FOLDERS } from '../constants'; +import { SETTINGS_CONTENT_STATIC_FOLDER } from '../constants'; export class ImageHelper { @@ -49,7 +49,7 @@ export class ImageHelper { */ public static relToAbs(filePath: string, value: string) { const wsFolder = Folders.getWorkspaceFolder(); - const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDERS); + const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDER); const staticPath = join(wsFolder?.fsPath || "", staticFolder || "", value); const contentFolderPath = filePath ? join(dirname(filePath), value) : null; @@ -68,7 +68,7 @@ export class ImageHelper { */ public static absToRel(imgValue: string) { const wsFolder = Folders.getWorkspaceFolder(); - const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDERS); + const staticFolder = Settings.get(SETTINGS_CONTENT_STATIC_FOLDER); let relPath = imgValue || ""; if (imgValue) { diff --git a/src/helpers/SettingsHelper.ts b/src/helpers/SettingsHelper.ts index 5920d2b1..52e9cb1a 100644 --- a/src/helpers/SettingsHelper.ts +++ b/src/helpers/SettingsHelper.ts @@ -2,7 +2,7 @@ import { Notifications } from './Notifications'; import { commands, Uri, workspace } from 'vscode'; import * as vscode from 'vscode'; import { TaxonomyType } from '../models'; -import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT } from '../constants'; +import { SETTING_TAXONOMY_TAGS, SETTING_TAXONOMY_CATEGORIES, CONFIG_KEY, CONTEXT, SETTINGS_CONTENT_STATIC_FOLDER } from '../constants'; import { Folders } from '../commands/Folders'; import { join, basename } from 'path'; import { existsSync, readFileSync, watch, writeFileSync } from 'fs'; @@ -223,7 +223,6 @@ export class Settings { return hasSetting; } - /** * Check if its the project config * @param filePath diff --git a/src/models/MediaPaths.ts b/src/models/MediaPaths.ts index 8d5e076c..967f1d76 100644 --- a/src/models/MediaPaths.ts +++ b/src/models/MediaPaths.ts @@ -5,6 +5,7 @@ export interface MediaPaths { media: MediaInfo[]; total: number; folders: string[]; + selectedFolder: string; } export interface MediaInfo { diff --git a/src/panelWebView/components/BaseView.tsx b/src/panelWebView/components/BaseView.tsx index 6371bfbf..40ea7009 100644 --- a/src/panelWebView/components/BaseView.tsx +++ b/src/panelWebView/components/BaseView.tsx @@ -5,8 +5,6 @@ import { MessageHelper } from '../../helpers/MessageHelper'; import { Collapsible } from './Collapsible'; import { GlobalSettings } from './GlobalSettings'; import { OtherActions } from './OtherActions'; -import { FileList } from './FileList'; -import { VsLabel } from './VscodeComponents'; import { FolderAndFiles } from './FolderAndFiles'; import { SponsorMsg } from './SponsorMsg';