feat: additional file types on lbry.tv

Should support markdown, PDF, and anything in the code viewer. Tested that it's working on web and app.
This commit is contained in:
Thomas Zarebczan 2019-11-26 14:08:34 -05:00 committed by Sean Yesmunt
parent 66ccbb468b
commit 722c0b978c
11 changed files with 81 additions and 23 deletions

View file

@ -4,7 +4,6 @@ module.exports = api => {
return {
presets: ['@babel/env', '@babel/react', '@babel/flow'],
plugins: [
'@babel/plugin-syntax-dynamic-import',
'import-glob',
'@babel/plugin-transform-runtime',
['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }],

View file

@ -903,4 +903,4 @@
"Reach out to hello@lbry.com for help, or check out %help_link%.": "Reach out to hello@lbry.com for help, or check out %help_link%.",
"You're not following any tags. Smash that %customize% button!": "You're not following any tags. Smash that %customize% button!",
"customize": "customize"
}
}

View file

@ -16,11 +16,11 @@ import path from 'path';
import fs from 'fs';
import Yrbl from 'component/yrbl';
// @if TARGET='app'
import DocumentViewer from 'component/viewers/documentViewer';
import DocxViewer from 'component/viewers/docxViewer';
import HtmlViewer from 'component/viewers/htmlViewer';
import PdfViewer from 'component/viewers/pdfViewer';
import HtmlViewer from 'component/viewers/htmlViewer';
// @if TARGET='app'
import DocxViewer from 'component/viewers/docxViewer';
import ComicBookViewer from 'component/viewers/comicBookViewer';
import ThreeViewer from 'component/viewers/threeViewer';
// @endif
@ -102,26 +102,33 @@ class FileRender extends React.PureComponent<Props> {
// Add routes to viewer...
};
// Supported contentTypes
const contentTypes = {
'application/pdf': <PdfViewer source={downloadPath || source} />,
'text/html': <HtmlViewer source={downloadPath || source} />,
'text/htm': <HtmlViewer source={downloadPath || source} />,
};
// Supported fileType
const fileTypes = {
// @if TARGET='app'
pdf: <PdfViewer source={downloadPath} />,
docx: <DocxViewer source={downloadPath} />,
html: <HtmlViewer source={downloadPath} />,
htm: <HtmlViewer source={downloadPath} />,
// @endif
// Add routes to viewer...
};
// Check for a valid fileType or mediaType
let viewer = (fileType && fileTypes[fileType]) || mediaTypes[mediaType];
// Check for a valid fileType, mediaType, or contentType
let viewer = (fileType && fileTypes[fileType]) || mediaTypes[mediaType] || contentTypes[contentType];
// Check for Human-readable files
if (!viewer && readableFiles.includes(mediaType)) {
viewer = (
<DocumentViewer
source={{
stream: options => fs.createReadStream(downloadPath, options),
// @if TARGET='app'
file: options => fs.createReadStream(downloadPath, options),
// @endif
stream: source,
fileType,
contentType,
}}

View file

@ -5,6 +5,7 @@ import {
makeSelectThumbnailForUri,
makeSelectStreamingUrlForUri,
makeSelectMediaTypeForUri,
makeSelectContentTypeForUri,
makeSelectUriIsStreamable,
makeSelectTitleForUri,
} from 'lbry-redux';
@ -23,6 +24,7 @@ const select = (state, props) => {
title: makeSelectTitleForUri(uri)(state),
thumbnail: makeSelectThumbnailForUri(uri)(state),
mediaType: makeSelectMediaTypeForUri(uri)(state),
contentType: makeSelectContentTypeForUri(uri)(state),
fileInfo: makeSelectFileInfoForUri(uri)(state),
obscurePreview: makeSelectShouldObscurePreview(uri)(state),
isPlaying: makeSelectIsPlaying(uri)(state),

View file

@ -15,6 +15,7 @@ import { onFullscreenChange } from 'util/full-screen';
type Props = {
mediaType: string,
contentType: string,
isLoading: boolean,
isPlaying: boolean,
fileInfo: FileListItem,
@ -47,6 +48,7 @@ export default function FileViewer(props: Props) {
triggerAnalyticsView,
claimRewards,
mediaType,
contentType,
} = props;
const [playTime, setPlayTime] = useState();
const [fileViewerRect, setFileViewerRect] = usePersistedState('inline-file-viewer:rect');
@ -56,7 +58,9 @@ export default function FileViewer(props: Props) {
});
const inline = pageUri === uri;
const isReadyToPlay = (IS_WEB && isStreamable) || (isStreamable && streamingUrl) || (fileInfo && fileInfo.completed);
const webStreamOnly = contentType === 'application/pdf' || mediaType === 'text';
const isReadyToPlay =
(IS_WEB && (isStreamable || webStreamOnly)) || (isStreamable && streamingUrl) || (fileInfo && fileInfo.completed);
const loadingMessage =
!isStreamable && fileInfo && fileInfo.blobs_completed >= 1 && (!fileInfo.download_path || !fileInfo.written_bytes)
? __("It looks like you deleted or moved this file. We're rebuilding it now. It will only take a few seconds.")

View file

@ -6,6 +6,7 @@ import {
makeSelectThumbnailForUri,
makeSelectStreamingUrlForUri,
makeSelectMediaTypeForUri,
makeSelectContentTypeForUri,
makeSelectUriIsStreamable,
} from 'lbry-redux';
import { makeSelectCostInfoForUri } from 'lbryinc';
@ -16,6 +17,7 @@ import FileViewer from './view';
const select = (state, props) => ({
thumbnail: makeSelectThumbnailForUri(props.uri)(state),
mediaType: makeSelectMediaTypeForUri(props.uri)(state),
contentType: makeSelectContentTypeForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
obscurePreview: makeSelectShouldObscurePreview(props.uri)(state),
isPlaying: makeSelectIsPlaying(props.uri)(state),

View file

@ -14,6 +14,7 @@ const SPACE_BAR_KEYCODE = 32;
type Props = {
play: string => void,
mediaType: string,
contentType: string,
isLoading: boolean,
isPlaying: boolean,
fileInfo: FileListItem,
@ -31,6 +32,7 @@ export default function FileViewer(props: Props) {
const {
play,
mediaType,
contentType,
isPlaying,
fileInfo,
uri,
@ -45,7 +47,8 @@ export default function FileViewer(props: Props) {
const cost = costInfo && costInfo.cost;
const isPlayable = ['audio', 'video'].includes(mediaType);
const fileStatus = fileInfo && fileInfo.status;
const supported = (IS_WEB && isStreamable) || !IS_WEB;
const webStreamOnly = contentType === 'application/pdf' || mediaType === 'text';
const supported = (IS_WEB && (isStreamable || webStreamOnly)) || !IS_WEB;
// Wrap this in useCallback because we need to use it to the keyboard effect
// If we don't a new instance will be created for every render and react will think the dependencies have changed, which will add/remove the listener for every render

View file

@ -1,14 +1,16 @@
// @flow
import React, { Suspense } from 'react';
import React from 'react';
import LoadingScreen from 'component/common/loading-screen';
import MarkdownPreview from 'component/common/markdown-preview';
import CodeViewer from 'component/viewers/codeViewer';
import * as https from 'https';
type Props = {
theme: string,
source: {
stream: string => any,
file: (?string) => any,
stream: string,
fileType: string,
contentType: string,
},
@ -32,9 +34,9 @@ class DocumentViewer extends React.PureComponent<Props, State> {
componentDidMount() {
const { source } = this.props;
if (source && source.stream) {
const stream = source.stream('utf8');
// @if TARGET='app'
if (source && source.file) {
const stream = source.file('utf8');
let data = '';
@ -50,6 +52,32 @@ class DocumentViewer extends React.PureComponent<Props, State> {
this.setState({ error: true, loading: false });
});
}
// @endif
// @if TARGET='web'
if (source && source.stream) {
https.get(
source.stream,
function(response) {
if (response.statusCode === 200) {
let body = '';
let i = 0;
response.on('data', function(chunk) {
i = i + 1;
body += chunk;
});
response.on(
'end',
function() {
this.setState({ content: body, loading: false });
}.bind(this)
);
} else {
this.setState({ error: true, loading: false });
}
}.bind(this)
);
}
// @endif
}
renderDocument() {
@ -58,8 +86,7 @@ class DocumentViewer extends React.PureComponent<Props, State> {
const { source, theme } = this.props;
const { fileType, contentType } = source;
const markdownType = ['md', 'markdown'];
if (markdownType.includes(fileType)) {
if (markdownType.includes(fileType) || contentType === 'text/markdown' || contentType === 'text/md') {
// Render markdown
viewer = <MarkdownPreview content={content} promptLinks />;
} else {

View file

@ -11,7 +11,12 @@ class HtmlViewer extends React.PureComponent<Props> {
const { source } = this.props;
return (
<div className="file-render__viewer" onContextMenu={stopContextMenu}>
{/* @if TARGET='app' */}
<iframe sandbox="" title={__('File preview')} src={`file://${source}`} />
{/* @endif */}
{/* @if TARGET='web' */}
<iframe sandbox="" title={__('File preview')} src={source} />
{/* @endif */}
</div>
);
}

View file

@ -27,21 +27,27 @@ class PdfViewer extends React.PureComponent<Props> {
// @if TARGET='app'
shell.openExternal(path);
// @endif
// @if TARGET='web'
console.error('provide stub for shell.openExternal'); // eslint-disable-line
// @endif
}
render() {
// We used to be able to just render a webview and display the pdf inside the app
// This was disabled on electron@3
// https://github.com/electron/electron/issues/12337
const { source } = this.props;
return (
<div className="file-render__viewer--pdf" onContextMenu={stopContextMenu}>
{/* @if TARGET='app' */}
<p>
{__('PDF opened externally.')} <Button button="link" label={__('Click here')} onClick={this.openFile} />{' '}
{__('to open it again.')}
</p>
{/* @endif */}
{/* @if TARGET='web' */}
<div className="file-render__viewer">
<iframe title={__('File preview')} src={source} />
</div>
{/* @endif */}
</div>
);
}

View file

@ -34,6 +34,9 @@ let baseConfig = {
{
test: /\.jsx?$/,
loader: 'babel-loader',
options: {
plugins: ['@babel/plugin-syntax-dynamic-import'],
},
},
{
test: /\.s?css$/,