load story content from streaming url and improve form validation + minor fixes
This commit is contained in:
parent
a5d1746151
commit
2ff1fc024c
6 changed files with 77 additions and 44 deletions
|
@ -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' */}
|
||||||
|
|
|
@ -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={{
|
||||||
|
|
|
@ -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);
|
|
||||||
|
|
|
@ -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>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -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),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
const text = await readFileStream(url);
|
||||||
if (text) {
|
if (text) {
|
||||||
// Store original content
|
// Store original content
|
||||||
setPrevFileText(text);
|
setPrevFileText(text);
|
||||||
// Update text editor form
|
// Update text editor form
|
||||||
updatePublishForm({ fileText: text });
|
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}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in a new issue