Refactoring to message listeners

This commit is contained in:
Elio Struyf
2021-12-15 13:50:40 +01:00
parent 050a513b48
commit cb72b53653
30 changed files with 303 additions and 260 deletions
+9
View File
@@ -797,6 +797,15 @@
"light": "/assets/icons/frontmatter-small-light.svg"
}
},
{
"command": "frontMatter.dashboard.media",
"title": "Open media dashboard",
"category": "Front matter",
"icon": {
"dark": "/assets/icons/frontmatter-small-dark.svg",
"light": "/assets/icons/frontmatter-small-light.svg"
}
},
{
"command": "frontMatter.dashboard.close",
"title": "Close dashboard",
+25 -95
View File
@@ -1,37 +1,19 @@
import { SETTINGS_CONTENT_STATIC_FOLDER, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTING_TAXONOMY_CONTENT_TYPES, DefaultFields, HOME_PAGE_NAVIGATION_ID, ExtensionState, COMMAND_NAME, SETTINGS_FRAMEWORK_ID, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_SORTING, CONTEXT, SETTING_CUSTOM_SCRIPTS, SETTINGS_CONTENT_SORTING_DEFAULT, SETTINGS_MEDIA_SORTING_DEFAULT } from '../constants';
import { ArticleHelper } from './../helpers/ArticleHelper';
import { basename, dirname, extname, join, parse } from "path";
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 { CustomScript as ICustomScript, DraftField, Framework, ScriptType, SortingSetting, SortOrder, SortType, TaxonomyType } from '../models';
import { Folders } from './Folders';
import { PagesListener } from './../listeners/PagesListener';
import { ExtensionListener } from './../listeners/ExtensionListener';
import { SETTINGS_DASHBOARD_OPENONSTART, CONTEXT } from '../constants';
import { join } from "path";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window } from "vscode";
import { Logger, Settings as SettingsHelper } from '../helpers';
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { DashboardMessage } from '../dashboardWebView/DashboardMessage';
import { Page } from '../dashboardWebView/models/Page';
import { openFileInEditor } from '../helpers/openFileInEditor';
import { Template } from './Template';
import { Notifications } from '../helpers/Notifications';
import { Extension } from '../helpers/Extension';
import { EditorHelper, WebviewHelper } from '@estruyf/vscode';
import { MediaInfo, MediaPaths } from './../models/MediaPaths';
import { decodeBase64Image } from '../helpers/decodeBase64Image';
import { WebviewHelper } from '@estruyf/vscode';
import { DashboardData } from '../models/DashboardData';
import { ExplorerView } from '../explorerView/ExplorerView';
import { MediaLibrary } from '../helpers/MediaLibrary';
import { parseWinPath } from '../helpers/parseWinPath';
import { DateHelper } from '../helpers/DateHelper';
import { FrameworkDetector } from '../helpers/FrameworkDetector';
import { ContentType } from '../helpers/ContentType';
import { SortingOption } from '../dashboardWebView/models';
import { Sorting } from '../helpers/Sorting';
import imageSize from 'image-size';
import { CustomScript } from '../helpers/CustomScript';
import { DashboardListener, SettingsListener } from '../listeners';
import { DashboardListener, MediaListener, SettingsListener } from '../listeners';
export class Dashboard {
private static webview: WebviewPanel | null = null;
private static mediaLib: MediaLibrary;
private static _viewData: DashboardData | undefined;
private static isDisposed: boolean = true;
@@ -53,12 +35,12 @@ export class Dashboard {
* Open or reveal the dashboard
*/
public static async open(data?: DashboardData) {
this.mediaLib = MediaLibrary.getInstance();
MediaLibrary.getInstance();
Dashboard._viewData = data;
if (Dashboard.isOpen) {
Dashboard.reveal();
Dashboard.reveal(!!data);
} else {
Dashboard.create();
}
@@ -76,9 +58,13 @@ export class Dashboard {
/**
* Reveal the dashboard if it is open
*/
public static reveal() {
public static reveal(hasData: boolean = false) {
if (Dashboard.webview) {
Dashboard.webview.reveal();
if (hasData) {
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard.viewData });
}
}
}
@@ -112,7 +98,8 @@ export class Dashboard {
'FrontMatter Dashboard',
ViewColumn.One,
{
enableScripts: true
enableScripts: true,
retainContextWhenHidden: true
}
);
@@ -130,6 +117,8 @@ export class Dashboard {
Dashboard._viewData = undefined;
const panel = ExplorerView.getInstance(extensionUri);
panel.getMediaSelection();
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: null });
}
await commands.executeCommand('setContext', CONTEXT.isDashboardOpen, this.webview?.visible);
@@ -148,37 +137,13 @@ export class Dashboard {
});
Dashboard.webview.webview.onDidReceiveMessage(async (msg) => {
Logger.info(`Receiving message from webview: ${msg.command}`);
DashboardListener.process(msg);
switch(msg.command) {
case DashboardMessage.insertPreviewImage:
Dashboard.insertImage(msg?.data);
break;
case DashboardMessage.updateMediaMetadata:
Dashboard.updateMediaMetadata(msg?.data);
break;
case DashboardMessage.createMediaFolder:
await commands.executeCommand(COMMAND_NAME.createFolder, msg?.data);
break;
case DashboardMessage.setFramework:
Dashboard.setFramework(msg?.data);
break;
case DashboardMessage.runCustomScript:
CustomScript.run(msg?.data?.script, msg?.data?.path);
break;
case DashboardMessage.setState:
if (msg?.data?.key && msg?.data?.value) {
Extension.getInstance().setState(msg?.data?.key, msg?.data?.value, "workspace");
}
break;
}
ExtensionListener.process(msg);
MediaListener.process(msg);
PagesListener.process(msg);
SettingsListener.process(msg);
});
}
@@ -190,11 +155,6 @@ export class Dashboard {
return Dashboard.webview?.webview;
}
public static switchFolder(folderPath: string) {
Dashboard.resetMedia();
Dashboard.getMedia(0, folderPath);
}
/**
* Post data to the dashboard
* @param msg
@@ -208,36 +168,6 @@ export class Dashboard {
Dashboard.webview?.webview.postMessage(msg);
}
}
/**
* Set the current site-generator or framework + related settings
* @param frameworkId
*/
private static setFramework(frameworkId: string | null) {
SettingsHelper.update(SETTINGS_FRAMEWORK_ID, frameworkId, true);
if (frameworkId) {
const allFrameworks = FrameworkDetector.getAll();
const framework = allFrameworks.find((f: Framework) => f.name === frameworkId);
if (framework) {
SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, framework.static, true);
} else {
SettingsHelper.update(SETTINGS_CONTENT_STATIC_FOLDER, "", true);
}
}
}
/**
* Update the metadata of the selected file
*/
private static async updateMediaMetadata({ file, filename, page, folder, ...metadata }: { file:string; filename:string; page: number; folder: string | null; metadata: any; }) {
Dashboard.mediaLib.set(file, metadata);
// Check if filename needs to be updated
Dashboard.mediaLib.updateFilename(file, filename);
Dashboard.getMedia(page || 0, folder || "");
}
/**
* Retrieve the webview HTML contents
+4 -1
View File
@@ -11,6 +11,8 @@ import { existsSync, mkdirSync } from 'fs';
import { format } from 'date-fns';
import { Dashboard } from './Dashboard';
import { parseWinPath } from '../helpers/parseWinPath';
import { MediaHelpers } from '../helpers/MediaHelpers';
import { MediaListener } from '../listeners';
export const WORKSPACE_PLACEHOLDER = `[[workspace]]`;
@@ -62,7 +64,8 @@ export class Folders {
}
if (Dashboard.isOpen) {
Dashboard.switchFolder(folderName);
MediaHelpers.resetMedia();
MediaListener.sendMediaFiles(0, folderName);
}
}
+1
View File
@@ -28,6 +28,7 @@ export const COMMAND_NAME = {
collapseSections: getCommandName("collapseSections"),
preview: getCommandName("preview"),
dashboard: getCommandName("dashboard"),
dashboardMedia: getCommandName("dashboard.media"),
dashboardClose: getCommandName("dashboard.close"),
promote: getCommandName("promoteSettings"),
insertImage: getCommandName("insertImage"),
@@ -20,19 +20,17 @@ export const Contents: React.FunctionComponent<IContentsProps> = ({pages, loadin
const pageFolders = [...new Set(pageItems.map(page => page.fmFolder))];
return (
<main className={`h-full w-full`}>
<div className="flex flex-col h-full overflow-auto">
<Header
folders={pageFolders}
totalPages={pageItems.length}
settings={settings} />
<div className="flex flex-col h-full overflow-auto">
<Header
folders={pageFolders}
totalPages={pageItems.length}
settings={settings} />
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
</div>
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4">
{ loading ? <Spinner /> : <Overview pages={pageItems} settings={settings} /> }
</div>
</main>
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
</div>
);
};
@@ -3,11 +3,12 @@ import { useRecoilValue } from 'recoil';
import { MarkdownIcon } from '../../../panelWebView/components/Icons/MarkdownIcon';
import { DashboardMessage } from '../../DashboardMessage';
import { Page } from '../../models/Page';
import { SettingsSelector, ViewSelector, ViewType } from '../../state';
import { SettingsSelector, ViewSelector } from '../../state';
import { DateField } from '../DateField';
import { Status } from '../Status';
import { Messenger } from '@estruyf/vscode/dist/client';
import useContentType from '../../../hooks/useContentType';
import { DashboardViewType } from '../../models';
export interface IItemProps extends Page {}
@@ -22,7 +23,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
Messenger.send(DashboardMessage.openFile, fmFilePath);
};
if (view === ViewType.Grid) {
if (view === DashboardViewType.Grid) {
return (
<li className="relative">
<button className={`group cursor-pointer flex flex-wrap items-start content-start h-full w-full bg-gray-50 dark:bg-vulcan-200 text-vulcan-500 dark:text-whisper-500 text-left overflow-hidden shadow-md hover:shadow-xl dark:hover:bg-vulcan-100`}
@@ -53,7 +54,7 @@ export const Item: React.FunctionComponent<IItemProps> = ({ fmFilePath, date, ti
</button>
</li>
);
} else if (view === ViewType.List) {
} else if (view === DashboardViewType.List) {
return (
<li className="relative">
<button className={`px-5 cursor-pointer w-full text-left grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8 py-2 border-b border-gray-300 hover:bg-gray-200 dark:border-vulcan-50 dark:hover:bg-vulcan-50 hover:bg-opacity-70`} onClick={openFile}>
@@ -1,6 +1,7 @@
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { ViewSelector, ViewType } from '../../state';
import { DashboardViewType } from '../../models';
import { ViewSelector } from '../../state';
export interface IListProps {}
@@ -8,15 +9,15 @@ export const List: React.FunctionComponent<IListProps> = ({children}: React.Prop
const view = useRecoilValue(ViewSelector);
let className = '';
if (view === ViewType.Grid) {
if (view === DashboardViewType.Grid) {
className = `grid grid-cols-2 gap-x-4 gap-y-8 sm:grid-cols-3 sm:gap-x-6 lg:grid-cols-4 xl:gap-x-8`;
} else if (view === ViewType.List) {
} else if (view === DashboardViewType.List) {
className = `-mx-4`;
}
return (
<ul role="list" className={className}>
{view === ViewType.List && (
{view === DashboardViewType.List && (
<li className="px-5 relative uppercase text-vulcan-100 dark:text-whisper-900 py-2 border-b border-vulcan-50">
<div className={`grid grid-cols-12 gap-x-4 sm:gap-x-6 xl:gap-x-8`}>
<div className="col-span-8">
+12 -6
View File
@@ -7,7 +7,7 @@ import { useRecoilValue } from 'recoil';
import { DashboardViewSelector } from '../state';
import { Contents } from './Contents/Contents';
import { Media } from './Media/Media';
import { ViewType } from '../models';
import { NavigationType } from '../models';
export interface IDashboardProps {
showWelcome: boolean;
@@ -30,9 +30,15 @@ export const Dashboard: React.FunctionComponent<IDashboardProps> = ({showWelcome
return <WelcomeScreen settings={settings} />;
}
if (view === ViewType.Media) {
return <Media />;
}
return <Contents pages={pages} loading={loading} />;
return (
<main className={`h-full w-full`}>
{
view === NavigationType.Media ? (
<Media />
) : (
<Contents pages={pages} loading={loading} />
)
}
</main>
);
};
@@ -5,7 +5,7 @@ import { useRecoilState, useRecoilValue } from 'recoil';
import { Sorting } from '.';
import { HOME_PAGE_NAVIGATION_ID } from '../../../constants';
import { parseWinPath } from '../../../helpers/parseWinPath';
import { ViewType } from '../../models';
import { NavigationType } from '../../models';
import { SelectedMediaFolderAtom, SettingsAtom } from '../../state';
export interface IBreadcrumbProps {}
@@ -99,7 +99,7 @@ export const Breadcrumb: React.FunctionComponent<IBreadcrumbProps> = (props: Rea
</ol>
<div className={`flex px-5`}>
<Sorting view={ViewType.Media} disableCustomSorting />
<Sorting view={NavigationType.Media} disableCustomSorting />
</div>
</nav>
);
@@ -3,7 +3,7 @@ import { Sorting } from './Sorting';
import { Searchbox } from './Searchbox';
import { Filter } from './Filter';
import { Folders } from './Folders';
import { Settings, ViewType } from '../../models';
import { Settings, NavigationType } from '../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { Startup } from '../Startup';
import { Navigation } from '../Navigation';
@@ -47,7 +47,7 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
Messenger.send(DashboardMessage.createByTemplate);
};
const updateView = (view: ViewType) => {
const updateView = (view: NavigationType) => {
setView(view);
resetSorting();
}
@@ -57,17 +57,17 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
<div className={`px-4 bg-gray-50 dark:bg-vulcan-50 border-b-2 border-gray-200 dark:border-vulcan-200`}>
<div className={`flex items-center justify-start`}>
<button className={`p-2 flex items-center ${view === "contents" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => updateView(ViewType.Contents)}>
<button className={`p-2 flex items-center ${view === "contents" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => updateView(NavigationType.Contents)}>
<MarkdownIcon className={`h-6 w-auto mr-2`} /><span>Contents</span>
</button>
<button className={`p-2 flex items-center ${view === "media" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => updateView(ViewType.Media)}>
<button className={`p-2 flex items-center ${view === "media" ? "bg-gray-200 dark:bg-vulcan-200" : ""} hover:bg-gray-100 dark:hover:bg-vulcan-100`} onClick={() => updateView(NavigationType.Media)}>
<PhotographIcon className={`h-6 w-auto mr-2`} /><span>Media</span>
</button>
</div>
</div>
{
view === ViewType.Contents && (
view === NavigationType.Contents && (
<>
<div className={`px-4 mt-3 mb-2 flex items-center justify-between`}>
<Searchbox />
@@ -112,14 +112,14 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
<Grouping />
<Sorting view={ViewType.Contents} />
<Sorting view={NavigationType.Contents} />
</div>
</>
)
}
{
view === ViewType.Media && (
view === NavigationType.Media && (
<>
<Pagination />
<Breadcrumb />
@@ -6,14 +6,14 @@ import { ExtensionState } from '../../../constants';
import { SortOrder, SortType } from '../../../models';
import { SortOption } from '../../constants/SortOption';
import { DashboardMessage } from '../../DashboardMessage';
import { ViewType } from '../../models';
import { NavigationType } from '../../models';
import { SortingOption } from '../../models/SortingOption';
import { SearchSelector, SettingsSelector, SortingAtom } from '../../state';
import { MenuButton, MenuItem, MenuItems } from '../Menu';
export interface ISortingProps {
disableCustomSorting?: boolean;
view: ViewType;
view: NavigationType;
}
export const sortOptions: SortingOption[] = [
@@ -30,7 +30,7 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({disableCustomSo
const updateSorting = (value: SortingOption) => {
Messenger.send(DashboardMessage.setState, {
key: `${view === ViewType.Contents ? ExtensionState.Dashboard.Contents.Sorting : ExtensionState.Dashboard.Media.Sorting}`,
key: `${view === NavigationType.Contents ? ExtensionState.Dashboard.Contents.Sorting : ExtensionState.Dashboard.Media.Sorting}`,
value: value
});
@@ -50,16 +50,16 @@ export const Sorting: React.FunctionComponent<ISortingProps> = ({disableCustomSo
let crntSortingOption = crntSorting;
if (!crntSortingOption) {
if (view === ViewType.Contents) {
if (view === NavigationType.Contents) {
crntSortingOption = settings?.dashboardState?.contents?.sorting || null;
} else if (view === ViewType.Media) {
} else if (view === NavigationType.Media) {
crntSortingOption = settings?.dashboardState?.media?.sorting || null;
}
if (crntSortingOption === null) {
if (view === ViewType.Contents && settings?.dashboardState.contents.defaultSorting) {
if (view === NavigationType.Contents && settings?.dashboardState.contents.defaultSorting) {
crntSortingOption = allOptions.find(f => f.id === settings?.dashboardState.contents.defaultSorting) || null;
} else if (view === ViewType.Media && settings?.dashboardState.contents.defaultSorting) {
} else if (view === NavigationType.Media && settings?.dashboardState.contents.defaultSorting) {
crntSortingOption = allOptions.find(f => f.id === settings?.dashboardState.contents.defaultSorting) || null;
}
}
@@ -1,9 +1,10 @@
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { ViewAtom, ViewType, SettingsSelector } from '../../state';
import { ViewAtom, SettingsSelector } from '../../state';
import { ViewGridIcon, ViewListIcon } from '@heroicons/react/solid';
import { Messenger } from '@estruyf/vscode/dist/client';
import { DashboardMessage } from '../../DashboardMessage';
import { DashboardViewType } from '../../models';
export interface IViewSwitchProps {}
@@ -12,7 +13,7 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (props: Rea
const settings = useRecoilValue(SettingsSelector);
const toggleView = () => {
const newView = view === ViewType.Grid ? ViewType.List : ViewType.Grid;
const newView = view === DashboardViewType.Grid ? DashboardViewType.List : DashboardViewType.Grid;
setView(newView);
Messenger.send(DashboardMessage.setPageViewType, newView);
};
@@ -25,11 +26,11 @@ export const ViewSwitch: React.FunctionComponent<IViewSwitchProps> = (props: Rea
return (
<div className={`flex rounded-sm bg-vulcan-50 lg:mb-1`}>
<button className={`flex items-center px-2 py-1 rounded-l-sm ${view === ViewType.Grid ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<button className={`flex items-center px-2 py-1 rounded-l-sm ${view === DashboardViewType.Grid ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<ViewGridIcon className={`w-4 h-4`} />
<span className={`sr-only`}>Change to grid</span>
</button>
<button className={`flex items-center px-2 py-1 rounded-r-sm ${view === ViewType.List ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<button className={`flex items-center px-2 py-1 rounded-r-sm ${view === DashboardViewType.List ? 'bg-teal-500 text-vulcan-500' : 'text-whisper-500'}`} onClick={toggleView}>
<ViewListIcon className={`w-4 h-4`} />
<span className={`sr-only`}>Change to list</span>
</button>
+61 -63
View File
@@ -73,75 +73,73 @@ export const Media: React.FunctionComponent<IMediaProps> = (props: React.PropsWi
}, ['']);
return (
<main className={`h-full w-full`}>
<div className="flex flex-col h-full overflow-auto">
<Header settings={settings} />
<div className="flex flex-col h-full overflow-auto">
<Header settings={settings} />
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4" {...getRootProps()}>
{
viewData?.data?.filePath && (
<div className={`text-lg text-center mb-6`}>
<p>Select the image you want to use for your article.</p>
<p className={`opacity-80 text-base`}>You can also drag and drop images from your desktop and select that once uploaded.</p>
</div>
)
}
{
isDragActive && (
<div className="absolute top-0 left-0 w-full h-full text-whisper-500 bg-gray-900 bg-opacity-70 flex flex-col justify-center items-center z-50">
<UploadIcon className={`h-32`} />
<p className={`text-xl max-w-md text-center`}>
{selectedFolder ? `Upload to ${selectedFolder}` : `No folder selected, files you drop will be added to the ${settings?.staticFolder || "public"} folder.`}
</p>
</div>
)
}
{
(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) => (
<Item key={file.fsPath} media={file} />
))
}
</List>
</div>
<div className="w-full flex-grow max-w-7xl mx-auto py-6 px-4" {...getRootProps()}>
{
loading && ( <Spinner /> )
viewData?.data?.filePath && (
<div className={`text-lg text-center mb-6`}>
<p>Select the image you want to use for your article.</p>
<p className={`opacity-80 text-base`}>You can also drag and drop images from your desktop and select that once uploaded.</p>
</div>
)
}
{
isDragActive && (
<div className="absolute top-0 left-0 w-full h-full text-whisper-500 bg-gray-900 bg-opacity-70 flex flex-col justify-center items-center z-50">
<UploadIcon className={`h-32`} />
<p className={`text-xl max-w-md text-center`}>
{selectedFolder ? `Upload to ${selectedFolder}` : `No folder selected, files you drop will be added to the ${settings?.staticFolder || "public"} folder.`}
</p>
</div>
)
}
<Lightbox />
{
(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>
)
}
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
{
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) => (
<Item key={file.fsPath} media={file} />
))
}
</List>
</div>
</main>
{
loading && ( <Spinner /> )
}
<Lightbox />
<SponsorMsg beta={settings?.beta} version={settings?.versionInfo} />
</div>
);
};
@@ -3,9 +3,11 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import { RefreshIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useRecoilState, useRecoilValue } from 'recoil';
import { useDebounce } from '../../../hooks/useDebounce';
import { usePrevious } from '../../../panelWebView/hooks/usePrevious';
import { DashboardCommand } from '../../DashboardCommand';
import { DashboardMessage } from '../../DashboardMessage';
import { LoadingAtom, MediaTotalSelector, PageAtom, SelectedMediaFolderSelector, SortingSelector } from '../../state';
import { LoadingAtom, MediaTotalSelector, PageAtom, SelectedMediaFolderSelector, SettingsSelector, SortingSelector } from '../../state';
import { FolderCreation } from './FolderCreation';
import { LIMIT } from './Media';
import { PaginationButton } from './PaginationButton';
@@ -13,12 +15,16 @@ import { PaginationButton } from './PaginationButton';
export interface IPaginationProps {}
export const Pagination: React.FunctionComponent<IPaginationProps> = ({}: React.PropsWithChildren<IPaginationProps>) => {
const [ lastUpdated, setLastUpdated ] = React.useState<string | null>(null);
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const crntSorting = useRecoilValue(SortingSelector);
const totalMedia = useRecoilValue(MediaTotalSelector);
const [ , setLoading ] = useRecoilState(LoadingAtom);
const [ page, setPage ] = useRecoilState(PageAtom);
const settings = useRecoilValue(SettingsSelector);
const debounceGetMedia = useDebounce<string | null>(lastUpdated, 200);
const prevSelectedFolder = usePrevious<string | null>(selectedFolder);
const totalPages = Math.ceil(totalMedia / LIMIT) - 1;
const getTotalPage = () => {
@@ -61,35 +67,39 @@ export const Pagination: React.FunctionComponent<IPaginationProps> = ({}: React.
}
React.useEffect(() => {
setLoading(true);
Messenger.send(DashboardMessage.getMedia, {
page,
folder: selectedFolder || '',
sorting: crntSorting
});
setLastUpdated(new Date().getTime().toString());
}, [page]);
React.useEffect(() => {
setLoading(true);
Messenger.send(DashboardMessage.getMedia, {
page: 0,
folder: selectedFolder || '',
sorting: crntSorting
});
setPage(0);
if (prevSelectedFolder !== null || settings?.dashboardState?.media.selectedFolder !== selectedFolder) {
setLoading(true);
setPage(0);
setLastUpdated(new Date().getTime().toString());
}
}, [selectedFolder]);
React.useEffect(() => {
setLoading(true);
Messenger.send(DashboardMessage.getMedia, {
page,
folder: selectedFolder || '',
sorting: crntSorting
});
setLastUpdated(new Date().getTime().toString());
}, [crntSorting]);
React.useEffect(() => {
if (debounceGetMedia) {
setLoading(true);
Messenger.send(DashboardMessage.getMedia, {
page,
folder: selectedFolder || '',
sorting: crntSorting
});
}
}, [debounceGetMedia]);
React.useEffect(() => {
Messenger.listen(mediaUpdate);
return () => {
Messenger.unlisten(mediaUpdate);
}
}, []);
return (
+5 -3
View File
@@ -6,7 +6,7 @@ import { Page } from '../models/Page';
import { DashboardViewAtom, SettingsAtom, ViewDataAtom } from '../state';
import { Messenger } from '@estruyf/vscode/dist/client';
import { EventData } from '@estruyf/vscode/dist/models';
import { ViewType } from '../models';
import { NavigationType } from '../models';
export default function useMessages() {
const [loading, setLoading] = useState<boolean>(false);
@@ -22,8 +22,10 @@ export default function useMessages() {
break;
case DashboardCommand.viewData:
setViewData(message.data.data);
if (message.data.data?.type === ViewType.Media) {
setView(ViewType.Media);
if (message.data.data?.type === NavigationType.Media) {
setView(NavigationType.Media);
} else if (message.data.data?.type === NavigationType.Contents) {
setView(NavigationType.Contents);
}
break;
case DashboardCommand.settings:
@@ -1,4 +1,4 @@
export enum ViewType {
export enum NavigationType {
Contents = "contents",
Media = "media"
}
+7 -3
View File
@@ -1,8 +1,8 @@
import { VersionInfo } from '../../models/VersionInfo';
import { ViewType } from '../state';
import { ContentFolder } from '../../models/ContentFolder';
import { ContentType, CustomScript, DraftField, Framework, SortingSetting } from '../../models';
import { SortingOption } from './SortingOption';
import { DashboardViewType } from '.';
export interface Settings {
beta: boolean;
@@ -14,7 +14,7 @@ export interface Settings {
categories: string[];
openOnStart: boolean | null;
versionInfo: VersionInfo;
pageViewType: ViewType | undefined;
pageViewType: DashboardViewType | undefined;
mediaSnippet: string[];
contentTypes: ContentType[];
contentFolders: ContentFolder[];
@@ -28,10 +28,14 @@ export interface Settings {
export interface DashboardState {
contents: ViewState;
media: ViewState;
media: MediaViewState;
}
export interface ViewState {
sorting: SortingOption | null | undefined;
defaultSorting: string | null | undefined;
}
export interface MediaViewState extends ViewState {
selectedFolder: string | null | undefined;
}
+1 -1
View File
@@ -1,6 +1,6 @@
export * from './DashboardViewType';
export * from './NavigationType';
export * from './Page';
export * from './Settings';
export * from './SortingOption';
export * from './Status';
export * from './ViewType';
@@ -1,7 +1,7 @@
import { atom } from 'recoil';
import { ViewType } from '../../models';
import { NavigationType } from '../../models';
export const DashboardViewAtom = atom<ViewType>({
export const DashboardViewAtom = atom<NavigationType>({
key: 'DashboardViewAtom',
default: ViewType.Contents
default: NavigationType.Contents
});
+9 -1
View File
@@ -45,7 +45,15 @@ export async function activate(context: vscode.ExtensionContext) {
// Pages dashboard
Dashboard.init();
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.dashboard, (data?: DashboardData) => {
Dashboard.open(data);
if (!data) {
Dashboard.open({ type: "contents" });
} else {
Dashboard.open(data);
}
}));
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.dashboardMedia, (data?: DashboardData) => {
Dashboard.open({ type: "media" });
}));
subscriptions.push(vscode.commands.registerCommand(COMMAND_NAME.dashboardClose, (data?: DashboardData) => {
+4 -3
View File
@@ -1,7 +1,7 @@
import { Folders } from "../commands/Folders";
import { Template } from "../commands/Template";
import { ExtensionState, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_SORTING, SETTINGS_CONTENT_SORTING_DEFAULT, SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_FRAMEWORK_ID, SETTINGS_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES } from "../constants";
import { DashboardViewType, SortingOption } from "../dashboardWebView/models";
import { DashboardViewType, SortingOption, Settings as ISettings } from "../dashboardWebView/models";
import { CustomScript, DraftField, ScriptType, SortingSetting, TaxonomyType } from "../models";
import { Extension } from "./Extension";
import { FrameworkDetector } from "./FrameworkDetector";
@@ -41,9 +41,10 @@ export class DashboardSettings {
},
media: {
sorting: await ext.getState<SortingOption | undefined>(ExtensionState.Dashboard.Media.Sorting, "workspace"),
defaultSorting: Settings.get<string>(SETTINGS_MEDIA_SORTING_DEFAULT)
defaultSorting: Settings.get<string>(SETTINGS_MEDIA_SORTING_DEFAULT),
selectedFolder: await ext.getState<string | undefined>(ExtensionState.SelectedFolder, "workspace")
}
}
} as Settings
} as ISettings
}
}
+3 -2
View File
@@ -1,5 +1,6 @@
import { Extension } from './Extension';
import { OutputChannel, window } from 'vscode';
import { format } from 'date-fns';
export class Logger {
private static instance: Logger;
@@ -17,11 +18,11 @@ export class Logger {
return Logger.instance;
}
public static info(message: string): void {
public static info(message: string, type: "INFO" | "WARNING" | "ERROR" = "INFO"): void {
if (!Logger.channel) {
Logger.getInstance();
}
Logger.channel?.appendLine(message);
Logger.channel?.appendLine(`["${type}" - ${format(new Date(), "HH:MM:ss")}] ${message}`);
}
}
+16 -2
View File
@@ -4,9 +4,9 @@ import { Folders } from "../commands/Folders";
import { ExtensionState, HOME_PAGE_NAVIGATION_ID, SETTINGS_CONTENT_STATIC_FOLDER } from "../constants";
import { SortingOption } from "../dashboardWebView/models";
import { MediaInfo, MediaPaths, SortOrder, SortType } from "../models";
import { basename, extname, join, parse } from "path";
import { basename, extname, join, parse, dirname } from "path";
import { existsSync, readdirSync, statSync, unlinkSync, writeFileSync } from "fs";
import { commands, Uri, workspace } from "vscode";
import { commands, Uri, workspace, window, Position } from "vscode";
import imageSize from "image-size";
import { EditorHelper } from "@estruyf/vscode";
import { ExplorerView } from "../explorerView/ExplorerView";
@@ -293,6 +293,20 @@ export class MediaHelpers {
}
}
/**
* Update the metadata of a media file
* @param data
*/
public static updateMetadata(data: any) {
const { file, filename, page, folder, ...metadata }: { file:string; filename:string; page: number; folder: string | null; metadata: any; } = data;
const mediaLib = MediaLibrary.getInstance();
mediaLib.set(file, metadata);
// Check if filename needs to be updated
mediaLib.updateFilename(file, filename);
}
/**
* Filter the media files
*/
+3 -4
View File
@@ -1,5 +1,4 @@
import { Dashboard } from '../commands/Dashboard';
import { MediaHelpers } from './MediaHelpers';
import { workspace } from 'vscode';
import { JsonDB } from 'node-json-db/dist/JsonDB';
import { basename, dirname, join, parse } from 'path';
@@ -34,7 +33,7 @@ export class MediaLibrary {
f.oldUri.path.endsWith('.png') ||
f.oldUri.path.endsWith('.gif')) {
this.rename(f.oldUri.fsPath, f.newUri.fsPath);
Dashboard.resetMedia();
MediaHelpers.resetMedia();
}
});
});
@@ -89,7 +88,7 @@ export class MediaLibrary {
} else {
renameSync(filePath, newPath);
this.rename(filePath, newPath);
Dashboard.resetMedia();
MediaHelpers.resetMedia();
}
} catch(err) {
Notifications.error(`Something went wrong updating "${name}" to "${filename}".`);
+4
View File
@@ -1,18 +1,22 @@
import { window } from "vscode";
import { Logger } from ".";
import { EXTENSION_NAME } from "../constants";
export class Notifications {
public static info(message: string, items?: any): Thenable<string> {
Logger.info(`${EXTENSION_NAME}: ${message}`, "INFO");
return window.showInformationMessage(`${EXTENSION_NAME}: ${message}`, items);
}
public static warning(message: string, items?: any): Thenable<string> {
Logger.info(`${EXTENSION_NAME}: ${message}`, "WARNING");
return window.showWarningMessage(`${EXTENSION_NAME}: ${message}`, items);
}
public static error(message: string, items?: any): Thenable<string> {
Logger.info(`${EXTENSION_NAME}: ${message}`, "ERROR");
return window.showErrorMessage(`${EXTENSION_NAME}: ${message}`, items);
}
}
+1 -3
View File
@@ -6,9 +6,7 @@ import { Logger } from "../helpers/Logger";
export abstract class BaseListener {
public static process(msg: { command: DashboardMessage, data: any }) {
Logger.info(`Receiving message from webview: ${msg.command}`);
}
public static process(msg: { command: DashboardMessage, data: any }) {}
/**
* Send a message to the webview
+1 -1
View File
@@ -18,7 +18,7 @@ export class DashboardListener extends BaseListener {
switch(msg.command) {
case DashboardMessage.getViewType:
if (Dashboard.viewData) {
Dashboard.postWebviewMessage({ command: DashboardCommand.viewData, data: Dashboard.viewData });
this.sendMsg(DashboardCommand.viewData, Dashboard.viewData);
}
break;
case DashboardMessage.reload:
+14
View File
@@ -2,6 +2,7 @@ import { commands, env } from "vscode";
import { SettingsListener } from ".";
import { COMMAND_NAME } from "../constants";
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
import { CustomScript, Extension } from "../helpers";
import { openFileInEditor } from "../helpers/openFileInEditor";
import { BaseListener } from "./BaseListener";
@@ -21,6 +22,19 @@ export class ExtensionListener extends BaseListener {
case DashboardMessage.copyToClipboard:
env.clipboard.writeText(msg.data);
break;
case DashboardMessage.runCustomScript:
CustomScript.run(msg?.data?.script, msg?.data?.path);
break;
case DashboardMessage.setState:
this.setState(msg?.data);
break;
}
}
private static setState(data: any) {
const { key, value } = data;
if (key && value) {
Extension.getInstance().setState(key, value, "workspace");
}
}
}
+25 -2
View File
@@ -3,6 +3,8 @@ import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
import { BaseListener } from "./BaseListener";
import { DashboardCommand } from '../dashboardWebView/DashboardCommand';
import { SortingOption } from '../dashboardWebView/models';
import { commands } from 'vscode';
import { COMMAND_NAME } from '../constants';
export class MediaListener extends BaseListener {
@@ -27,7 +29,14 @@ export class MediaListener extends BaseListener {
this.delete(msg?.data);
break;
case DashboardMessage.insertPreviewImage:
MediaHelpers.insertMediaToMarkdown(msg?.data);
break;
case DashboardMessage.updateMediaMetadata:
this.update(msg.data);
break;
case DashboardMessage.createMediaFolder:
await commands.executeCommand(COMMAND_NAME.createFolder, msg?.data);
break;
}
}
@@ -37,7 +46,7 @@ export class MediaListener extends BaseListener {
* @param folder
* @param sorting
*/
private static async sendMediaFiles(page: number = 0, folder: string = '', sorting: SortingOption | null = null) {
public static async sendMediaFiles(page: number = 0, folder: string = '', sorting: SortingOption | null = null) {
const files = await MediaHelpers.getMedia(page, folder, sorting);
this.sendMsg(DashboardCommand.media, files);
}
@@ -75,4 +84,18 @@ export class MediaListener extends BaseListener {
this.sendMediaFiles(data.page || 0, data.folder || "");
} catch {}
}
/**
* Update media metadata
* @param data
*/
private static update(data: any) {
try {
const { page, folder } = data;
MediaHelpers.updateMetadata(data);
this.sendMediaFiles(page || 0, folder || "");
} catch {}
}
}
+23 -6
View File
@@ -1,13 +1,9 @@
import { Folders } from "../commands/Folders";
import { Template } from "../commands/Template";
import { ExtensionState, SETTINGS_CONTENT_DRAFT_FIELD, SETTINGS_CONTENT_SORTING, SETTINGS_CONTENT_SORTING_DEFAULT, SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_DASHBOARD_MEDIA_SNIPPET, SETTINGS_DASHBOARD_OPENONSTART, SETTINGS_FRAMEWORK_ID, SETTINGS_MEDIA_SORTING_DEFAULT, SETTING_CUSTOM_SCRIPTS, SETTING_TAXONOMY_CONTENT_TYPES } from "../constants";
import { SETTINGS_CONTENT_STATIC_FOLDER, SETTINGS_FRAMEWORK_ID } from "../constants";
import { DashboardCommand } from "../dashboardWebView/DashboardCommand";
import { DashboardMessage } from "../dashboardWebView/DashboardMessage";
import { DashboardViewType, SortingOption } from "../dashboardWebView/models";
import { DashboardSettings, Settings } from "../helpers";
import { Extension } from "../helpers/Extension";
import { FrameworkDetector } from "../helpers/FrameworkDetector";
import { CustomScript, DraftField, ScriptType, SortingSetting, TaxonomyType } from "../models";
import { Framework } from "../models";
import { BaseListener } from "./BaseListener";
@@ -27,6 +23,9 @@ export class SettingsListener extends BaseListener {
case DashboardMessage.updateSetting:
this.update(msg.data);
break;
case DashboardMessage.setFramework:
this.setFramework(msg?.data);
break;
}
}
@@ -49,4 +48,22 @@ export class SettingsListener extends BaseListener {
this.sendMsg(DashboardCommand.settings, settings);
}
/**
* Set the current site-generator or framework + related settings
* @param frameworkId
*/
private static setFramework(frameworkId: string | null) {
Settings.update(SETTINGS_FRAMEWORK_ID, frameworkId, true);
if (frameworkId) {
const allFrameworks = FrameworkDetector.getAll();
const framework = allFrameworks.find((f: Framework) => f.name === frameworkId);
if (framework) {
Settings.update(SETTINGS_CONTENT_STATIC_FOLDER, framework.static, true);
} else {
Settings.update(SETTINGS_CONTENT_STATIC_FOLDER, "", true);
}
}
}
}