#72 #91 - Further implementation of media view + content folder support

This commit is contained in:
Elio Struyf
2021-09-07 17:52:34 +02:00
parent 86c609f49f
commit 134a5aaa0d
22 changed files with 530 additions and 61 deletions
+114 -22
View File
@@ -1,7 +1,8 @@
import { SETTINGS_CONTENT_STATIC_FOLDERS, SETTING_DATE_FIELD, SETTING_SEO_DESCRIPTION_FIELD, SETTINGS_DASHBOARD_OPENONSTART } from './../constants/settings';
import { ArticleHelper } from './../helpers/ArticleHelper';
import { basename, extname, join } from "path";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace } from "vscode";
import { dirname, extname, join } from "path";
import { existsSync, statSync } from "fs";
import { commands, Uri, ViewColumn, Webview, WebviewPanel, window, workspace, env } from "vscode";
import { SettingsHelper } from '../helpers';
import { TaxonomyType } from '../models';
import { Folders } from './Folders';
@@ -17,12 +18,13 @@ import { Extension } from '../helpers/Extension';
import { parseJSON } from 'date-fns';
import { ViewType } from '../pagesView/state';
import { WebviewHelper } from '@estruyf/vscode';
import { MediaPaths } from '../models/MediaPaths';
import { MediaInfo, MediaPaths } from './../models/MediaPaths';
export class Dashboard {
private static webview: WebviewPanel | null = null;
private static isDisposed: boolean = true;
private static media: MediaInfo[] = [];
/** 
* Init the dashboard
@@ -131,7 +133,10 @@ export class Dashboard {
Extension.getInstance().setState(EXTENSION_STATE_PAGES_VIEW, msg.data);
break;
case DashboardMessage.getMedia:
Dashboard.getMedia();
Dashboard.getMedia(msg?.data?.page, msg?.data?.folder)
break;
case DashboardMessage.copyToClipboard:
env.clipboard.writeText(msg.data);
break;
}
});
@@ -142,15 +147,19 @@ export class Dashboard {
*/
private static async getSettings() {
const ext = Extension.getInstance();
const config = SettingsHelper.getConfig();
const wsFolder = Folders.getWorkspaceFolder();
Dashboard.postWebviewMessage({
command: DashboardCommand.settings,
data: {
wsFolder: wsFolder ? wsFolder.fsPath : '',
staticFolder: config.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS),
folders: Folders.get(),
initialized: await Template.isInitialized(),
tags: SettingsHelper.getTaxonomy(TaxonomyType.Tag),
categories: SettingsHelper.getTaxonomy(TaxonomyType.Category),
openOnStart: SettingsHelper.getConfig().get(SETTINGS_DASHBOARD_OPENONSTART),
openOnStart: config.get(SETTINGS_DASHBOARD_OPENONSTART),
versionInfo: ext.getVersion(),
pageViewType: await ext.getState<ViewType | undefined>(EXTENSION_STATE_PAGES_VIEW)
} as Settings
@@ -168,23 +177,80 @@ export class Dashboard {
/**
* Retrieve all media files
*/
private static async getMedia() {
private static async getMedia(page: number = 0, folder: string = '') {
const wsFolder = Folders.getWorkspaceFolder();
const config = SettingsHelper.getConfig();
const staticFolder = config.get<string>(SETTINGS_CONTENT_STATIC_FOLDERS);
workspace.findFiles(`${staticFolder || ""}/**/*`).then(async (files) => {
const media = files.filter(file => {
const ext = extname(file.fsPath);
return ['.jpg', '.jpeg', '.png', '.gif', '.svg'].includes(ext);
}).map((file) => ({
fsPath: file.fsPath,
vsPath: Dashboard.webview?.webview.asWebviewUri(file).toString()
} as MediaPaths));
Dashboard.postWebviewMessage({
command: DashboardCommand.media,
data: media
if (Dashboard.media.length === 0) {
const contentFolder = Folders.get();
let allMedia: MediaInfo[] = [];
if (staticFolder) {
const files = await workspace.findFiles(`${staticFolder || ""}/**/*`);
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}/**/*`);
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);
}
// Filter the media
let files = Dashboard.media;
if (folder) {
files = files.filter(f => f.fsPath.includes(folder));
}
// Retrieve the total after filtering and before the slicing happens
const total = files.length;
// Get media set
files = files.slice(page * 16, ((page + 1) * 16));
files = files.map((file) => ({
...file,
stats: statSync(file.fsPath)
}));
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);
}
if (relFolderPath?.startsWith('/')) {
relFolderPath = relFolderPath.substring(1);
}
return dirname(relFolderPath);
}))];
Dashboard.postWebviewMessage({
command: DashboardCommand.media,
data: {
media: files,
total: total,
folders
} as MediaPaths
});
}
@@ -228,10 +294,22 @@ export class Dashboard {
};
if (article?.data.preview && wsFolder) {
const previewPath = join(wsFolder.fsPath, staticFolder || "", article?.data.preview);
const previewUri = Uri.file(previewPath);
const preview = Dashboard.webview?.webview.asWebviewUri(previewUri);
page.preview = preview?.toString() || "";
const staticPath = join(wsFolder.fsPath, staticFolder || "", article?.data.preview);
const contentFolderPath = join(dirname(file.filePath), article?.data.preview);
let previewUri = null;
if (existsSync(staticPath)) {
previewUri = Uri.file(staticPath);
} else if (existsSync(contentFolderPath)) {
previewUri = Uri.file(contentFolderPath);
}
if (previewUri) {
const preview = Dashboard.webview?.webview.asWebviewUri(previewUri);
page.preview = preview?.toString() || "";
} else {
page.preview = "";
}
}
pages.push(page);
@@ -250,6 +328,20 @@ export class Dashboard {
});
}
/**
* Filter the media files
*/
private static filterMedia(files: Uri[]) {
return files.filter(file => {
const ext = extname(file.fsPath);
return ['.jpg', '.jpeg', '.png', '.gif', '.svg'].includes(ext);
}).map((file) => ({
fsPath: file.fsPath,
vsPath: Dashboard.webview?.webview.asWebviewUri(file).toString(),
stats: undefined
} as MediaInfo));
}
/**
* Post data to the dashboard
* @param msg
+13 -1
View File
@@ -1 +1,13 @@
export interface MediaPaths { fsPath: string; vsPath: string | undefined; }
import { Stats } from "fs";
export interface MediaPaths {
media: MediaInfo[];
total: number;
folders: string[];
}
export interface MediaInfo {
fsPath: string;
vsPath: string | undefined;
stats: Stats | undefined;
}
+1
View File
@@ -8,4 +8,5 @@ export enum DashboardMessage {
reload = 'reload',
setPageViewType = 'setPageViewType',
getMedia = 'getMedia',
copyToClipboard = 'copyToClipboard',
}
@@ -1,11 +1,11 @@
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { MarkdownIcon } from '../../viewpanel/components/Icons/MarkdownIcon';
import { DashboardMessage } from '../DashboardMessage';
import { Page } from '../models/Page';
import { ViewSelector, ViewType } from '../state';
import { DateField } from './DateField';
import { Status } from './Status';
import { MarkdownIcon } from '../../../viewpanel/components/Icons/MarkdownIcon';
import { DashboardMessage } from '../../DashboardMessage';
import { Page } from '../../models/Page';
import { ViewSelector, ViewType } from '../../state';
import { DateField } from '../DateField';
import { Status } from '../Status';
import { Messenger } from '@estruyf/vscode/dist/client';
export interface IItemProps extends Page {}
@@ -1,6 +1,6 @@
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { ViewSelector, ViewType } from '../state';
import { ViewSelector, ViewType } from '../../state';
export interface IListProps {}
@@ -8,8 +8,8 @@ import { GroupOption } from '../../constants/GroupOption';
import { Page } from '../../models/Page';
import { Settings } from '../../models/Settings';
import { GroupingSelector } from '../../state';
import { Item } from '../Item';
import { List } from '../List';
import { Item } from './Item';
import { List } from './List';
export interface IOverviewProps {
pages: Page[];
+13 -6
View File
@@ -16,6 +16,7 @@ import { Messenger } from '@estruyf/vscode/dist/client';
import { ClearFilters } from './ClearFilters';
import { MarkdownIcon } from '../../../viewpanel/components/Icons/MarkdownIcon';
import { PhotographIcon } from '@heroicons/react/outline';
import { Pagination } from '../Media/Pagination';
export interface IHeaderProps {
settings: Settings | null;
@@ -37,15 +38,15 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
};
return (
<div className={`w-full max-w-7xl mx-auto sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
<div className={`w-full sticky top-0 z-40 bg-gray-100 dark:bg-vulcan-500`}>
<div className={`px-4 bg-vulcan-50 border-b-2 border-gray-300 dark:border-vulcan-200`}>
<div className={`py-2 flex items-center justify-start space-x-6 xl:space-x-8`}>
<button className={`flex items-center hover:text-teal-900 ${view === "contents" ? "font-bold" : ""}`} onClick={() => setView("contents")}>
<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={() => setView("contents")}>
<MarkdownIcon className={`h-6 w-auto mr-2`} /><span>Contents</span>
</button>
<button className={`flex items-center hover:text-teal-900 ${view === "media" ? "font-bold" : ""}`} onClick={() => setView("media")}>
<PhotographIcon className={`h-5 w-auto mr-2`} /><span>Media</span>
<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={() => setView("media")}>
<PhotographIcon className={`h-6 w-auto mr-2`} /><span>Media</span>
</button>
</div>
</div>
@@ -89,6 +90,12 @@ export const Header: React.FunctionComponent<IHeaderProps> = ({totalPages, folde
</>
)
}
{
view === "media" && (
<Pagination />
)
}
</div>
);
};
@@ -0,0 +1,77 @@
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>
);
};
+84
View File
@@ -0,0 +1,84 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { ClipboardCopyIcon, FolderIcon, PhotographIcon } from '@heroicons/react/outline';
import { basename, dirname } from 'path';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { MediaInfo } from '../../../models/MediaPaths';
import { DashboardMessage } from '../../DashboardMessage';
import { SettingsSelector } from '../../state';
export interface IItemProps {
media: MediaInfo;
}
export const Item: React.FunctionComponent<IItemProps> = ({media}: React.PropsWithChildren<IItemProps>) => {
const settings = useRecoilValue(SettingsSelector);
const getFolder = () => {
if (settings?.wsFolder && media.fsPath) {
let relPath = media.fsPath.split(settings.wsFolder).pop();
if (settings.staticFolder && relPath) {
relPath = relPath.split(settings.staticFolder).pop();
}
return dirname(relPath || "");
}
return "";
};
const copyToClipboard = () => {
let relPath: string | undefined = "";
if (settings?.wsFolder && media.fsPath) {
relPath = media.fsPath.split(settings.wsFolder).pop();
if (settings.staticFolder && relPath) {
relPath = relPath.split(settings.staticFolder).pop();
}
}
Messenger.send(DashboardMessage.copyToClipboard, relPath || "");
};
const calculateSize = () => {
if (media?.stats?.size) {
const size = media.stats.size / (1024*1024);
if (size > 1) {
return `${size.toFixed(2)} MB`;
} else {
return `${(size * 1024).toFixed(2)} KB`;
}
}
};
return (
<li className="relative bg-gray-50 dark:bg-vulcan-200 hover:shadow-xl dark:hover:bg-vulcan-100">
<div className="bg-white group block w-full aspect-w-10 aspect-h-7 overflow-hidden ">
<img src={media.vsPath} alt={basename(media.fsPath)} className="mx-auto object-cover pointer-events-none" />
</div>
<div className={`relative py-4 pl-4 pr-10`}>
<div className={`absolute top-4 right-4`}>
<button title={`Copy media path`}
className={`hover:text-teal-900 focus:outline-none`}
onClick={copyToClipboard}>
<ClipboardCopyIcon className={`h-5 w-5`} />
<span className={`sr-only`}>Copy media path</span>
</button>
</div>
<p className="text-sm dark:text-whisper-900 font-bold pointer-events-none flex items-center">
{basename(media.fsPath)}
</p>
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
<FolderIcon className={`w-5 h-5 mr-1`} /> {getFolder()}
</p>
{
media?.stats?.size && (
<p className="mt-2 text-sm dark:text-whisper-900 font-medium pointer-events-none flex items-center">
{calculateSize()}
</p>
)
}
</div>
</li>
);
};
+11
View File
@@ -0,0 +1,11 @@
import * as React from 'react';
export interface IListProps {}
export const List: React.FunctionComponent<IListProps> = ({children}: React.PropsWithChildren<IListProps>) => {
return (
<ul role="list" 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">
{children}
</ul>
);
};
+20 -23
View File
@@ -1,35 +1,32 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { MediaPaths } from '../../../models/MediaPaths';
import { useRecoilState, useRecoilValue } from 'recoil';
import { MediaInfo, MediaPaths } from '../../../models/MediaPaths';
import { DashboardCommand } from '../../DashboardCommand';
import { DashboardMessage } from '../../DashboardMessage';
import { SettingsSelector } from '../../state';
import { MediaFoldersAtom, MediaTotalAtom, SettingsSelector } from '../../state';
import { Header } from '../Header';
import { Item } from './Item';
import { List } from './List';
export interface IMediaProps {}
const LIMIT = 25;
export const LIMIT = 16;
export const Media: React.FunctionComponent<IMediaProps> = (props: React.PropsWithChildren<IMediaProps>) => {
const settings = useRecoilValue(SettingsSelector);
const [ media, setMedia ] = React.useState<MediaPaths[]>([]);
const [ page, setPage ] = React.useState<number>(0);
const crntMedia = media.splice(page * LIMIT, LIMIT);
const [ media, setMedia ] = React.useState<MediaInfo[]>([]);
const [ , setTotal ] = useRecoilState(MediaTotalAtom);
const [ , setFolders ] = useRecoilState(MediaFoldersAtom);
React.useEffect(() => {
Messenger.send(DashboardMessage.getMedia);
Messenger.listen<MediaPaths[]>((message) => {
Messenger.listen<MediaPaths>((message) => {
if (message.command === DashboardCommand.media) {
setMedia(message.data);
setPage(0);
setMedia(message.data.media);
setTotal(message.data.total);
setFolders(message.data.folders);
}
});
}, ['']);
console.log(crntMedia)
return (
<main className={`h-full w-full`}>
@@ -37,13 +34,13 @@ export const Media: React.FunctionComponent<IMediaProps> = (props: React.PropsWi
<Header settings={settings} />
<div className="w-full max-w-7xl mx-auto py-6 px-4">
{
crntMedia.map((media) => (
<div key={media.fsPath} className="flex flex-col items-center justify-center w-full h-64">
<img src={media.vsPath} className="w-full h-full" />
</div>
))
}
<List>
{
media.map((file) => (
<Item key={file.fsPath} media={file} />
))
}
</List>
</div>
</div>
</main>
@@ -0,0 +1,116 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import * as React from 'react';
import { useRecoilValue } from 'recoil';
import { DashboardMessage } from '../../DashboardMessage';
import { MediaTotalSelector, SelectedMediaFolderSelector } from '../../state';
import { FolderSelection } from './FolderSelection';
import { LIMIT } from './Media';
import { PaginationButton } from './PaginationButton';
export interface IPaginationProps {}
export const Pagination: React.FunctionComponent<IPaginationProps> = ({}: React.PropsWithChildren<IPaginationProps>) => {
const selectedFolder = useRecoilValue(SelectedMediaFolderSelector);
const totalMedia = useRecoilValue(MediaTotalSelector);
const [ page, setPage ] = React.useState<number>(0);
const totalPages = Math.ceil(totalMedia / LIMIT) - 1;
const getTotalPage = () => {
const mediaItems = ((page + 1) * LIMIT);
if (totalMedia < mediaItems) {
return totalMedia;
}
return mediaItems;
};
// Write me function to retrieve buttons before and after current page
const getButtons = (): number[] => {
const maxButtons = 5;
const buttons: number[] = [];
const start = page - maxButtons;
const end = page + maxButtons;
for (let i = start; i <= end; i++) {
if (i >= 0 && i <= totalPages) {
buttons.push(i);
}
}
return buttons;
};
const buttons = getButtons();
React.useEffect(() => {
Messenger.send(DashboardMessage.getMedia, {
page,
folder: selectedFolder || ''
});
}, [page]);
React.useEffect(() => {
Messenger.send(DashboardMessage.getMedia, {
page: 0,
folder: selectedFolder || ''
});
setPage(0);
}, [selectedFolder]);
return (
<nav
className="py-4 px-5 flex items-center justify-between bg-gray-200 border-b border-gray-300 dark:bg-vulcan-400 dark:border-vulcan-100"
aria-label="Pagination"
>
<div className="hidden sm:block">
<p className="text-sm text-gray-500 dark:text-whisper-900">
Showing <span className="font-medium">{(page * LIMIT) + 1}</span> to <span className="font-medium">{getTotalPage()}</span> of{' '}
<span className="font-medium">{totalMedia}</span> results
</p>
</div>
<FolderSelection />
<div className="flex justify-between sm:justify-end space-x-2 text-sm">
<PaginationButton
title="First"
disabled={page === 0}
onClick={() => {
if (page > 0) {
setPage(0)
}
}} />
<PaginationButton
title="Previous"
disabled={page === 0}
onClick={() => {
if (page > 0) {
setPage(page - 1)
}
}} />
{buttons.map((button) => (
<button
key={button}
disabled={button === page}
onClick={() => {
setPage(button)
}
}
className={`${page === button ? 'bg-gray-200 px-2 text-vulcan-500' : 'text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500'}`}
>{button + 1}</button>
))}
<PaginationButton
title="Next"
disabled={page >= totalPages}
onClick={() => setPage(page + 1)} />
<PaginationButton
title="Last"
disabled={page >= totalPages}
onClick={() => setPage(totalPages)} />
</div>
</nav>
);
};
@@ -0,0 +1,19 @@
import * as React from 'react';
export interface IPaginationButtonProps {
title: string;
disabled?: boolean;
onClick: () => void;
}
export const PaginationButton: React.FunctionComponent<IPaginationButtonProps> = ({title, disabled, onClick}: React.PropsWithChildren<IPaginationButtonProps>) => {
return (
<button
disabled={disabled}
onClick={onClick}
className={`text-gray-500 hover:text-gray-600 dark:text-whisper-900 dark:hover:text-whisper-500 disabled:hover:text-gray-500 dark:disabled:hover:text-whisper-900 disabled:opacity-50`}
>
{title}
</button>
);
};
+2
View File
@@ -3,6 +3,8 @@ import { ViewType } from '../state';
import { ContentFolder } from './../../models/ContentFolder';
export interface Settings {
wsFolder: string;
staticFolder: string;
folders: ContentFolder[];
initialized: boolean
tags: string[];
@@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const MediaFoldersAtom = atom<string[]>({
key: 'MediaFoldersAtom',
default: []
});
@@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const MediaTotalAtom = atom<number>({
key: 'MediaTotalAtom',
default: 0
});
@@ -0,0 +1,6 @@
import { atom } from 'recoil';
export const SelectedMediaFolderAtom = atom<string | null>({
key: 'SelectedMediaFolderAtom',
default: null
});
+3
View File
@@ -2,7 +2,10 @@ export * from './CategoryAtom';
export * from './DashboardViewAtom';
export * from './FolderAtom';
export * from './GroupingAtom';
export * from './MediaFoldersAtom';
export * from './MediaTotalAtom';
export * from './SearchAtom';
export * from './SelectedMediaFolderAtom';
export * from './SettingsAtom';
export * from './SortingAtom';
export * from './TabAtom';
@@ -0,0 +1,9 @@
import { selector } from 'recoil';
import { MediaFoldersAtom } from '..';
export const MediaFoldersSelector = selector({
key: 'MediaFoldersSelector',
get: ({get}) => {
return get(MediaFoldersAtom);
}
});
@@ -0,0 +1,9 @@
import { selector } from 'recoil';
import { MediaTotalAtom } from '..';
export const MediaTotalSelector = selector({
key: 'MediaTotalSelector',
get: ({get}) => {
return get(MediaTotalAtom);
}
});
@@ -0,0 +1,9 @@
import { selector } from 'recoil';
import { SelectedMediaFolderAtom } from '..';
export const SelectedMediaFolderSelector = selector({
key: 'SelectedMediaFolderSelector',
get: ({get}) => {
return get(SelectedMediaFolderAtom);
}
});
+3
View File
@@ -2,7 +2,10 @@ export * from './CategorySelector';
export * from './DashboardViewSelector';
export * from './FolderSelector';
export * from './GroupingSelector';
export * from './MediaFoldersSelector';
export * from './MediaTotalSelector';
export * from './SearchSelector';
export * from './SelectedMediaFolderSelector';
export * from './SettingsSelector';
export * from './SortingSelector';
export * from './TabSelector';