File viewer v2 #1826

Merged
btzr-io merged 17 commits from fix-viewer into master 2018-08-06 03:53:57 +02:00
7 changed files with 60 additions and 70 deletions
Showing only changes of commit 2c8f11750a - Show all commits

View file

@ -9,33 +9,36 @@ import DocxViewer from 'component/viewers/docxViewer';
type Props = {
mediaType: string,
source: {
filePath: string,
fileName: string,
fileType: string,
downloadPath: string,
stream: opts => void,
blob: callback => void,
},
currentTheme: string,
};
class FileRender extends React.PureComponent<Props> {
renderViewer() {
const { source, mediaType, currentTheme: theme } = this.props;
const viewerProps = { source, theme };
const { source, mediaType, currentTheme } = this.props;
// Extract relevant data to render file
const { blob, stream, fileName, fileType, contentType, downloadPath } = source;
// Supported mediaTypes
const mediaTypes = {
'3D-file': <ThreeViewer {...viewerProps} />,
document: <DocumentViewer {...viewerProps} />,
'3D-file': <ThreeViewer source={{ fileType, downloadPath }} theme={currentTheme} />,
document: <DocumentViewer source={{ stream, fileType, contentType }} theme={currentTheme} />,
// Add routes to viewer...
};
// Supported fileType
const fileTypes = {
pdf: <PdfViewer {...viewerProps} />,
docx: <DocxViewer {...viewerProps} />,
pdf: <PdfViewer source={downloadPath} />,
docx: <DocxViewer source={downloadPath} />,
// Add routes to viewer...
};
const { fileType } = source;
const viewer = mediaType && source && (fileTypes[fileType] || mediaTypes[mediaType]);
const unsupportedMessage = __("Sorry, looks like we can't preview this file.");
const unsupported = <LoadingScreen status={unsupportedMessage} spinner={false} />;

View file

@ -9,7 +9,7 @@ import FileRender from 'component/fileRender';
import Thumbnail from 'component/common/thumbnail';
import LoadingScreen from 'component/common/loading-screen';
class VideoPlayer extends React.PureComponent {
class MediaPlayer extends React.PureComponent {
static MP3_CONTENT_TYPES = ['audio/mpeg3', 'audio/mpeg'];
static FILE_MEDIA_TYPES = ['e-book', 'comic-book', 'document', '3D-file'];
@ -54,7 +54,7 @@ class VideoPlayer extends React.PureComponent {
};
// use renderAudio override for mp3
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
if (MediaPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
this.renderAudio(container, null, false);
}
// Render custom viewer: FileRender
@ -105,7 +105,7 @@ class VideoPlayer extends React.PureComponent {
if (this.playableType() && !startedPlaying && downloadCompleted) {
const container = this.media.children[0];
if (VideoPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
if (MediaPlayer.MP3_CONTENT_TYPES.indexOf(contentType) > -1) {
this.renderAudio(this.media, true);
} else {
player.render(this.file(), container, {
@ -183,30 +183,30 @@ class VideoPlayer extends React.PureComponent {
// This files are supported using a custom viewer
const { mediaType } = this.props;
return VideoPlayer.FILE_MEDIA_TYPES.indexOf(mediaType) > -1;
return MediaPlayer.FILE_MEDIA_TYPES.indexOf(mediaType) > -1;
}
renderFile() {
// This is what render-media does with unplayable files
const { fileName, downloadPath, contentType, mediaType } = this.props;
toBlobURL(fs.createReadStream(downloadPath), contentType, (err, url) => {
if (err) {
this.setState({ unsupported: true });
return false;
}
// File to render
const fileSource = {
fileName,
contentType,
downloadPath,
fileType: path.extname(fileName).substring(1),
};
// File to render
const fileSource = {
fileName,
contentType,
downloadPath,
filePath: url,
fileType: path.extname(fileName).substring(1),
};
// Update state
this.setState({ fileSource });
});
// Readable stream from file
fileSource.stream = opts => fs.createReadStream(downloadPath, opts);
// Blob url from stream
fileSource.blob = callback =>
toBlobURL(fs.createReadStream(downloadPath), contentType, callback);
// Update state
this.setState({ fileSource });
}
renderAudio(container, autoplay) {
@ -287,5 +287,5 @@ class VideoPlayer extends React.PureComponent {
}
}
export default VideoPlayer;
export default MediaPlayer;
/* eslint-disable */

View file

@ -1,7 +1,6 @@
// @flow
import React from 'react';
import fs from 'fs';
import LoadingScreen from 'component/common/loading-screen';
import CodeViewer from 'component/viewers/codeViewer';
import MarkdownPreview from 'component/common/markdown-preview';
@ -9,9 +8,9 @@ import MarkdownPreview from 'component/common/markdown-preview';
type Props = {
theme: string,
source: {
stream: opts => void,
fileType: string,
filePath: string,
downloadPath: string,
contentType: string,
},
};
@ -27,7 +26,7 @@ class DocumentViewer extends React.PureComponent<Props> {
componentDidMount() {
const { source } = this.props;
const stream = fs.createReadStream(source.downloadPath, 'utf8');
const stream = source.stream('utf8');
let data = '';
@ -40,23 +39,20 @@ class DocumentViewer extends React.PureComponent<Props> {
});
stream.on('error', error => {
this.setState({ error });
this.setState({ error: true, loading: false });
});
}
renderDocument() {
renderDocument(content = null) {
let viewer = null;
const { source, theme } = this.props;
const { fileType, contentType } = source;
const { content, error } = this.state;
const isReady = content && !error;
const markdownType = ['md', 'markdown'];
if (isReady && markdownType.includes(fileType)) {
if (markdownType.includes(fileType)) {
// Render markdown
viewer = <MarkdownPreview content={content} promptLinks />;
} else if (isReady) {
} else {
// Render plain text
viewer = <CodeViewer value={content} contentType={contentType} theme={theme} />;
}
@ -65,15 +61,16 @@ class DocumentViewer extends React.PureComponent<Props> {
}
render() {
const { error, loading } = this.state;
const { error, loading, content } = this.state;
const isReady = content && !error;
const loadingMessage = __('Rendering document.');
const errorMessage = __("Sorry, looks like we can't load the document.");
return (
<div className="file-render__viewer document-viewer">
{loading && !error && <LoadingScreen status={loadingMessage} spinner />}
{error && <LoadingScreen status={errorMessage} spinner={false} />}
{this.renderDocument()}
{error && <LoadingScreen status={errorMessage} spinner={!error} />}
{isReady && this.renderDocument(content)}
</div>
);
}

View file

@ -7,11 +7,7 @@ import LoadingScreen from 'component/common/loading-screen';
import MarkdownPreview from 'component/common/markdown-preview';
type Props = {
source: {
fileType: string,
filePath: string,
downloadPath: string,
},
source: string,
};
class DocxViewer extends React.PureComponent<Props> {
@ -26,6 +22,7 @@ class DocxViewer extends React.PureComponent<Props> {
componentDidMount() {
const { source } = this.props;
// Overwrite element and styles
const options = {
styleMap: [
"p[style-name='Title'] => h1:fresh",
@ -38,15 +35,19 @@ class DocxViewer extends React.PureComponent<Props> {
"p[style-name='Aside Text'] => div.aside > p:fresh",
],
};
// Parse docx to html
mammoth
.convertToHtml({ path: source.downloadPath }, options)
.convertToHtml({ path: source }, options)
.then(result => {
// Remove images and tables
const breakdance = new Breakdance({ omit: ['table', 'img'] });
// Convert html to markdown
const markdown = breakdance.render(result.value);
this.setState({ content: markdown, loading: false });
})
.catch(error => {
this.setState({ error, loading: false });
this.setState({ error: true, loading: false });
})
.done();
}
@ -54,11 +55,12 @@ class DocxViewer extends React.PureComponent<Props> {
render() {
const { content, error, loading } = this.state;
const loadingMessage = __('Rendering document.');
const errorMessage = __("Sorry, looks like we can't load the document.");
return (
<div className="document-viewer file-render__viewer">
{loading && <LoadingScreen status={loadingMessage} spinner />}
{error && <LoadingScreen status={error} spinner={false} />}
{error && <LoadingScreen status={errorMessage} spinner={false} />}
{content && (
<div className="document-viewer__content">
<MarkdownPreview content={content} promptLinks />

View file

@ -1,12 +1,9 @@
// @flow
import React from 'react';
import { stopContextMenu } from 'util/contextMenu';
type Props = {
source: {
fileType: string,
filePath: string,
downloadPath: string,
},
source: string,
};
class PdfViewer extends React.PureComponent<Props> {
@ -15,20 +12,11 @@ class PdfViewer extends React.PureComponent<Props> {
this.viewer = React.createRef();
}
// TODO: Enable context-menu
stopContextMenu = event => {
event.preventDefault();
event.stopPropagation();
};
render() {
const { source } = this.props;
return (
<div className="file-render__viewer" onContextMenu={this.stopContextMenu}>
<webview
ref={this.viewer}
src={`chrome://pdf-viewer/index.html?src=file://${source.downloadPath}`}
/>
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
<webview ref={this.viewer} src={`chrome://pdf-viewer/index.html?src=file://${source}`} />
</div>
);
}

View file

@ -14,7 +14,7 @@ type Props = {
autoRotate: boolean,
source: {
fileType: string,
filePath: string,
downloadPath: string,
},
};

View file

@ -17,13 +17,13 @@ const Loader = (fileType, manager) => {
return fileTypes[fileType] ? fileTypes[fileType]() : null;
};
const ThreeLoader = ({ fileType, filePath }, renderModel, managerEvents) => {
const ThreeLoader = ({ fileType = null, downloadPath = null }, renderModel, managerEvents) => {
if (fileType) {
const manager = Manager(managerEvents);
const loader = Loader(fileType, manager);
if (loader) {
loader.load(filePath, data => {
loader.load(`file://${downloadPath}`, data => {
renderModel(fileType, data);
});
}