#131 #132 - Changes to media dashboard

This commit is contained in:
Elio Struyf
2021-10-04 20:57:54 +02:00
parent c295761560
commit b525a6a211
21 changed files with 339 additions and 137 deletions
+2
View File
@@ -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
+74 -40
View File
@@ -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<string>(SETTINGS_CONTENT_STATIC_FOLDERS),
staticFolder: SettingsHelper.get<string>(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<ViewType | undefined>(EXTENSION_STATE_PAGES_VIEW),
mediaSnippet: SettingsHelper.get<string[]>(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<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const staticFolder = SettingsHelper.get<string>(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<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const staticFolder = SettingsHelper.get<string>(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<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const staticFolder = SettingsHelper.get<string>(SETTINGS_CONTENT_STATIC_FOLDER);
const wsPath = wsFolder ? wsFolder.fsPath : "";
let absFolderPath = join(wsPath, staticFolder || "", folder || "");
+54 -2
View File
@@ -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<string>(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
+1
View File
@@ -33,4 +33,5 @@ export const COMMAND_NAME = {
dashboard: getCommandName("dashboard"),
promote: getCommandName("promoteSettings"),
insertImage: getCommandName("insertImage"),
createFolder: getCommandName("createFolder"),
};
+1 -1
View File
@@ -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";
+1
View File
@@ -17,4 +17,5 @@ export enum DashboardMessage {
deleteMedia = 'deleteMedia',
insertPreviewImage = 'insertPreviewImage',
updateMediaMetadata = 'updateMediaMetadata',
createMediaFolder = 'createMediaFolder',
}
@@ -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<IBreadcrumbProps> = (props: React.PropsWithChildren<IBreadcrumbProps>) => {
const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const settings = useRecoilValue(SettingsAtom);
const [ folders, setFolders ] = React.useState<string[]>([]);
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 (
<nav className="bg-gray-200 text-vulcan-300 dark:bg-vulcan-400 dark:text-whisper-600 border-b border-gray-300 dark:border-vulcan-100 flex py-2" aria-label="Breadcrumb">
<ol role="list" className="w-full mx-auto flex space-x-4 px-5">
<li className="flex">
<div className="flex items-center">
<button onClick={() => setSelectedFolder(null)} className="text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500">
<HomeIcon className="flex-shrink-0 h-5 w-5" aria-hidden="true" />
<span className="sr-only">Home</span>
</button>
</div>
</li>
{folders.map((folder) => (
<li key={folder} className="flex">
<div className="flex items-center">
<svg
className="flex-shrink-0 h-5 w-5 text-gray-300 dark:text-whisper-900"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 20 20"
aria-hidden="true"
>
<path d="M5.555 17.776l8-16 .894.448-8 16-.894-.448z" />
</svg>
<button
onClick={() => setSelectedFolder(folder)}
className="ml-4 text-sm font-medium text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500"
>
{basename(folder)}
</button>
</div>
</li>
))}
</ol>
</nav>
);
};
@@ -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';
@@ -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<IHeaderProps> = ({totalPages, folde
{
view === "media" && (
<Pagination />
<>
<Pagination />
<Breadcrumb />
</>
)
}
</div>
@@ -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<IFolderCreationProps> = (props: React.PropsWithChildren<IFolderCreationProps>) => {
const selectedFolder = useRecoilValue(SelectedMediaFolderAtom);
const onFolderCreation = () => {
Messenger.send(DashboardMessage.createMediaFolder, {
selectedFolder
});
};
return (
<button
className={`inline-flex items-center px-3 py-1 border border-transparent text-xs leading-4 font-medium text-white dark:text-vulcan-500 bg-teal-600 hover:bg-teal-700 focus:outline-none disabled:bg-gray-500`}
title={`Create new folder`}
onClick={onFolderCreation}>
<FolderAddIcon className={`mr-2 h-6 w-6`} />
<span className={``}>Create new folder</span>
</button>
);
};
@@ -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<IFolderItemProps> = ({ folder, wsFolder, staticFolder }: React.PropsWithChildren<IFolderItemProps>) => {
const [ , setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const relFolderPath = wsFolder ? folder.replace(wsFolder, '') : folder;
return (
<li className={`group relative bg-gray-200 dark:bg-vulcan-300 hover:shadow-xl dark:hover:bg-vulcan-100 text-gray-600 hover:text-gray-700 dark:text-whisper-900 dark:hover:text-whisper-800 p-4`}>
<button className={`w-full flex flex-col items-center`} onClick={() => setSelectedFolder(folder)}>
<FolderIcon className={`h-auto w-1/2`} />
<p className="text-sm font-bold pointer-events-none flex items-center">
{basename(relFolderPath)}
</p>
</button>
</li>
);
};
@@ -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<IFolderSelectionProps> = (props: React.PropsWithChildren<IFolderSelectionProps>) => {
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 (
<div>
<Downshift
isOpen={focus}
selectedItem={selectedFolder}
onOuterClick={() => setFocus(false)}
onSelect={(selFolder) => {
setSelectedFolder(selFolder);
setFocus(false);
}}>
{
({
getInputProps,
getItemProps,
getMenuProps,
isOpen,
inputValue,
getRootProps
}) => (
<div className={`relative flex items-center`}>
<label className={`text-sm text-gray-500 dark:text-whisper-900`}>Filter by: </label>
<div
className={`inline-flex items-center`}
{...getRootProps({} as any, {suppressRefError: true})}
>
<input disabled={!!selectedFolder} onFocus={() => 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 && (
<button title={`Clear`} onClick={() => setSelectedFolder(null)}><XIcon className={`ml-2 h-6 w-6 text-red-500 hover:text-red-800`} /></button>
)
}
</div>
<div className={`${focus ? `block` : `hidden`} top-8 absolute right-0 z-10 mt-2 w-min rounded-md shadow-2xl bg-white dark:bg-vulcan-500 ring-1 ring-vulcan-400 dark:ring-white ring-opacity-5 focus:outline-none text-sm max-h-96 overflow-auto`} {...getMenuProps()}>
{isOpen
? allFolders
.filter((item: string) => !inputValue || item.includes(inputValue))
.map((item, index) => (
<div
className="cursor-pointer text-gray-500 dark:text-whisper-900 block px-4 py-2 text-sm font-medium w-full text-left hover:bg-gray-100 hover:text-gray-700 dark:hover:text-whisper-600 dark:hover:bg-vulcan-100"
{...getItemProps({ key: item, index, item })}
>
{item}
</div>
))
: null}
</div>
</div>
)
}
</Downshift>
</div>
);
};
@@ -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<IMediaProps> = (props: React.PropsWithChildren<IMediaProps>) => {
const settings = useRecoilValue(SettingsSelector);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const [ selectedFolder, setSelectedFolder ] = useRecoilState(SelectedMediaFolderAtom);
const [ media, setMedia ] = React.useState<MediaInfo[]>([]);
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<IMediaProps> = (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<IMediaProps> = (props: React.PropsWi
)
}
{
(media.length === 0 && folders.length === 0 && !loading) && (
<div className={`flex items-center justify-center h-full`}>
<div className={`max-w-xl text-center`}>
<FrontMatterIcon className={`text-vulcan-300 dark:text-whisper-800 h-32 mx-auto opacity-90 mb-8`} />
<p className={`text-xl font-medium`}>No media files to show. You can drag &amp; drop new files.</p>
</div>
</div>
)
}
{
folders && folders.length > 0 && (
<div className={`mb-8`}>
<List>
{
folders && folders.map((folder) => (
<FolderItem key={folder} folder={folder} staticFolder={settings?.staticFolder} wsFolder={settings?.wsFolder} />
))
}
</List>
</div>
)
}
<List>
{
media.map((file) => (
@@ -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<IPaginationProps> = ({}: React.
</p>
</div>
<FolderSelection />
<FolderCreation />
<div className="flex justify-between sm:justify-end space-x-2 text-sm">
<PaginationButton
+1
View File
@@ -16,4 +16,5 @@ export interface Settings {
pageViewType: ViewType | undefined;
mediaSnippet: string[];
contentTypes: ContentType[];
contentFolders: string[];
}
+1 -2
View File
@@ -1,6 +1,6 @@
import { DashboardData } from '../models/DashboardData';
import { Template } from '../commands/Template';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from '../constants';
import { DefaultFields, SETTINGS_CONTENT_FRONTMATTER_HIGHLIGHT, SETTING_AUTO_UPDATE_DATE, SETTING_CUSTOM_SCRIPTS, SETTING_SEO_CONTENT_MIN_LENGTH, SETTING_SEO_DESCRIPTION_FIELD, SETTING_SLUG_UPDATE_FILE_NAME, SETTING_PREVIEW_HOST, SETTING_DATE_FORMAT, SETTING_COMMA_SEPARATED_FIELDS, SETTINGS_CONTENT_STATIC_FOLDER, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_PANEL_FREEFORM, SETTING_SEO_DESCRIPTION_LENGTH, SETTING_SEO_TITLE_LENGTH, SETTING_SLUG_PREFIX, SETTING_SLUG_SUFFIX, SETTING_TAXONOMY_CATEGORIES, SETTING_TAXONOMY_TAGS } from '../constants';
import * as os from 'os';
import { PanelSettings, CustomScript } from '../models/PanelSettings';
import { CancellationToken, Disposable, Uri, Webview, WebviewView, WebviewViewProvider, WebviewViewResolveContext, window, workspace, commands, env as vscodeEnv } from "vscode";
@@ -216,7 +216,6 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
const wsFolder = Folders.getWorkspaceFolder();
const filePath = window.activeTextEditor?.document.uri.fsPath;
const commaSeparated = Settings.get<string[]>(SETTING_COMMA_SEPARATED_FIELDS);
const staticFolder = Settings.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const contentTypes = Settings.get<string>(SETTING_TAXONOMY_CONTENT_TYPES);
const articleDetails = this.getArticleDetails();
+4 -1
View File
@@ -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
);
}
+3 -3
View File
@@ -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<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const staticFolder = Settings.get<string>(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<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
const staticFolder = Settings.get<string>(SETTINGS_CONTENT_STATIC_FOLDER);
let relPath = imgValue || "";
if (imgValue) {
+1 -2
View File
@@ -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
+1
View File
@@ -5,6 +5,7 @@ export interface MediaPaths {
media: MediaInfo[];
total: number;
folders: string[];
selectedFolder: string;
}
export interface MediaInfo {
-2
View File
@@ -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';