Skip to content

Commit

Permalink
Merge pull request #36 from RvanderLaan/toolbar
Browse files Browse the repository at this point in the history
Toolbar
  • Loading branch information
hummingly authored Apr 11, 2019
2 parents 0ce87ff + db29500 commit f737aa3
Show file tree
Hide file tree
Showing 16 changed files with 563 additions and 248 deletions.
48 changes: 12 additions & 36 deletions src/renderer/frontend/App.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,31 @@
import {
Breadcrumbs,
IBreadcrumbProps,
InputGroup,
Button,
} from '@blueprintjs/core';
import { observer } from 'mobx-react-lite';
import React from 'react';

import FileList from './components/FileList';
import Outliner from './components/Outliner';
import { IRootStoreProp, withRootstore } from './contexts/StoreContext';
import Inspector from './components/Inspector';
import Toolbar from './components/Toolbar';
import ErrorBoundary from './components/ErrorBoundary';

interface IAppProps extends IRootStoreProp {}
interface IAppProps extends IRootStoreProp { }

const App = ({ rootStore: { uiStore } }: IAppProps) => {
// Breadcrumbs placeholder
const breadcrumbs: IBreadcrumbProps[] = [
{ icon: 'symbol-square' },
{ icon: 'folder-close', text: 'Cars' },
{ icon: 'folder-close', text: 'Yellow' },
{ icon: 'document', text: 'New' },
];

const themeClass = uiStore.theme === 'DARK' ? 'bp3-dark' : 'bp3-light';

return (
<div id={'layoutContainer'} className={`${themeClass}`}>
<Outliner />

<main>
<div className="header">
<Breadcrumbs items={breadcrumbs} />

{/* This can be replaced with the custom SearchBar component later */}
<InputGroup type="search" leftIcon="search" placeholder="Search" />

<Button
icon="info-sign"
onClick={() => {
uiStore.isInspectorOpen = !uiStore.isInspectorOpen;
}}
/>
</div>
<div id="layoutContainer" className={`${themeClass}`}>
<ErrorBoundary>
<Toolbar />

<br />
<Outliner />

<FileList />
</main>
<main>
<FileList />
</main>

<Inspector />
<Inspector />
</ErrorBoundary>
</div>
);
};
Expand Down
72 changes: 72 additions & 0 deletions src/renderer/frontend/components/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { remote } from 'electron';
import { Button, NonIdealState, ButtonGroup, EditableText } from '@blueprintjs/core';

interface IErrorBoundaryState {
hasError: boolean;
error: string;
}

class ErrorBoundary extends React.Component<{}, IErrorBoundaryState> {
static getDerivedStateFromError(error: any) {
// Update state so the next render will show the fallback UI.
return { hasError: true, error };
}
state = {
hasError: false,
error: '',
};

componentDidCatch(error: any, info: any) {
// TODO: Send error to logging service
const stringifiedError = JSON.stringify(error, Object.getOwnPropertyNames(error), 2);
this.setState({ error: stringifiedError });
}

viewInspector() {
remote.getCurrentWebContents()
.openDevTools();
}

reloadApplication() {
remote.getCurrentWindow()
.reload();
}

render() {
const { hasError, error } = this.state;
if (hasError) {
// You can render any custom fallback UI
return (
<div className="error-boundary">
<NonIdealState
icon={<span>😞</span>}
title="Something went wrong."
description="You can try one of the following options or contact the maintainers"
action={<ButtonGroup>
<Button onClick={this.reloadApplication} icon="refresh" intent="primary">
Reload
</Button>
<Button onClick={this.viewInspector} intent="warning" icon="error">
View in DevTools
</Button>
<Button disabled intent="danger" icon="database">
Clear database
</Button>
</ButtonGroup>}
>
<EditableText
className="bp3-intent-danger bp3-monospace-text message"
value={error.toString()}
isEditing={false}
multiline
/>
</NonIdealState>
</div>
);
}
return this.props.children;
}
}

