load story content from streaming url and improve form validation + minor fixes

This commit is contained in:
btzr-io 2020-07-27 21:19:00 -05:00 committed by Sean Yesmunt
parent a5d1746151
commit 2ff1fc024c
6 changed files with 77 additions and 44 deletions

View file

@ -79,11 +79,11 @@ function PublishFile(props: Props) {
// Reset filePath if publish mode changed // Reset filePath if publish mode changed
useEffect(() => { useEffect(() => {
if (mode === PUBLISH_MODES.STORY) { if (mode === PUBLISH_MODES.STORY) {
if (currentFileType !== 'text/markdown') { if (currentFileType !== 'text/markdown' && !isStillEditing) {
updatePublishForm({ filePath: '', name: '' }); updatePublishForm({ filePath: '', name: '' });
} }
} }
}, [currentFileType, mode, updatePublishForm]); }, [currentFileType, mode, isStillEditing, updatePublishForm]);
useEffect(() => { useEffect(() => {
if (!filePath || filePath === '') { if (!filePath || filePath === '') {
@ -339,7 +339,13 @@ function PublishFile(props: Props) {
<FileSelector disabled={disabled} currentPath={currentFile} onFileChosen={handleFileChange} /> <FileSelector disabled={disabled} currentPath={currentFile} onFileChosen={handleFileChange} />
)} )}
{isPublishStory && ( {isPublishStory && (
<StoryEditor label={__('Story content')} uri={uri} disabled={disabled} setPrevFileText={setPrevFileText} /> <StoryEditor
label={__('Story content')}
uri={uri}
disabled={disabled}
setPrevFileText={setPrevFileText}
setCurrentFileType={setCurrentFileType}
/>
)} )}
{isPublishFile && getMessage()} {isPublishFile && getMessage()}
{/* @if TARGET='app' */} {/* @if TARGET='app' */}

View file

@ -115,7 +115,9 @@ function PublishForm(props: Props) {
} = props; } = props;
const TAGS_LIMIT = 5; const TAGS_LIMIT = 5;
const formDisabled = (!filePath && !editingURI) || publishing; const fileFormDisabled = mode === PUBLISH_MODES.FILE && !filePath;
const storyFormDisabled = mode === PUBLISH_MODES.STORY && (!fileText || fileText === '');
const formDisabled = ((fileFormDisabled || storyFormDisabled) && !editingURI) || publishing;
const isInProgress = filePath || editingURI || name || title; const isInProgress = filePath || editingURI || name || title;
// If they are editing, they don't need a new file chosen // If they are editing, they don't need a new file chosen
@ -126,7 +128,9 @@ function PublishForm(props: Props) {
bid && bid &&
!bidError && !bidError &&
!(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS); !(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS);
const isOverwritingExistingClaim = !editingURI && myClaimForUri; const isOverwritingExistingClaim = !editingURI && myClaimForUri;
const formValid = isOverwritingExistingClaim const formValid = isOverwritingExistingClaim
? false ? false
: editingURI && !filePath : editingURI && !filePath
@ -260,8 +264,8 @@ function PublishForm(props: Props) {
} }
} }
function changePublishMode(name) { function changePublishMode(modeName) {
setMode(name); setMode(modeName);
} }
// Update mode on editing // Update mode on editing
@ -283,16 +287,16 @@ function PublishForm(props: Props) {
return ( return (
<div className="card-stack"> <div className="card-stack">
<div className="button-tab-group"> <div className="button-tab-group">
{MODES.map((name, index) => ( {MODES.map((modeName, index) => (
<Button <Button
key={index} key={index}
icon={name} icon={modeName}
label={name} label={modeName}
button="alt" button="alt"
onClick={() => { onClick={() => {
changePublishMode(name); changePublishMode(modeName);
}} }}
className={classnames('button-toggle', { 'button-toggle--active': mode === name })} className={classnames('button-toggle', { 'button-toggle--active': mode === modeName })}
/> />
))} ))}
</div> </div>
@ -366,7 +370,7 @@ function PublishForm(props: Props) {
</div> </div>
<p className="help"> <p className="help">
{!formDisabled && !formValid ? ( {!formDisabled && !formValid ? (
<PublishFormErrors /> <PublishFormErrors mode={mode} />
) : ( ) : (
<I18nMessage <I18nMessage
tokens={{ tokens={{

View file

@ -3,16 +3,14 @@ import { makeSelectPublishFormValue, selectIsStillEditing } from 'lbry-redux';
import PublishPage from './view'; import PublishPage from './view';
const select = state => ({ const select = state => ({
bid: makeSelectPublishFormValue('bid')(state),
name: makeSelectPublishFormValue('name')(state), name: makeSelectPublishFormValue('name')(state),
title: makeSelectPublishFormValue('title')(state), title: makeSelectPublishFormValue('title')(state),
bid: makeSelectPublishFormValue('bid')(state), fileText: makeSelectPublishFormValue('fileText')(state),
bidError: makeSelectPublishFormValue('bidError')(state), bidError: makeSelectPublishFormValue('bidError')(state),
editingUri: makeSelectPublishFormValue('editingUri')(state), editingUri: makeSelectPublishFormValue('editingUri')(state),
uploadThumbnailStatus: makeSelectPublishFormValue('uploadThumbnailStatus')(state), uploadThumbnailStatus: makeSelectPublishFormValue('uploadThumbnailStatus')(state),
isStillEditing: selectIsStillEditing(state), isStillEditing: selectIsStillEditing(state),
}); });
export default connect( export default connect(select, null)(PublishPage);
select,
null
)(PublishPage);

View file

@ -2,21 +2,35 @@
import React from 'react'; import React from 'react';
import { THUMBNAIL_STATUSES, isNameValid } from 'lbry-redux'; import { THUMBNAIL_STATUSES, isNameValid } from 'lbry-redux';
import { INVALID_NAME_ERROR } from 'constants/claim'; import { INVALID_NAME_ERROR } from 'constants/claim';
import * as PUBLISH_MODES from 'constants/publish_types';
type Props = { type Props = {
mode: ?string,
title: ?string, title: ?string,
name: ?string, name: ?string,
bid: ?string, bid: ?string,
bidError: ?string, bidError: ?string,
editingURI: ?string, editingURI: ?string,
filePath: ?string, filePath: ?string,
fileText: ?string,
isStillEditing: boolean, isStillEditing: boolean,
uploadThumbnailStatus: string, uploadThumbnailStatus: string,
}; };
function PublishFormErrors(props: Props) { function PublishFormErrors(props: Props) {
const { name, title, bid, bidError, editingURI, filePath, isStillEditing, uploadThumbnailStatus } = props; const {
name,
mode,
title,
bid,
bidError,
editingURI,
filePath,
fileText,
isStillEditing,
uploadThumbnailStatus,
} = props;
const emptyStoryError = mode === PUBLISH_MODES.STORY && (!fileText || fileText.trim() === '');
// These are extra help // These are extra help
// If there is an error it will be presented as an inline error as well // If there is an error it will be presented as an inline error as well
return ( return (
@ -26,6 +40,7 @@ function PublishFormErrors(props: Props) {
{!isNameValid(name, false) && INVALID_NAME_ERROR} {!isNameValid(name, false) && INVALID_NAME_ERROR}
{!bid && <div>{__('A deposit amount is required')}</div>} {!bid && <div>{__('A deposit amount is required')}</div>}
{bidError && <div>{__('Please check your deposit amount.')}</div>} {bidError && <div>{__('Please check your deposit amount.')}</div>}
{emptyStoryError && <div>{__("Can't publish an empty story")}</div>}
{uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && ( {uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
<div>{__('Please wait for thumbnail to finish uploading')}</div> <div>{__('Please wait for thumbnail to finish uploading')}</div>
)} )}

View file

@ -4,6 +4,7 @@ import {
makeSelectPublishFormValue, makeSelectPublishFormValue,
doUpdatePublishForm, doUpdatePublishForm,
makeSelectFileInfoForUri, makeSelectFileInfoForUri,
makeSelectStreamingUrlForUri,
} from 'lbry-redux'; } from 'lbry-redux';
import StoryEditor from './view'; import StoryEditor from './view';
@ -11,6 +12,7 @@ const select = (state, props) => ({
fileInfo: makeSelectFileInfoForUri(props.uri)(state), fileInfo: makeSelectFileInfoForUri(props.uri)(state),
filePath: makeSelectPublishFormValue('filePath')(state), filePath: makeSelectPublishFormValue('filePath')(state),
fileText: makeSelectPublishFormValue('fileText')(state), fileText: makeSelectPublishFormValue('fileText')(state),
streamingUrl: makeSelectStreamingUrlForUri(props.uri)(state),
isStillEditing: selectIsStillEditing(state), isStillEditing: selectIsStillEditing(state),
}); });

View file

@ -1,5 +1,4 @@
// @flow // @flow
import fs from 'fs';
import React, { useEffect } from 'react'; import React, { useEffect } from 'react';
import { SIMPLE_SITE } from 'config'; import { SIMPLE_SITE } from 'config';
import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field'; import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field';
@ -13,9 +12,11 @@ type Props = {
fileInfo: FileListItem, fileInfo: FileListItem,
filePath: string | WebFile, filePath: string | WebFile,
fileText: ?string, fileText: ?string,
streamingUrl: ?string,
isStillEditing: boolean, isStillEditing: boolean,
setPrevFileText: string => void, setPrevFileText: string => void,
updatePublishForm: ({}) => void, updatePublishForm: ({}) => void,
setCurrentFileType: string => void,
}; };
function StoryEditor(props: Props) { function StoryEditor(props: Props) {
@ -26,11 +27,14 @@ function StoryEditor(props: Props) {
fileInfo, fileInfo,
filePath, filePath,
fileText, fileText,
streamingUrl,
isStillEditing, isStillEditing,
setPrevFileText, setPrevFileText,
updatePublishForm, updatePublishForm,
setCurrentFileType,
} = props; } = props;
const [isLoadign, setIsLoadign] = React.useState(false);
const [advancedEditor, setAdvancedEditor] = usePersistedState('publish-form-story-mode', false); const [advancedEditor, setAdvancedEditor] = usePersistedState('publish-form-story-mode', false);
function toggleMarkdown() { function toggleMarkdown() {
@ -38,50 +42,54 @@ function StoryEditor(props: Props) {
} }
useEffect(() => { useEffect(() => {
// @if TARGET='app' if (fileText && isLoadign) {
function readFile(path) { setIsLoadign(false);
return new Promise((resolve, reject) => { }
fs.readFile(path, 'utf8', (error, data) => { }, [fileText, isLoadign, setIsLoadign]);
error ? reject(error) : resolve(data);
}); useEffect(() => {
}); function readFileStream(url) {
setIsLoadign(true);
return fetch(url).then(res => res.text());
} }
async function updateEditorText(path) { async function updateEditorText(url) {
const text = await readFile(path); try {
if (text) { const text = await readFileStream(url);
// Store original content if (text) {
setPrevFileText(text); // Store original content
// Update text editor form setPrevFileText(text);
updatePublishForm({ fileText: text }); // Update text editor form
updatePublishForm({ fileText: text });
}
} catch (error) {
// Handle error..
} }
} }
const isEditingFile = isStillEditing && uri && fileInfo; const isEditingFile = isStillEditing && uri && fileInfo;
if (isEditingFile) { if (isEditingFile) {
const { mime_type: mimeType, download_path: downloadPath } = fileInfo; const { mime_type: mimeType } = fileInfo;
// Editing same file (previously published) // Editing same file (previously published)
// User can use a different file to replace the content // User can use a different file to replace the content
if (!filePath && mimeType === 'text/markdown') { if (!filePath && streamingUrl && mimeType === 'text/markdown') {
updateEditorText(downloadPath); setCurrentFileType(mimeType);
updateEditorText(streamingUrl);
} }
} }
}, [uri, isStillEditing, filePath, fileInfo, setPrevFileText, updatePublishForm, streamingUrl, setCurrentFileType]);
// @endif
}, [uri, isStillEditing, filePath, fileInfo, setPrevFileText, updatePublishForm]);
return ( return (
<FormField <FormField
type={!SIMPLE_SITE && advancedEditor ? 'markdown' : 'textarea'} type={!SIMPLE_SITE && advancedEditor ? 'markdown' : 'textarea'}
name="content_story" name="content_story"
label={label} label={label}
placeholder={__('My content for this story...')} placeholder={isLoadign ? __('Loadign...') : __('My content for this story...')}
value={fileText} value={fileText}
disabled={disabled} disabled={isLoadign || disabled}
onChange={value => updatePublishForm({ fileText: advancedEditor ? value : value.target.value })} onChange={value => updatePublishForm({ fileText: advancedEditor ? value : value.target.value })}
quickActionLabel={advancedEditor ? __('Simple Editor') : __('Advanced Editor')} quickActionLabel={!SIMPLE_SITE && (advancedEditor ? __('Simple Editor') : __('Advanced Editor'))}
quickActionHandler={toggleMarkdown} quickActionHandler={toggleMarkdown}
textAreaMaxLength={FF_MAX_CHARS_IN_DESCRIPTION} textAreaMaxLength={FF_MAX_CHARS_IN_DESCRIPTION}
/> />