From c47c6f60340e81b5037b3cb7b055567e5b033ca2 Mon Sep 17 00:00:00 2001 From: btzr-io Date: Tue, 28 Jul 2020 21:56:07 -0500 Subject: [PATCH] fix story validation and content loading on web --- ui/component/publishFile/view.jsx | 3 ++ ui/component/publishForm/view.jsx | 16 ++++---- ui/component/publishFormErrors/index.js | 1 - ui/component/publishFormErrors/view.jsx | 18 +-------- ui/component/storyEditor/index.js | 10 +---- ui/component/storyEditor/view.jsx | 50 ++++++++++++++----------- ui/effects/use-fetch-streaming-url.js | 38 +++++++++++++++++++ 7 files changed, 81 insertions(+), 55 deletions(-) create mode 100644 ui/effects/use-fetch-streaming-url.js diff --git a/ui/component/publishFile/view.jsx b/ui/component/publishFile/view.jsx index 3882efae3..cfc9c5232 100644 --- a/ui/component/publishFile/view.jsx +++ b/ui/component/publishFile/view.jsx @@ -18,6 +18,7 @@ type Props = { name: ?string, title: ?string, filePath: string | WebFile, + fileMimeType: ?string, isStillEditing: boolean, balance: number, updatePublishForm: ({}) => void, @@ -43,6 +44,7 @@ function PublishFile(props: Props) { title, balance, filePath, + fileMimeType, isStillEditing, updatePublishForm, disabled, @@ -343,6 +345,7 @@ function PublishFile(props: Props) { label={__('Story content')} uri={uri} disabled={disabled} + fileMimeType={fileMimeType} setPrevFileText={setPrevFileText} setCurrentFileType={setCurrentFileType} /> diff --git a/ui/component/publishForm/view.jsx b/ui/component/publishForm/view.jsx index 4cd321be1..c662a6775 100644 --- a/ui/component/publishForm/view.jsx +++ b/ui/component/publishForm/view.jsx @@ -120,9 +120,12 @@ function PublishForm(props: Props) { const TAGS_LIMIT = 5; const fileFormDisabled = mode === PUBLISH_MODES.FILE && !filePath; - const storyFormDisabled = mode === PUBLISH_MODES.STORY && (!fileText || fileText === ''); - const formDisabled = ((fileFormDisabled || storyFormDisabled) && !editingURI) || publishing; + const emptyStoryError = mode === PUBLISH_MODES.STORY && (!fileText || fileText.trim() === ''); + const formDisabled = (fileFormDisabled && !editingURI) || emptyStoryError || publishing; const isInProgress = filePath || editingURI || name || title; + // Editing content info + const uri = myClaimForUri ? myClaimForUri.permanent_url : undefined; + const fileMimeType = myClaimForUri ? myClaimForUri.value.source.media_type : undefined; // If they are editing, they don't need a new file chosen const formValidLessFile = @@ -131,6 +134,7 @@ function PublishForm(props: Props) { title && bid && !bidError && + !emptyStoryError && !(uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS); const isOverwritingExistingClaim = !editingURI && myClaimForUri; @@ -280,19 +284,16 @@ function PublishForm(props: Props) { // Update mode on editing useEffect(() => { if (autoSwitchMode && editingURI && myClaimForUri) { - const { media_type: mediaType } = myClaimForUri.value.source; // Change publish mode to "story" if editing content type is markdown - if (mediaType === 'text/markdown' && mode !== PUBLISH_MODES.STORY) { + if (fileMimeType === 'text/markdown' && mode !== PUBLISH_MODES.STORY) { setMode(PUBLISH_MODES.STORY); // Prevent forced mode setAutoSwitchMode(false); } } - }, [autoSwitchMode, editingURI, myClaimForUri, mode, setMode, setAutoSwitchMode]); + }, [autoSwitchMode, editingURI, fileMimeType, myClaimForUri, mode, setMode, setAutoSwitchMode]); // Editing claim uri - const uri = myClaimForUri ? myClaimForUri.permanent_url : undefined; - return (
@@ -312,6 +313,7 @@ function PublishForm(props: Props) { ({ bid: makeSelectPublishFormValue('bid')(state), name: makeSelectPublishFormValue('name')(state), title: makeSelectPublishFormValue('title')(state), - fileText: makeSelectPublishFormValue('fileText')(state), bidError: makeSelectPublishFormValue('bidError')(state), editingUri: makeSelectPublishFormValue('editingUri')(state), uploadThumbnailStatus: makeSelectPublishFormValue('uploadThumbnailStatus')(state), diff --git a/ui/component/publishFormErrors/view.jsx b/ui/component/publishFormErrors/view.jsx index f0f601931..1d949910d 100644 --- a/ui/component/publishFormErrors/view.jsx +++ b/ui/component/publishFormErrors/view.jsx @@ -2,35 +2,20 @@ import React from 'react'; import { THUMBNAIL_STATUSES, isNameValid } from 'lbry-redux'; import { INVALID_NAME_ERROR } from 'constants/claim'; -import * as PUBLISH_MODES from 'constants/publish_types'; type Props = { - mode: ?string, title: ?string, name: ?string, bid: ?string, bidError: ?string, editingURI: ?string, filePath: ?string, - fileText: ?string, isStillEditing: boolean, uploadThumbnailStatus: string, }; function PublishFormErrors(props: Props) { - const { - name, - mode, - title, - bid, - bidError, - editingURI, - filePath, - fileText, - isStillEditing, - uploadThumbnailStatus, - } = props; - const emptyStoryError = mode === PUBLISH_MODES.STORY && (!fileText || fileText.trim() === ''); + const { name, title, bid, bidError, editingURI, filePath, isStillEditing, uploadThumbnailStatus } = props; // These are extra help // If there is an error it will be presented as an inline error as well return ( @@ -40,7 +25,6 @@ function PublishFormErrors(props: Props) { {!isNameValid(name, false) && INVALID_NAME_ERROR} {!bid &&
{__('A deposit amount is required')}
} {bidError &&
{__('Please check your deposit amount.')}
} - {emptyStoryError &&
{__("Can't publish an empty story")}
} {uploadThumbnailStatus === THUMBNAIL_STATUSES.IN_PROGRESS && (
{__('Please wait for thumbnail to finish uploading')}
)} diff --git a/ui/component/storyEditor/index.js b/ui/component/storyEditor/index.js index 2d72f50e0..a09a8c695 100644 --- a/ui/component/storyEditor/index.js +++ b/ui/component/storyEditor/index.js @@ -1,18 +1,10 @@ import { connect } from 'react-redux'; -import { - selectIsStillEditing, - makeSelectPublishFormValue, - doUpdatePublishForm, - makeSelectFileInfoForUri, - makeSelectStreamingUrlForUri, -} from 'lbry-redux'; +import { selectIsStillEditing, makeSelectPublishFormValue, doUpdatePublishForm } from 'lbry-redux'; import StoryEditor from './view'; const select = (state, props) => ({ - fileInfo: makeSelectFileInfoForUri(props.uri)(state), filePath: makeSelectPublishFormValue('filePath')(state), fileText: makeSelectPublishFormValue('fileText')(state), - streamingUrl: makeSelectStreamingUrlForUri(props.uri)(state), isStillEditing: selectIsStillEditing(state), }); diff --git a/ui/component/storyEditor/view.jsx b/ui/component/storyEditor/view.jsx index 9437a3e60..0001e4e79 100644 --- a/ui/component/storyEditor/view.jsx +++ b/ui/component/storyEditor/view.jsx @@ -4,15 +4,15 @@ import { SIMPLE_SITE } from 'config'; import { FF_MAX_CHARS_IN_DESCRIPTION } from 'constants/form-field'; import { FormField } from 'component/common/form'; import usePersistedState from 'effects/use-persisted-state'; +import useFetchStreamingUrl from 'effects/use-fetch-streaming-url'; type Props = { uri: ?string, label: ?string, disabled: ?boolean, - fileInfo: FileListItem, filePath: string | WebFile, fileText: ?string, - streamingUrl: ?string, + fileMimeType: ?string, isStillEditing: boolean, setPrevFileText: string => void, updatePublishForm: ({}) => void, @@ -24,32 +24,43 @@ function StoryEditor(props: Props) { uri, label, disabled, - fileInfo, filePath, fileText, - streamingUrl, + fileMimeType, isStillEditing, setPrevFileText, updatePublishForm, setCurrentFileType, } = props; - const [isLoadign, setIsLoadign] = React.useState(false); + const editing = isStillEditing && uri; + + const [ready, setReady] = React.useState(!editing); + const [loading, setLoading] = React.useState(false); const [advancedEditor, setAdvancedEditor] = usePersistedState('publish-form-story-mode', false); + const { streamingUrl } = useFetchStreamingUrl(uri); function toggleMarkdown() { setAdvancedEditor(!advancedEditor); } + // Ready to edit content useEffect(() => { - if (fileText && isLoadign) { - setIsLoadign(false); + if (!ready && !loading && fileText && streamingUrl) { + setReady(true); } - }, [fileText, isLoadign, setIsLoadign]); + }, [ready, loading, fileText, streamingUrl]); + + useEffect(() => { + if (fileText && loading) { + setLoading(false); + } else if (!fileText && loading) { + setLoading(true); + } + }, [fileText, loading, setLoading]); useEffect(() => { function readFileStream(url) { - setIsLoadign(true); return fetch(url).then(res => res.text()); } @@ -67,24 +78,21 @@ function StoryEditor(props: Props) { } } - const isEditingFile = isStillEditing && uri && fileInfo; - - if (isEditingFile) { - const { mime_type: mimeType } = fileInfo; + if (editing) { // Editing same file (previously published) // User can use a different file to replace the content - if (!filePath && !fileText && streamingUrl && mimeType === 'text/markdown') { - setCurrentFileType(mimeType); + if (!ready && !filePath && !fileText && streamingUrl && fileMimeType === 'text/markdown') { + setCurrentFileType(fileMimeType); updateEditorText(streamingUrl); } } }, [ - uri, + ready, + editing, fileText, filePath, - fileInfo, + fileMimeType, streamingUrl, - isStillEditing, setPrevFileText, updatePublishForm, setCurrentFileType, @@ -95,9 +103,9 @@ function StoryEditor(props: Props) { type={!SIMPLE_SITE && advancedEditor ? 'markdown' : 'textarea'} name="content_story" label={label} - placeholder={isLoadign ? __('Loadign...') : __('My content for this story...')} - value={fileText} - disabled={isLoadign || disabled} + placeholder={__('My content for this story...')} + value={ready ? fileText : __('Loading...')} + disabled={!ready || disabled} onChange={value => updatePublishForm({ fileText: advancedEditor ? value : value.target.value })} quickActionLabel={!SIMPLE_SITE && (advancedEditor ? __('Simple Editor') : __('Advanced Editor'))} quickActionHandler={toggleMarkdown} diff --git a/ui/effects/use-fetch-streaming-url.js b/ui/effects/use-fetch-streaming-url.js new file mode 100644 index 000000000..374b8784d --- /dev/null +++ b/ui/effects/use-fetch-streaming-url.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { Lbry } from 'lbry-redux'; +import useIsMounted from 'effects/use-is-mounted'; + +// Fetch streaming url +export default function useFetchStreamingUrl(uri) { + const isMounted = useIsMounted(); + + const [state, setState] = React.useState({ + error: false, + fetching: false, + streamingUrl: null, + }); + + React.useEffect(() => { + async function fetchClaim(claimUri) { + try { + setState({ fetching: true }); + const data = await Lbry.get({ uri: claimUri }); + + if (data && isMounted.current) { + const { streaming_url: streamingUrl } = data; + setState({ fetching: false, streamingUrl }); + } + } catch (error) { + if (isMounted.current) { + setState({ error, fetching: false }); + } + } + } + + if (uri && !state.error && !state.fetching && !state.streamingUrl) { + fetchClaim(uri); + } + }, [uri, state]); + + return state; +}