export default ErrorBoundary;
2 changes: 1 addition & 1 deletion src/renderer/frontend/components/FileInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const SingleFileInfo = observer(({ file }: { file: ClientFile }) => {
);

return (
<section className="fileInfo">
<section id="fileInfo">
{fileInfoList.map(({ key, value }) => [
<div key={`fileInfoKey-${key}`} className="inpectorHeading">
{key}
Expand Down
81 changes: 24 additions & 57 deletions src/renderer/frontend/components/FileList.tsx
Original file line number Diff line number Diff line change
@@ -1,72 +1,39 @@
import { remote } from 'electron';
import fse from 'fs-extra';
import path from 'path';
import React from 'react';

import React, { useCallback } from 'react';
import { observer } from 'mobx-react-lite';
import { Button } from '@blueprintjs/core';

import { withRootstore, IRootStoreProp } from '../contexts/StoreContext';
import FileStore from '../stores/FileStore';

import Gallery from './Gallery';
import FileSelectionHeader from './FileSelectionHeader';
import { Tag, ITagProps } from '@blueprintjs/core';

export interface IFileListProps extends IRootStoreProp {}

const chooseDirectory = async (fileStore: FileStore) => {
const dirs = remote.dialog.showOpenDialog({
properties: ['openDirectory', 'multiSelections'],
});

if (!dirs) {
return;
}

dirs.forEach(async (dir) => {
// Check if directory
// const stats = await fse.lstat(dirs[0]);
const imgExtensions = ['gif', 'png', 'jpg', 'jpeg'];

const filenames = await fse.readdir(dir);
const imgFileNames = filenames.filter((f) =>
imgExtensions.some((ext) =>
f.toLowerCase()
.endsWith(ext)),
);
const FileList = ({ rootStore: { uiStore, fileStore, tagStore } }: IFileListProps) => {

imgFileNames.forEach(async (filename) => {
const joinedPath = path.join(dir, filename);
console.log(joinedPath);
fileStore.addFile(joinedPath);
});
});
};

const FileList = ({ rootStore: { uiStore, fileStore } }: IFileListProps) => {
const removeSelectedFiles = async () => {
await fileStore.removeFilesById(uiStore.fileSelection);
uiStore.fileSelection.clear();
};

const selectionModeOn = uiStore.fileSelection.length > 0;
const handleDeselectTag = useCallback(
(_, props: ITagProps) => {
const clickedTag = tagStore.tagList.find((t) => t.id === props.id);
if (clickedTag) {
uiStore.deselectTag(clickedTag);
}
},
[],
);

return (
<div className="gallery">
{ selectionModeOn && (
<FileSelectionHeader
numSelectedFiles={uiStore.fileSelection.length}
onCancel={() => uiStore.fileSelection.clear()}
onRemove={removeSelectedFiles}
/>
)}

<Button onClick={() => chooseDirectory(fileStore)} icon="folder-open">
Add images to your Visual Library
</Button>

<br />
<br />
<div id="query-overview">
{uiStore.clientTagSelection.map((tag) => (
<Tag
key={tag.id}
id={tag.id}
intent="primary"
onRemove={handleDeselectTag}
>
{tag.name}
</Tag>),
)}
</div>

<Gallery />
</div>
Expand Down
57 changes: 0 additions & 57 deletions src/renderer/frontend/components/FileSelectionHeader.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/renderer/frontend/components/FileTag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ const Multi = observer(({ files }: IFileTagProps) => {

const FileTag = ({ files }: IFileTagProps) => {
return (
<section className="fileTag">
<section id="fileTag">
<div className="inpectorHeading">Tags</div>
{files.length === 1 ? (
<Single file={files[0]} />
Expand Down
71 changes: 71 additions & 0 deletions src/renderer/frontend/components/ImportForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useCallback } from 'react';
import { observer } from 'mobx-react-lite';
import { useContext } from 'react';
import { remote } from 'electron';
import fse from 'fs-extra';
import path from 'path';

import StoreContext from '../contexts/StoreContext';
import { Button } from '@blueprintjs/core';
import FileStore from '../stores/FileStore';

const chooseDirectory = async (fileStore: FileStore) => {
const dirs = remote.dialog.showOpenDialog({
properties: ['openDirectory', 'multiSelections'],
});

if (!dirs) {
return;
}

dirs.forEach(async (dir) => {
// Check if directory
// const stats = await fse.lstat(dirs[0]);
const imgExtensions = ['gif', 'png', 'jpg', 'jpeg'];

const filenames = await fse.readdir(dir);
const imgFileNames = filenames.filter((f) =>
imgExtensions.some((ext) =>
f.toLowerCase()
.endsWith(ext)),
);

imgFileNames.forEach(async (filename) => {
const joinedPath = path.join(dir, filename);
console.log(joinedPath);
fileStore.addFile(joinedPath);
});
});
};

const ImportForm = () => {
// Todo: Add Location entity to DB, so we can have user-picked directories as well
// Todo: Also show sub-directories in tree

const { fileStore } = useContext(StoreContext);

const handleChooseDirectory = useCallback(
() => chooseDirectory(fileStore),
[],
);

return (
<>
<Button fill disabled icon="document-open">
Import images
</Button>

<Button fill onClick={handleChooseDirectory} icon="folder-shared">
Import single directory
</Button>

<Button fill disabled icon="folder-open">
Import nested directories
</Button>

{/* Todo: Show progress bar here */}
</>
);
};

export default observer(ImportForm);
Loading

0 comments on commit f737aa3

Please sign in to comment.