Fix publish form when editing livestream (#565)
* Update publish form when editing livestream + update to radios for liveststream release time * Fix bug where upload tools may remain visible upon switching upload type, even when no option to upload is available. * move publish source state up, when editing livestream only show scheduling option when source is none. * Reset any set release time if switching to live stream mode. * Update date/time cmpnts to reset any chnages they made on umount. Update schedule date/time cmpnt to clear release time when selecting anytime option. * Additional filtering of internal tags * Default to replay view when editing a liveststream
This commit is contained in:
parent
e94f66a5fe
commit
da06c14e60
8 changed files with 155 additions and 113 deletions
|
@ -29,7 +29,7 @@ type Props = {
|
|||
needsYTAuth: boolean,
|
||||
fetchAccessToken: () => void,
|
||||
accessToken: string,
|
||||
isLivestreamMode: boolean,
|
||||
showSchedulingOptions: boolean,
|
||||
};
|
||||
|
||||
function PublishAdditionalOptions(props: Props) {
|
||||
|
@ -40,7 +40,7 @@ function PublishAdditionalOptions(props: Props) {
|
|||
otherLicenseDescription,
|
||||
licenseUrl,
|
||||
updatePublishForm,
|
||||
isLivestreamMode,
|
||||
showSchedulingOptions,
|
||||
// user,
|
||||
// useLBRYUploader,
|
||||
// needsYTAuth,
|
||||
|
@ -156,7 +156,7 @@ function PublishAdditionalOptions(props: Props) {
|
|||
)} */}
|
||||
{/* @endif */}
|
||||
<div className="section">
|
||||
{!isLivestreamMode && <PublishReleaseDate />}
|
||||
{!showSchedulingOptions && <PublishReleaseDate />}
|
||||
|
||||
<FormField
|
||||
label={__('Language')}
|
||||
|
|
|
@ -19,6 +19,7 @@ import Empty from 'component/common/empty';
|
|||
import moment from 'moment';
|
||||
import classnames from 'classnames';
|
||||
import ReactPaginate from 'react-paginate';
|
||||
import { SOURCE_NONE, SOURCE_SELECT, SOURCE_UPLOAD } from 'constants/publish_sources';
|
||||
|
||||
type Props = {
|
||||
uri: ?string,
|
||||
|
@ -51,6 +52,8 @@ type Props = {
|
|||
channelSignature: { signature?: string, signing_ts?: string },
|
||||
isCheckingLivestreams: boolean,
|
||||
setWaitForFile: (boolean) => void,
|
||||
fileSelectSource: string,
|
||||
changeFileSelectSource: (string) => void,
|
||||
};
|
||||
|
||||
function PublishFile(props: Props) {
|
||||
|
@ -84,12 +87,10 @@ function PublishFile(props: Props) {
|
|||
channelSignature,
|
||||
isCheckingLivestreams,
|
||||
setWaitForFile,
|
||||
fileSelectSource,
|
||||
changeFileSelectSource,
|
||||
} = props;
|
||||
|
||||
const SOURCE_NONE = 'none';
|
||||
const SOURCE_SELECT = 'select';
|
||||
const SOURCE_UPLOAD = 'upload';
|
||||
|
||||
const RECOMMENDED_BITRATE = 6000000;
|
||||
const TV_PUBLISH_SIZE_LIMIT_BYTES = WEB_PUBLISH_SIZE_LIMIT_GB * 1073741824;
|
||||
const TV_PUBLISH_SIZE_LIMIT_GB_STR = String(WEB_PUBLISH_SIZE_LIMIT_GB);
|
||||
|
@ -114,16 +115,13 @@ function PublishFile(props: Props) {
|
|||
const fileSelectorModes = [
|
||||
{ label: __('Upload'), actionName: SOURCE_UPLOAD, icon: ICONS.PUBLISH },
|
||||
{ label: __('Choose Replay'), actionName: SOURCE_SELECT, icon: ICONS.MENU },
|
||||
{ label: __('None'), actionName: SOURCE_NONE },
|
||||
{ label: isLivestreamClaim ? __('Edit / Update') : __('None'), actionName: SOURCE_NONE },
|
||||
];
|
||||
|
||||
const livestreamDataStr = JSON.stringify(livestreamData);
|
||||
const hasLivestreamData = livestreamData && Boolean(livestreamData.length);
|
||||
const showSourceSelector = isLivestreamClaim || (hasLivestreamData && mode === PUBLISH_MODES.FILE);
|
||||
|
||||
const [fileSelectSource, setFileSelectSource] = useState(
|
||||
IS_WEB && showSourceSelector && name ? SOURCE_SELECT : SOURCE_UPLOAD
|
||||
);
|
||||
// const [showFileUpdate, setShowFileUpdate] = useState(false);
|
||||
const [selectedFileIndex, setSelectedFileIndex] = useState(null);
|
||||
const PAGE_SIZE = 4;
|
||||
|
@ -142,15 +140,21 @@ function PublishFile(props: Props) {
|
|||
}
|
||||
}, [currentFileType, mode, isStillEditing, updatePublishForm]);
|
||||
|
||||
// set default file source to select if necessary
|
||||
// Initialize default file source state.
|
||||
useEffect(() => {
|
||||
if (hasLivestreamData && isLivestreamClaim) {
|
||||
setWaitForFile(true);
|
||||
setFileSelectSource(SOURCE_SELECT);
|
||||
} else if (isLivestreamClaim) {
|
||||
setFileSelectSource(SOURCE_NONE);
|
||||
// Editing a livestream
|
||||
if (isLivestreamClaim) {
|
||||
changeFileSelectSource(SOURCE_SELECT);
|
||||
}
|
||||
}, [hasLivestreamData, isLivestreamClaim, setFileSelectSource]);
|
||||
// Publishing a livestream
|
||||
else if (mode === PUBLISH_MODES.LIVESTREAM) {
|
||||
changeFileSelectSource(SOURCE_NONE);
|
||||
} else if (showSourceSelector && name) {
|
||||
changeFileSelectSource(SOURCE_SELECT);
|
||||
} else {
|
||||
changeFileSelectSource(SOURCE_UPLOAD);
|
||||
}
|
||||
}, [mode]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const normalizeUrlForProtocol = (url) => {
|
||||
if (url.startsWith('https://')) {
|
||||
|
@ -328,7 +332,7 @@ function PublishFile(props: Props) {
|
|||
updatePublishForm({ remoteFileUrl: livestreamData[selectedFileIndex].data.fileLocation });
|
||||
}
|
||||
}
|
||||
setFileSelectSource(source);
|
||||
changeFileSelectSource(source);
|
||||
setWaitForFile(source !== SOURCE_NONE);
|
||||
}
|
||||
|
||||
|
@ -437,7 +441,7 @@ function PublishFile(props: Props) {
|
|||
updatePublishForm(publishFormParams);
|
||||
}
|
||||
|
||||
const showFileUpload = mode === PUBLISH_MODES.FILE;
|
||||
const showFileUpload = mode === PUBLISH_MODES.FILE || PUBLISH_MODES.LIVESTREAM;
|
||||
const isPublishPost = mode === PUBLISH_MODES.POST;
|
||||
|
||||
return (
|
||||
|
@ -514,7 +518,7 @@ function PublishFile(props: Props) {
|
|||
</fieldset-section>
|
||||
)}
|
||||
|
||||
{fileSelectSource === SOURCE_UPLOAD && showFileUpload && (
|
||||
{showSourceSelector && fileSelectSource === SOURCE_UPLOAD && showFileUpload && (
|
||||
<>
|
||||
<FileSelector
|
||||
label={__('File')}
|
||||
|
@ -528,86 +532,97 @@ function PublishFile(props: Props) {
|
|||
{getUploadMessage()}
|
||||
</>
|
||||
)}
|
||||
{fileSelectSource === SOURCE_SELECT && showFileUpload && hasLivestreamData && !isCheckingLivestreams && (
|
||||
<>
|
||||
<fieldset-section>
|
||||
<label>{__('Select Replay')}</label>
|
||||
<div className="table__wrapper">
|
||||
<table className="table table--livestream-data">
|
||||
<tbody>
|
||||
{livestreamData.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE).map((item, i) => (
|
||||
<tr
|
||||
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
|
||||
key={item.id}
|
||||
className={classnames('livestream__data-row', {
|
||||
'livestream__data-row--selected': selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i,
|
||||
})}
|
||||
>
|
||||
<td>
|
||||
<FormField
|
||||
type="radio"
|
||||
checked={selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i}
|
||||
label={null}
|
||||
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
|
||||
className="livestream__data-row-radio"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div className="livestream_thumb_container">
|
||||
{item.data.thumbnails.slice(0, 3).map((thumb) => (
|
||||
<img key={thumb} className="livestream___thumb" src={thumb} />
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{`${Math.floor(item.data.fileDuration / 60)} ${
|
||||
Math.floor(item.data.fileDuration / 60) > 1 ? __('minutes') : __('minute')
|
||||
}`}
|
||||
<div className="table__item-label">
|
||||
{`${moment(item.data.uploadedAt).from(moment())}`}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<CopyableText
|
||||
primaryButton
|
||||
copyable={normalizeUrlForProtocol(item.data.fileLocation)}
|
||||
snackMessage={__('Url copied.')}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset-section>
|
||||
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
|
||||
{showSourceSelector &&
|
||||
fileSelectSource === SOURCE_SELECT &&
|
||||
showFileUpload &&
|
||||
hasLivestreamData &&
|
||||
!isCheckingLivestreams && (
|
||||
<>
|
||||
<fieldset-section>
|
||||
<ReactPaginate
|
||||
pageCount={totalPages}
|
||||
pageRangeDisplayed={2}
|
||||
previousLabel="‹"
|
||||
nextLabel="›"
|
||||
activeClassName="pagination__item--selected"
|
||||
pageClassName="pagination__item"
|
||||
previousClassName="pagination__item pagination__item--previous"
|
||||
nextClassName="pagination__item pagination__item--next"
|
||||
breakClassName="pagination__item pagination__item--break"
|
||||
marginPagesDisplayed={2}
|
||||
onPageChange={(e) => handlePaginateReplays(e.selected + 1)}
|
||||
forcePage={currentPage - 1}
|
||||
initialPage={currentPage - 1}
|
||||
containerClassName="pagination"
|
||||
/>
|
||||
<label>{__('Select Replay')}</label>
|
||||
<div className="table__wrapper">
|
||||
<table className="table table--livestream-data">
|
||||
<tbody>
|
||||
{livestreamData
|
||||
.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE)
|
||||
.map((item, i) => (
|
||||
<tr
|
||||
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
|
||||
key={item.id}
|
||||
className={classnames('livestream__data-row', {
|
||||
'livestream__data-row--selected':
|
||||
selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i,
|
||||
})}
|
||||
>
|
||||
<td>
|
||||
<FormField
|
||||
type="radio"
|
||||
checked={selectedFileIndex === (currentPage - 1) * PAGE_SIZE + i}
|
||||
label={null}
|
||||
onClick={() => setSelectedFileIndex((currentPage - 1) * PAGE_SIZE + i)}
|
||||
className="livestream__data-row-radio"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div className="livestream_thumb_container">
|
||||
{item.data.thumbnails.slice(0, 3).map((thumb) => (
|
||||
<img key={thumb} className="livestream___thumb" src={thumb} />
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{`${Math.floor(item.data.fileDuration / 60)} ${
|
||||
Math.floor(item.data.fileDuration / 60) > 1 ? __('minutes') : __('minute')
|
||||
}`}
|
||||
<div className="table__item-label">
|
||||
{`${moment(item.data.uploadedAt).from(moment())}`}
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<CopyableText
|
||||
primaryButton
|
||||
copyable={normalizeUrlForProtocol(item.data.fileLocation)}
|
||||
snackMessage={__('Url copied.')}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</fieldset-section>
|
||||
</fieldset-group>
|
||||
</>
|
||||
)}
|
||||
{fileSelectSource === SOURCE_SELECT && showFileUpload && !hasLivestreamData && !isCheckingLivestreams && (
|
||||
<div className="main--empty empty">
|
||||
<Empty text={__('No replays found.')} />
|
||||
</div>
|
||||
)}
|
||||
{fileSelectSource === SOURCE_SELECT && showFileUpload && isCheckingLivestreams && (
|
||||
<fieldset-group class="fieldset-group--smushed fieldgroup--paginate">
|
||||
<fieldset-section>
|
||||
<ReactPaginate
|
||||
pageCount={totalPages}
|
||||
pageRangeDisplayed={2}
|
||||
previousLabel="‹"
|
||||
nextLabel="›"
|
||||
activeClassName="pagination__item--selected"
|
||||
pageClassName="pagination__item"
|
||||
previousClassName="pagination__item pagination__item--previous"
|
||||
nextClassName="pagination__item pagination__item--next"
|
||||
breakClassName="pagination__item pagination__item--break"
|
||||
marginPagesDisplayed={2}
|
||||
onPageChange={(e) => handlePaginateReplays(e.selected + 1)}
|
||||
forcePage={currentPage - 1}
|
||||
initialPage={currentPage - 1}
|
||||
containerClassName="pagination"
|
||||
/>
|
||||
</fieldset-section>
|
||||
</fieldset-group>
|
||||
</>
|
||||
)}
|
||||
{showSourceSelector &&
|
||||
fileSelectSource === SOURCE_SELECT &&
|
||||
showFileUpload &&
|
||||
!hasLivestreamData &&
|
||||
!isCheckingLivestreams && (
|
||||
<div className="main--empty empty">
|
||||
<Empty text={__('No replays found.')} />
|
||||
</div>
|
||||
)}
|
||||
{showSourceSelector && fileSelectSource === SOURCE_SELECT && showFileUpload && isCheckingLivestreams && (
|
||||
<div className="main--empty empty">
|
||||
<Spinner small />
|
||||
</div>
|
||||
|
|
|
@ -32,6 +32,7 @@ import Spinner from 'component/spinner';
|
|||
import { toHex } from 'util/hex';
|
||||
import { LIVESTREAM_REPLAY_API } from 'constants/livestream';
|
||||
import PublishStreamReleaseDate from 'component/publishStreamReleaseDate';
|
||||
import { SOURCE_NONE } from 'constants/publish_sources';
|
||||
|
||||
// @if TARGET='app'
|
||||
import fs from 'fs';
|
||||
|
@ -173,7 +174,8 @@ function PublishForm(props: Props) {
|
|||
[PUBLISH_MODES.LIVESTREAM]: 'Livestream --[noun, livestream tab button]--',
|
||||
};
|
||||
|
||||
const [mode, setMode] = React.useState(_uploadType || PUBLISH_MODES.FILE);
|
||||
const defaultPublishMode = isLivestreamClaim ? PUBLISH_MODES.LIVESTREAM : PUBLISH_MODES.FILE;
|
||||
const [mode, setMode] = React.useState(_uploadType || defaultPublishMode);
|
||||
const [isCheckingLivestreams, setCheckingLivestreams] = React.useState(false);
|
||||
|
||||
let customSubtitle;
|
||||
|
@ -422,9 +424,8 @@ function PublishForm(props: Props) {
|
|||
|
||||
// set mode based on urlParams 'type'
|
||||
useEffect(() => {
|
||||
// Default to standard file publish if none specified
|
||||
if (!_uploadType) {
|
||||
setMode(PUBLISH_MODES.FILE);
|
||||
setMode(defaultPublishMode);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -448,9 +449,8 @@ function PublishForm(props: Props) {
|
|||
return;
|
||||
}
|
||||
|
||||
// Default to standard file publish
|
||||
setMode(PUBLISH_MODES.FILE);
|
||||
}, [_uploadType, enableLivestream]);
|
||||
setMode(defaultPublishMode);
|
||||
}, [_uploadType, enableLivestream, defaultPublishMode]);
|
||||
|
||||
// if we have a type urlparam, update it? necessary?
|
||||
useEffect(() => {
|
||||
|
@ -560,6 +560,15 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}, [mode, updatePublishForm]);
|
||||
|
||||
// Source Selector State.
|
||||
const [fileSelectSource, setFileSelectSource] = useState();
|
||||
const changeFileSelectSource = (state) => setFileSelectSource(state);
|
||||
|
||||
const [showSchedulingOptions, setShowSchedulingOptions] = useState(false);
|
||||
useEffect(() => {
|
||||
setShowSchedulingOptions(isLivestreamMode && fileSelectSource === SOURCE_NONE);
|
||||
}, [isLivestreamMode, fileSelectSource]);
|
||||
|
||||
if (publishing) {
|
||||
return (
|
||||
<div className="main--empty">
|
||||
|
@ -568,12 +577,15 @@ function PublishForm(props: Props) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Editing claim uri
|
||||
return (
|
||||
<div className="card-stack">
|
||||
<ChannelSelect hideAnon={isLivestreamMode} disabled={disabled} />
|
||||
|
||||
<PublishFile
|
||||
fileSelectSource={fileSelectSource}
|
||||
changeFileSelectSource={changeFileSelectSource}
|
||||
uri={permanentUrl}
|
||||
mode={mode}
|
||||
fileMimeType={fileMimeType}
|
||||
|
@ -610,7 +622,7 @@ function PublishForm(props: Props) {
|
|||
|
||||
{!publishing && (
|
||||
<div className={classnames({ 'card--disabled': formDisabled })}>
|
||||
{isLivestreamMode && <Card className={'card--enable-overflow'} body={<PublishStreamReleaseDate />} />}
|
||||
{showSchedulingOptions && <Card className={'card--enable-overflow'} body={<PublishStreamReleaseDate />} />}
|
||||
|
||||
{mode !== PUBLISH_MODES.POST && <PublishDescription disabled={formDisabled} />}
|
||||
|
||||
|
@ -646,7 +658,7 @@ function PublishForm(props: Props) {
|
|||
<PublishBid disabled={isStillEditing || formDisabled} />
|
||||
{!isLivestreamMode && <PublishPrice disabled={formDisabled} />}
|
||||
|
||||
<PublishAdditionalOptions disabled={formDisabled} isLivestreamMode={isLivestreamMode} />
|
||||
<PublishAdditionalOptions disabled={formDisabled} showSchedulingOptions={showSchedulingOptions} />
|
||||
</div>
|
||||
)}
|
||||
<section>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import Button from 'component/button';
|
||||
import DateTimePicker from 'react-datetime-picker';
|
||||
|
||||
|
@ -86,6 +86,12 @@ const PublishReleaseDate = (props: Props) => {
|
|||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
updatePublishForm({ releaseTimeEdited: undefined });
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="form-field-date-picker">
|
||||
<label>{__('Release date')}</label>
|
||||
|
|
|
@ -59,8 +59,8 @@ const PublishStreamReleaseDate = (props: Props) => {
|
|||
|
||||
<div className={'w-full flex flex-col mt-s md:mt-0 md:h-12 md:items-center md:flex-row'}>
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="rightNow"
|
||||
type="radio"
|
||||
name="anytime"
|
||||
disabled={false}
|
||||
onChange={handleToggle}
|
||||
checked={!publishLater}
|
||||
|
@ -69,8 +69,8 @@ const PublishStreamReleaseDate = (props: Props) => {
|
|||
|
||||
<div className={'md:ml-m mt-s md:mt-0'}>
|
||||
<FormField
|
||||
type="checkbox"
|
||||
name="rightNow"
|
||||
type="radio"
|
||||
name="scheduled_time"
|
||||
disabled={false}
|
||||
onChange={handleToggle}
|
||||
checked={publishLater}
|
||||
|
|
3
ui/constants/publish_sources.js
Normal file
3
ui/constants/publish_sources.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export const SOURCE_NONE = 'none';
|
||||
export const SOURCE_SELECT = 'select';
|
||||
export const SOURCE_UPLOAD = 'upload';
|
|
@ -13,6 +13,7 @@ import ChannelThumbnail from 'component/channelThumbnail';
|
|||
import * as ICONS from 'constants/icons';
|
||||
import Icon from 'component/common/icon';
|
||||
import { NO_FILE } from 'redux/actions/publish';
|
||||
import { INTERNAL_TAGS } from 'constants/tags';
|
||||
|
||||
type Props = {
|
||||
filePath: string | WebFile,
|
||||
|
@ -200,9 +201,11 @@ const ModalPublishPreview = (props: Props) => {
|
|||
<p>{licenseType}</p>
|
||||
);
|
||||
|
||||
const visibleTags = tags.filter((tag) => !INTERNAL_TAGS.includes(tag.name));
|
||||
|
||||
const tagsValue =
|
||||
// Do nothing for onClick(). Setting to 'null' results in "View Tag" action -- we don't want to leave the modal.
|
||||
tags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
|
||||
visibleTags.map((tag) => <Tag key={tag.name} title={tag.name} name={tag.name} type={'flow'} onClick={() => {}} />);
|
||||
|
||||
const depositValue = bid ? <LbcSymbol postfix={`${bid}`} size={14} /> : <p>---</p>;
|
||||
|
||||
|
|
|
@ -333,6 +333,9 @@ export const makeSelectMetadataForUri = (uri: string) =>
|
|||
|
||||
export const makeSelectMetadataItemForUri = (uri: string, key: string) =>
|
||||
createSelector(makeSelectMetadataForUri(uri), (metadata: ChannelMetadata | StreamMetadata) => {
|
||||
if (key === 'tags') {
|
||||
return metadata.tags ? metadata.tags.filter((tag) => !INTERNAL_TAGS.includes(tag)) : [];
|
||||
}
|
||||
return metadata ? metadata[key] : undefined;
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue