tom rebase attempt #5881
13 changed files with 599 additions and 120 deletions
22
flow-typed/livestream.js
vendored
Normal file
22
flow-typed/livestream.js
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// @flow
|
||||
|
||||
declare type LivestreamReplayItem = {
|
||||
data: {
|
||||
claimId: string,
|
||||
deleted: boolean,
|
||||
deletedAt: ?string,
|
||||
ffprobe: any,
|
||||
fileDuration: number, // decimal? float? string?
|
||||
fileType: string,
|
||||
fileLocation: string,
|
||||
fileSize: number,
|
||||
key: string,
|
||||
published: boolean,
|
||||
publishedAt: ?string,
|
||||
service: string,
|
||||
thumbnails: Array<string>,
|
||||
uploadedAt: string, // Date?
|
||||
},
|
||||
id: string,
|
||||
}
|
||||
declare type LivestreamReplayData = Array<LivestreamReplayItem>;
|
11
flow-typed/subscription.js
vendored
11
flow-typed/subscription.js
vendored
|
@ -1,15 +1,4 @@
|
|||
// @flow
|
||||
import * as ACTIONS from 'constants/action_types';
|
||||
import {
|
||||
DOWNLOADED,
|
||||
DOWNLOADING,
|
||||
NOTIFY_ONLY,
|
||||
VIEW_ALL,
|
||||
VIEW_LATEST_FIRST,
|
||||
SUGGESTED_TOP_BID,
|
||||
SUGGESTED_TOP_SUBSCRIBED,
|
||||
SUGGESTED_FEATURED,
|
||||
} from 'constants/subscriptions';
|
||||
|
||||
declare type Subscription = {
|
||||
channelName: string, // @CryptoCandor,
|
||||
|
|
|
@ -1777,6 +1777,11 @@
|
|||
"Learn more and sign petition": "Learn more and sign petition",
|
||||
"Publishing...": "Publishing...",
|
||||
"Collection": "Collection",
|
||||
"Upload that unlabeled video you found behind the TV in 1991": "Upload that unlabeled video you found behind the TV in 1991",
|
||||
"Select Replay": "Select Replay",
|
||||
"Craft an epic post clearly explaining... whatever.": "Craft an epic post clearly explaining... whatever.",
|
||||
"waiting": "waiting",
|
||||
"%viewer_count% currently %viewer_state%": "%viewer_count% currently %viewer_state%",
|
||||
"More from %claim_name%": "More from %claim_name%",
|
||||
"--end--": "--end--"
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ import { SIMPLE_SITE, SITE_NAME, ENABLE_FILE_REACTIONS } from 'config';
|
|||
import * as PAGES from 'constants/pages';
|
||||
import * as MODALS from 'constants/modal_types';
|
||||
import * as ICONS from 'constants/icons';
|
||||
import * as PUBLISH_MODES from 'constants/publish_types';
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import FileDownloadLink from 'component/fileDownloadLink';
|
||||
|
@ -114,25 +113,13 @@ function FileActions(props: Props) {
|
|||
<Button
|
||||
className="button--file-action"
|
||||
icon={ICONS.EDIT}
|
||||
label={__('Edit')}
|
||||
navigate={`/$/${PAGES.UPLOAD}${isLivestreamClaim ? `?type=${PUBLISH_MODES.LIVESTREAM}` : ''}`}
|
||||
label={isLivestreamClaim ? __('Update') : __('Edit')}
|
||||
navigate={`/$/${PAGES.UPLOAD}`}
|
||||
onClick={() => {
|
||||
prepareEdit(claim, editUri, fileInfo);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{claimIsMine && isLivestreamClaim && (
|
||||
<Button
|
||||
className="button--file-action"
|
||||
icon={ICONS.PUBLISH}
|
||||
label={__('Publish Replay')}
|
||||
navigate={`/$/${PAGES.UPLOAD}?type=${PUBLISH_MODES.FILE}`}
|
||||
onClick={() => {
|
||||
prepareEdit(claim, editUri, fileInfo);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showDelete && (
|
||||
<Button
|
||||
title={__('Remove from your library')}
|
||||
|
|
|
@ -5,15 +5,17 @@ import {
|
|||
makeSelectPublishFormValue,
|
||||
doUpdatePublishForm,
|
||||
doClearPublish,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
} from 'lbry-redux';
|
||||
import { doToast } from 'redux/actions/notifications';
|
||||
import { selectFfmpegStatus } from 'redux/selectors/settings';
|
||||
import PublishPage from './view';
|
||||
|
||||
const select = state => ({
|
||||
const select = (state, props) => ({
|
||||
name: makeSelectPublishFormValue('name')(state),
|
||||
title: makeSelectPublishFormValue('title')(state),
|
||||
filePath: makeSelectPublishFormValue('filePath')(state),
|
||||
remoteUrl: makeSelectPublishFormValue('remoteFileUrl')(state),
|
||||
optimize: makeSelectPublishFormValue('optimize')(state),
|
||||
isStillEditing: selectIsStillEditing(state),
|
||||
balance: selectBalance(state),
|
||||
|
@ -22,12 +24,13 @@ const select = state => ({
|
|||
size: makeSelectPublishFormValue('fileSize')(state),
|
||||
duration: makeSelectPublishFormValue('fileDur')(state),
|
||||
isVid: makeSelectPublishFormValue('fileVid')(state),
|
||||
isLivestreamClaim: makeSelectClaimIsStreamPlaceholder(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
const perform = (dispatch) => ({
|
||||
clearPublish: () => dispatch(doClearPublish()),
|
||||
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
|
||||
showToast: message => dispatch(doToast({ message, isError: true })),
|
||||
updatePublishForm: (value) => dispatch(doUpdatePublishForm(value)),
|
||||
showToast: (message) => dispatch(doToast({ message, isError: true })),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(PublishPage);
|
||||
|
|
|
@ -8,12 +8,16 @@ import PostEditor from 'component/postEditor';
|
|||
import FileSelector from 'component/common/file-selector';
|
||||
import Button from 'component/button';
|
||||
import Card from 'component/common/card';
|
||||
import { FormField } from 'component/common/form';
|
||||
import { Form, FormField } from 'component/common/form';
|
||||
import Spinner from 'component/spinner';
|
||||
import I18nMessage from 'component/i18nMessage';
|
||||
import usePersistedState from 'effects/use-persisted-state';
|
||||
import * as PUBLISH_MODES from 'constants/publish_types';
|
||||
import PublishName from 'component/publishName';
|
||||
import CopyableText from 'component/copyableText';
|
||||
import moment from 'moment';
|
||||
import classnames from 'classnames';
|
||||
import ReactPaginate from 'react-paginate';
|
||||
|
||||
type Props = {
|
||||
uri: ?string,
|
||||
|
@ -35,9 +39,15 @@ type Props = {
|
|||
size: number,
|
||||
duration: number,
|
||||
isVid: boolean,
|
||||
subtitle: string,
|
||||
setPublishMode: (string) => void,
|
||||
setPrevFileText: (string) => void,
|
||||
header: Node,
|
||||
livestreamData: LivestreamReplayData,
|
||||
isLivestreamClaim: boolean,
|
||||
remoteUrl?: string,
|
||||
checkLivestreams: () => void,
|
||||
isCheckingLivestreams: boolean,
|
||||
};
|
||||
|
||||
function PublishFile(props: Props) {
|
||||
|
@ -63,29 +73,58 @@ function PublishFile(props: Props) {
|
|||
setPublishMode,
|
||||
setPrevFileText,
|
||||
header,
|
||||
livestreamData,
|
||||
isLivestreamClaim,
|
||||
subtitle,
|
||||
remoteUrl,
|
||||
checkLivestreams,
|
||||
isCheckingLivestreams,
|
||||
} = props;
|
||||
|
||||
const SOURCE_NONE = 'none';
|
||||
const SOURCE_SELECT = 'select';
|
||||
const SOURCE_UPLOAD = 'upload';
|
||||
|
||||
const RECOMMENDED_BITRATE = 6000000;
|
||||
const TV_PUBLISH_SIZE_LIMIT: number = 4294967296;
|
||||
const TV_PUBLISH_SIZE_LIMIT_STR_GB = '4';
|
||||
const PAGE_SIZE = 4;
|
||||
|
||||
const PROCESSING_MB_PER_SECOND = 0.5;
|
||||
const MINUTES_THRESHOLD = 30;
|
||||
const HOURS_THRESHOLD = MINUTES_THRESHOLD * 60;
|
||||
const MARKDOWN_FILE_EXTENSIONS = ['txt', 'md', 'markdown'];
|
||||
const sizeInMB = Number(size) / 1000000;
|
||||
const secondsToProcess = sizeInMB / PROCESSING_MB_PER_SECOND;
|
||||
|
||||
const fileSelectorModes = [
|
||||
{ label: __('Select Replay'), actionName: SOURCE_SELECT, icon: ICONS.MENU },
|
||||
{ label: __('Upload'), actionName: SOURCE_UPLOAD, icon: ICONS.PUBLISH },
|
||||
{ label: __('Not Yet'), actionName: SOURCE_NONE, icon: ICONS.REMOVE },
|
||||
];
|
||||
|
||||
const hasLivestreamData = livestreamData && Boolean(livestreamData.length);
|
||||
const showSourceSelector = isLivestreamClaim && hasLivestreamData;
|
||||
|
||||
const ffmpegAvail = ffmpegStatus.available;
|
||||
const [oversized, setOversized] = useState(false);
|
||||
const [currentFile, setCurrentFile] = useState(null);
|
||||
const [currentFileType, setCurrentFileType] = useState(null);
|
||||
const [optimizeAvail, setOptimizeAvail] = useState(false);
|
||||
const [userOptimize, setUserOptimize] = usePersistedState('publish-file-user-optimize', false);
|
||||
const [fileSelectSource, setFileSelectSource] = useState(
|
||||
IS_WEB && showSourceSelector ? SOURCE_SELECT : SOURCE_UPLOAD
|
||||
);
|
||||
// const [showFileUpdate, setShowFileUpdate] = useState(false);
|
||||
const [selectedFileIndex, setSelectedFileIndex] = useState(null);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const totalPages =
|
||||
hasLivestreamData && livestreamData.length > PAGE_SIZE ? Math.ceil(livestreamData.length / PAGE_SIZE) : 1;
|
||||
|
||||
const RECOMMENDED_BITRATE = 6000000;
|
||||
const TV_PUBLISH_SIZE_LIMIT: number = 4294967296;
|
||||
const TV_PUBLISH_SIZE_LIMIT_STR_GB = '4';
|
||||
const UPLOAD_SIZE_MESSAGE = __(
|
||||
'%SITE_NAME% uploads are limited to %limit% GB. Download the app for unrestricted publishing.',
|
||||
{ SITE_NAME, limit: TV_PUBLISH_SIZE_LIMIT_STR_GB }
|
||||
);
|
||||
const PROCESSING_MB_PER_SECOND = 0.5;
|
||||
const MINUTES_THRESHOLD = 30;
|
||||
const HOURS_THRESHOLD = MINUTES_THRESHOLD * 60;
|
||||
const MARKDOWN_FILE_EXTENSIONS = ['txt', 'md', 'markdown'];
|
||||
|
||||
const sizeInMB = Number(size) / 1000000;
|
||||
const secondsToProcess = sizeInMB / PROCESSING_MB_PER_SECOND;
|
||||
|
||||
// Reset filePath if publish mode changed
|
||||
useEffect(() => {
|
||||
|
@ -93,9 +132,40 @@ function PublishFile(props: Props) {
|
|||
if (currentFileType !== 'text/markdown' && !isStillEditing) {
|
||||
updatePublishForm({ filePath: '' });
|
||||
}
|
||||
} else if (mode === PUBLISH_MODES.LIVESTREAM) {
|
||||
updatePublishForm({ filePath: '' });
|
||||
}
|
||||
}, [currentFileType, mode, isStillEditing, updatePublishForm]);
|
||||
|
||||
// set default file source to select if necessary
|
||||
useEffect(() => {
|
||||
if (hasLivestreamData && isLivestreamClaim) {
|
||||
setFileSelectSource(SOURCE_SELECT);
|
||||
} else if (isLivestreamClaim) {
|
||||
setFileSelectSource(SOURCE_NONE);
|
||||
}
|
||||
}, [hasLivestreamData, isLivestreamClaim, setFileSelectSource]);
|
||||
|
||||
const normalizeUrlForProtocol = (url) => {
|
||||
if (url.startsWith('https://')) {
|
||||
return url;
|
||||
} else {
|
||||
if (url.startsWith('http://')) {
|
||||
return url;
|
||||
} else {
|
||||
return `https://${url}`;
|
||||
}
|
||||
}
|
||||
};
|
||||
// update remoteUrl when replay selected
|
||||
useEffect(() => {
|
||||
if (selectedFileIndex !== null) {
|
||||
updatePublishForm({
|
||||
remoteFileUrl: normalizeUrlForProtocol(livestreamData[selectedFileIndex].data.fileLocation),
|
||||
});
|
||||
}
|
||||
}, [selectedFileIndex, updatePublishForm]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!filePath || filePath === '') {
|
||||
setCurrentFile('');
|
||||
|
@ -121,6 +191,10 @@ function PublishFile(props: Props) {
|
|||
updatePublishForm({ fileDur: duration, fileSize: size, fileVid: isvid });
|
||||
}
|
||||
|
||||
function handlePaginateReplays(page) {
|
||||
setCurrentPage(page);
|
||||
}
|
||||
|
||||
function getBitrate(size, duration) {
|
||||
const s = Number(size);
|
||||
const d = Number(duration);
|
||||
|
@ -154,7 +228,14 @@ function PublishFile(props: Props) {
|
|||
}
|
||||
}
|
||||
|
||||
function getMessage() {
|
||||
function getSourceMessage() {
|
||||
if (remoteUrl || filePath) {
|
||||
return __('Replay selected for update');
|
||||
} else {
|
||||
return __('No replay selected');
|
||||
}
|
||||
}
|
||||
function getUploadMessage() {
|
||||
// @if TARGET='web'
|
||||
if (oversized) {
|
||||
return (
|
||||
|
@ -186,6 +267,11 @@ function PublishFile(props: Props) {
|
|||
}
|
||||
|
||||
if (!!isStillEditing && name) {
|
||||
if (isLivestreamClaim) {
|
||||
return (
|
||||
<p className="help">{__('You can upload your own recording or select a replay when your stream is over')}</p>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<p className="help">
|
||||
{__("If you don't choose a file, the file from your existing claim %name% will be used", { name: name })}
|
||||
|
@ -225,6 +311,26 @@ function PublishFile(props: Props) {
|
|||
return newName.replace(INVALID_URI_CHARS, '-');
|
||||
}
|
||||
|
||||
function handleFileSource(source) {
|
||||
if (source === SOURCE_NONE) {
|
||||
// clear files and remotes...
|
||||
// https://github.com/lbryio/lbry-desktop/issues/5855
|
||||
// publish is trying to use one field to share html file blob and string and such
|
||||
// $FlowFixMe
|
||||
handleFileChange(false);
|
||||
updatePublishForm({ remoteFileUrl: undefined });
|
||||
} else if (source === SOURCE_UPLOAD) {
|
||||
updatePublishForm({ remoteFileUrl: undefined });
|
||||
} else if (source === SOURCE_SELECT) {
|
||||
// $FlowFixMe
|
||||
handleFileChange(false);
|
||||
if (selectedFileIndex !== null) {
|
||||
updatePublishForm({ remoteFileUrl: livestreamData[selectedFileIndex].data.fileLocation });
|
||||
}
|
||||
}
|
||||
setFileSelectSource(source);
|
||||
}
|
||||
|
||||
function handleTitleChange(event) {
|
||||
const title = event.target.value;
|
||||
// Update title
|
||||
|
@ -247,7 +353,11 @@ function PublishFile(props: Props) {
|
|||
|
||||
// select file, start to select a new one, then cancel
|
||||
if (!file) {
|
||||
updatePublishForm({ filePath: '', name: '' });
|
||||
if (isStillEditing) {
|
||||
updatePublishForm({ filePath: '' });
|
||||
} else {
|
||||
updatePublishForm({ filePath: '', name: '' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -326,7 +436,7 @@ function PublishFile(props: Props) {
|
|||
updatePublishForm(publishFormParams);
|
||||
}
|
||||
|
||||
const isPublishFile = mode === PUBLISH_MODES.FILE;
|
||||
const showFileUpload = mode === PUBLISH_MODES.FILE || (mode === PUBLISH_MODES.LIVESTREAM && hasLivestreamData);
|
||||
const isPublishPost = mode === PUBLISH_MODES.POST;
|
||||
|
||||
return (
|
||||
|
@ -334,7 +444,7 @@ function PublishFile(props: Props) {
|
|||
className={disabled || balance === 0 ? 'card--disabled' : ''}
|
||||
title={
|
||||
<div>
|
||||
{header}
|
||||
{header} {/* display mode buttons from parent */}
|
||||
{publishing && <Spinner type={'small'} />}
|
||||
{inProgress && (
|
||||
<div>
|
||||
|
@ -343,10 +453,10 @@ function PublishFile(props: Props) {
|
|||
)}
|
||||
</div>
|
||||
}
|
||||
subtitle={isStillEditing && __('You are currently editing your upload.')}
|
||||
subtitle={subtitle || (isStillEditing && __('You are currently editing your upload.'))}
|
||||
actions={
|
||||
<React.Fragment>
|
||||
<PublishName />
|
||||
<PublishName uri={uri} />
|
||||
<FormField
|
||||
type="text"
|
||||
name="content_title"
|
||||
|
@ -356,28 +466,154 @@ function PublishFile(props: Props) {
|
|||
value={title}
|
||||
onChange={handleTitleChange}
|
||||
/>
|
||||
{isPublishFile && (
|
||||
{/* Decide whether to show file upload or replay selector */}
|
||||
{/* @if TARGET='web' */}
|
||||
<>
|
||||
{showSourceSelector && (
|
||||
<fieldset-section>
|
||||
<div className="section__actions--between">
|
||||
<div>
|
||||
<label>{__('Add replay video')}</label>
|
||||
<div>
|
||||
{fileSelectorModes.map((fmode) => (
|
||||
<Button
|
||||
key={fmode.label}
|
||||
icon={fmode.icon}
|
||||
iconSize={18}
|
||||
label={fmode.label}
|
||||
button="alt"
|
||||
onClick={() => {
|
||||
// $FlowFixMe
|
||||
handleFileSource(fmode.actionName);
|
||||
}}
|
||||
className={classnames('button-toggle', {
|
||||
'button-toggle--active': fileSelectSource === fmode.actionName,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="help--inline">{getSourceMessage()}</div>
|
||||
</div>
|
||||
<Button
|
||||
button="secondary"
|
||||
label={__('Check for Replays')}
|
||||
disabled={isCheckingLivestreams}
|
||||
icon={ICONS.REFRESH}
|
||||
onClick={() => checkLivestreams()}
|
||||
/>
|
||||
</div>
|
||||
</fieldset-section>
|
||||
)}
|
||||
|
||||
{fileSelectSource === SOURCE_UPLOAD && showFileUpload && (
|
||||
<>
|
||||
<FileSelector
|
||||
label={__('Video file')}
|
||||
disabled={disabled}
|
||||
currentPath={currentFile}
|
||||
onFileChosen={handleFileChange}
|
||||
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
|
||||
accept="video/mp4,video/x-m4v,video/*"
|
||||
placeholder={__('Select video file to upload')}
|
||||
/>
|
||||
{getUploadMessage()}
|
||||
</>
|
||||
)}
|
||||
{fileSelectSource === SOURCE_SELECT && showFileUpload && (
|
||||
<>
|
||||
<fieldset-section>
|
||||
<label>{__('Select Replay')}</label>
|
||||
<div className="table__wrapper">
|
||||
<table className="table table--livestream-data">
|
||||
<tbody>
|
||||
{livestreamData &&
|
||||
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>
|
||||
<Form style={totalPages <= 1 ? { display: 'none' } : null} onSubmit={handlePaginateReplays}>
|
||||
<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>
|
||||
</Form>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
{/* @endif */}
|
||||
{/* @if TARGET='app' */}
|
||||
{showFileUpload && (
|
||||
<FileSelector
|
||||
label={__('File')}
|
||||
label={__('Video file')}
|
||||
disabled={disabled}
|
||||
currentPath={currentFile}
|
||||
onFileChosen={handleFileChange}
|
||||
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
|
||||
accept="video/mp4,video/x-m4v,video/*"
|
||||
placeholder={__('Select video file to upload')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isPublishPost && (
|
||||
<PostEditor
|
||||
label={__('Post --[noun, markdown post tab button]--')}
|
||||
uri={uri}
|
||||
disabled={disabled}
|
||||
fileMimeType={fileMimeType}
|
||||
setPrevFileText={setPrevFileText}
|
||||
setCurrentFileType={setCurrentFileType}
|
||||
/>
|
||||
)}
|
||||
{isPublishFile && getMessage()}
|
||||
{/* @if TARGET='app' */}
|
||||
{isPublishFile && (
|
||||
{showFileUpload && (
|
||||
<FormField
|
||||
type="checkbox"
|
||||
checked={userOptimize}
|
||||
|
@ -387,7 +623,7 @@ function PublishFile(props: Props) {
|
|||
name="optimize"
|
||||
/>
|
||||
)}
|
||||
{isPublishFile && !ffmpegAvail && (
|
||||
{showFileUpload && !ffmpegAvail && (
|
||||
<p className="help">
|
||||
<I18nMessage
|
||||
tokens={{
|
||||
|
@ -398,7 +634,7 @@ function PublishFile(props: Props) {
|
|||
</I18nMessage>
|
||||
</p>
|
||||
)}
|
||||
{isPublishFile && Boolean(size) && ffmpegAvail && optimize && isVid && (
|
||||
{showFileUpload && Boolean(size) && ffmpegAvail && optimize && isVid && (
|
||||
<p className="help">
|
||||
<I18nMessage
|
||||
tokens={{
|
||||
|
@ -412,6 +648,16 @@ function PublishFile(props: Props) {
|
|||
</p>
|
||||
)}
|
||||
{/* @endif */}
|
||||
{isPublishPost && (
|
||||
<PostEditor
|
||||
label={__('Post --[noun, markdown post tab button]--')}
|
||||
uri={uri}
|
||||
disabled={disabled}
|
||||
fileMimeType={fileMimeType}
|
||||
setPrevFileText={setPrevFileText}
|
||||
setCurrentFileType={setCurrentFileType}
|
||||
/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
}
|
||||
/>
|
||||
|
|
|
@ -12,7 +12,10 @@ import {
|
|||
doPrepareEdit,
|
||||
doCheckPublishNameAvailability,
|
||||
SETTINGS,
|
||||
selectMyChannelClaims,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
} from 'lbry-redux';
|
||||
import * as RENDER_MODES from 'constants/file_render_modes';
|
||||
import { doPublishDesktop } from 'redux/actions/publish';
|
||||
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
|
||||
import {
|
||||
|
@ -22,27 +25,36 @@ import {
|
|||
selectActiveChannelStakedLevel,
|
||||
} from 'redux/selectors/app';
|
||||
import { makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { makeSelectFileRenderModeForUri } from 'redux/selectors/content';
|
||||
import PublishPage from './view';
|
||||
import { selectUser } from 'redux/selectors/user';
|
||||
|
||||
const select = (state) => ({
|
||||
...selectPublishFormValues(state),
|
||||
user: selectUser(state),
|
||||
// The winning claim for a short lbry uri
|
||||
amountNeededForTakeover: selectTakeOverAmount(state),
|
||||
// My previously published claims under this short lbry uri
|
||||
myClaimForUri: selectMyClaimForUri(state),
|
||||
// If I clicked the "edit" button, have I changed the uri?
|
||||
// Need this to make it easier to find the source on previously published content
|
||||
isStillEditing: selectIsStillEditing(state),
|
||||
isResolvingUri: selectIsResolvingPublishUris(state),
|
||||
totalRewardValue: selectUnclaimedRewardValue(state),
|
||||
modal: selectModal(state),
|
||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||
activeChannelClaim: selectActiveChannelClaim(state),
|
||||
incognito: selectIncognito(state),
|
||||
activeChannelStakedLevel: selectActiveChannelStakedLevel(state),
|
||||
});
|
||||
const select = (state) => {
|
||||
const myClaimForUri = selectMyClaimForUri(state);
|
||||
const permanentUrl = (myClaimForUri && myClaimForUri.permanent_url) || '';
|
||||
const isPostClaim = makeSelectFileRenderModeForUri(permanentUrl)(state) === RENDER_MODES.MARKDOWN;
|
||||
return {
|
||||
...selectPublishFormValues(state),
|
||||
user: selectUser(state),
|
||||
// The winning claim for a short lbry uri
|
||||
amountNeededForTakeover: selectTakeOverAmount(state),
|
||||
isLivestreamClaim: makeSelectClaimIsStreamPlaceholder(permanentUrl)(state),
|
||||
isPostClaim,
|
||||
permanentUrl,
|
||||
// My previously published claims under this short lbry uri
|
||||
// If I clicked the "edit" button, have I changed the uri?
|
||||
// Need this to make it easier to find the source on previously published content
|
||||
myClaimForUri,
|
||||
isStillEditing: selectIsStillEditing(state),
|
||||
isResolvingUri: selectIsResolvingPublishUris(state),
|
||||
totalRewardValue: selectUnclaimedRewardValue(state),
|
||||
modal: selectModal(state),
|
||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||
activeChannelClaim: selectActiveChannelClaim(state),
|
||||
myChannels: selectMyChannelClaims(state),
|
||||
incognito: selectIncognito(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
updatePublishForm: (value) => dispatch(doUpdatePublishForm(value)),
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
import { SITE_NAME, ENABLE_NO_SOURCE_CLAIMS, SIMPLE_SITE, CHANNEL_STAKED_LEVEL_LIVESTREAM } from 'config';
|
||||
import React, { useEffect } from 'react';
|
||||
import { buildURI, isURIValid, isNameValid, THUMBNAIL_STATUSES } from 'lbry-redux';
|
||||
import { buildURI, isURIValid, isNameValid, THUMBNAIL_STATUSES, Lbry } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
import ChannelSelect from 'component/channelSelector';
|
||||
import classnames from 'classnames';
|
||||
|
@ -27,6 +27,7 @@ import I18nMessage from 'component/i18nMessage';
|
|||
import * as PUBLISH_MODES from 'constants/publish_types';
|
||||
import { useHistory } from 'react-router';
|
||||
import Spinner from 'component/spinner';
|
||||
import { toHex } from 'util/hex';
|
||||
|
||||
// @if TARGET='app'
|
||||
import fs from 'fs';
|
||||
|
@ -82,6 +83,9 @@ type Props = {
|
|||
incognito: boolean,
|
||||
user: ?User,
|
||||
activeChannelStakedLevel: number,
|
||||
isLivestreamClaim: boolean,
|
||||
isPostClaim: boolean,
|
||||
permanentUrl: ?string,
|
||||
};
|
||||
|
||||
function PublishForm(props: Props) {
|
||||
|
@ -114,28 +118,67 @@ function PublishForm(props: Props) {
|
|||
incognito,
|
||||
user,
|
||||
activeChannelStakedLevel,
|
||||
} = props;
|
||||
isLivestreamClaim,
|
||||
isPostClaim,
|
||||
permanentUrl,
|
||||
|
||||
const { replace, location } = useHistory();
|
||||
const urlParams = new URLSearchParams(location.search);
|
||||
const uploadType = urlParams.get('type');
|
||||
const livestreamEnabled =
|
||||
ENABLE_NO_SOURCE_CLAIMS &&
|
||||
const TYPE_PARAM = 'type';
|
||||
const uploadType = urlParams.get(TYPE_PARAM);
|
||||
const enableLivestream = ENABLE_NO_SOURCE_CLAIMS &&
|
||||
user &&
|
||||
!user.odysee_live_disabled &&
|
||||
(activeChannelStakedLevel >= CHANNEL_STAKED_LEVEL_LIVESTREAM || user.odysee_live_enabled);
|
||||
// $FlowFixMe
|
||||
const MODES = livestreamEnabled
|
||||
? Object.values(PUBLISH_MODES)
|
||||
: Object.values(PUBLISH_MODES).filter((mode) => mode !== PUBLISH_MODES.LIVESTREAM);
|
||||
const AVAILABLE_MODES = Object.values(PUBLISH_MODES).filter((mode) => {
|
||||
if (editingURI) {
|
||||
if (isPostClaim) {
|
||||
return mode === PUBLISH_MODES.POST;
|
||||
} else if (isLivestreamClaim) {
|
||||
return mode === PUBLISH_MODES.LIVESTREAM && enableLivestream;
|
||||
} else {
|
||||
return mode === PUBLISH_MODES.FILE;
|
||||
}
|
||||
} else {
|
||||
if (mode === PUBLISH_MODES.LIVESTREAM) {
|
||||
return enableLivestream;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const MODE_TO_I18N_STR = {
|
||||
[PUBLISH_MODES.FILE]: SIMPLE_SITE ? 'Video' : 'File',
|
||||
[PUBLISH_MODES.POST]: 'Post --[noun, markdown post tab button]--',
|
||||
[PUBLISH_MODES.LIVESTREAM]: 'Livestream --[noun, livestream tab button]--',
|
||||
};
|
||||
// Component state
|
||||
|
||||
const [mode, setMode] = React.useState(uploadType || PUBLISH_MODES.FILE);
|
||||
const [isCheckingLivestreams, setCheckingLivestreams] = React.useState(false);
|
||||
|
||||
let customSubtitle;
|
||||
if (mode === PUBLISH_MODES.LIVESTREAM || isLivestreamClaim) {
|
||||
if (isLivestreamClaim) {
|
||||
customSubtitle = __('Update your livestream');
|
||||
} else {
|
||||
customSubtitle = __('Prepare an upcoming livestream');
|
||||
}
|
||||
} else if (mode === PUBLISH_MODES.POST || isPostClaim) {
|
||||
if (isPostClaim) {
|
||||
customSubtitle = __('Edit your post');
|
||||
} else {
|
||||
customSubtitle = __('Craft an epic post clearly explaining... whatever.');
|
||||
}
|
||||
} else {
|
||||
if (editingURI) {
|
||||
customSubtitle = __('Update your video');
|
||||
} else {
|
||||
customSubtitle = __('Upload that unlabeled video you found behind the TV in 1991');
|
||||
}
|
||||
}
|
||||
|
||||
const [autoSwitchMode, setAutoSwitchMode] = React.useState(true);
|
||||
|
||||
// Used to check if the url name has changed:
|
||||
|
@ -145,18 +188,23 @@ function PublishForm(props: Props) {
|
|||
const [fileEdited, setFileEdited] = React.useState(false);
|
||||
const [prevFileText, setPrevFileText] = React.useState('');
|
||||
|
||||
const [livestreamData, setLivestreamData] = React.useState([]);
|
||||
const [signedMessage, setSignedMessage] = React.useState({});
|
||||
const signedMessageStr = JSON.stringify(signedMessage);
|
||||
const TAGS_LIMIT = 5;
|
||||
const fileFormDisabled = mode === PUBLISH_MODES.FILE && !filePath;
|
||||
const emptyPostError = mode === PUBLISH_MODES.POST && (!fileText || fileText.trim() === '');
|
||||
const formDisabled = (fileFormDisabled && !editingURI) || emptyPostError || publishing;
|
||||
const isInProgress = filePath || editingURI || name || title;
|
||||
const activeChannelName = activeChannelClaim && activeChannelClaim.name;
|
||||
const activeChannelClaimStr = JSON.stringify(activeChannelClaim);
|
||||
// Editing content info
|
||||
const uri = myClaimForUri ? myClaimForUri.permanent_url : undefined;
|
||||
const fileMimeType =
|
||||
myClaimForUri && myClaimForUri.value && myClaimForUri.value.source
|
||||
? myClaimForUri.value.source.media_type
|
||||
: undefined;
|
||||
const claimChannelId = myClaimForUri && myClaimForUri.signing_channel && myClaimForUri.signing_channel.claim_id;
|
||||
|
||||
const nameEdited = isStillEditing && name !== prevName;
|
||||
|
||||
// If they are editing, they don't need a new file chosen
|
||||
|
@ -178,6 +226,29 @@ function PublishForm(props: Props) {
|
|||
: formValidLessFile;
|
||||
|
||||
const [previewing, setPreviewing] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (activeChannelClaimStr) {
|
||||
const channelClaim = JSON.parse(activeChannelClaimStr);
|
||||
const message = 'get-claim-id-replays';
|
||||
setSignedMessage({ signature: null, signing_ts: null });
|
||||
// ensure we have a channel
|
||||
if (channelClaim.claim_id) {
|
||||
Lbry.channel_sign({
|
||||
channel_id: channelClaim.claim_id,
|
||||
hexdata: toHex(message),
|
||||
})
|
||||
.then((data) => {
|
||||
console.log('data', data);
|
||||
setSignedMessage(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
setSignedMessage({ signature: null, signing_ts: null });
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [activeChannelClaimStr, setSignedMessage]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!modal) {
|
||||
setTimeout(() => {
|
||||
|
@ -186,12 +257,38 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}, [modal]);
|
||||
|
||||
const isLivestream = mode === PUBLISH_MODES.LIVESTREAM;
|
||||
// move this to lbryinc OR to a file under ui, and/or provide a standardized livestreaming config.
|
||||
function checkLivestreams(channelId, signature, timestamp) {
|
||||
// $FlowFixMe Bitwave's API can handle garbage
|
||||
setCheckingLivestreams(true);
|
||||
fetch(`https://api.bitwave.tv/v1/replays/odysee/${channelId}?signature=${signature}&signing_ts=${timestamp}`) // claimChannelId
|
||||
.then((res) => res.json())
|
||||
.then((res) => {
|
||||
if (!res || !res.data) {
|
||||
setLivestreamData([]);
|
||||
}
|
||||
setLivestreamData(res.data);
|
||||
setCheckingLivestreams(false);
|
||||
})
|
||||
.catch((e) => {
|
||||
setLivestreamData([]);
|
||||
setCheckingLivestreams(false);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const signedMessage = JSON.parse(signedMessageStr);
|
||||
if (claimChannelId && isLivestreamClaim && signedMessage.signature) {
|
||||
checkLivestreams(claimChannelId, signedMessage.signature, signedMessage.signing_ts);
|
||||
}
|
||||
}, [claimChannelId, isLivestreamClaim, signedMessageStr]);
|
||||
|
||||
const isLivestreamMode = mode === PUBLISH_MODES.LIVESTREAM;
|
||||
let submitLabel;
|
||||
if (publishing) {
|
||||
if (isStillEditing) {
|
||||
submitLabel = __('Saving...');
|
||||
} else if (isLivestream) {
|
||||
} else if (isLivestreamMode) {
|
||||
submitLabel = __('Creating...');
|
||||
} else {
|
||||
submitLabel = __('Uploading...');
|
||||
|
@ -201,7 +298,7 @@ function PublishForm(props: Props) {
|
|||
} else {
|
||||
if (isStillEditing) {
|
||||
submitLabel = __('Save');
|
||||
} else if (isLivestream) {
|
||||
} else if (isLivestreamMode) {
|
||||
submitLabel = __('Create');
|
||||
} else {
|
||||
submitLabel = __('Upload');
|
||||
|
@ -221,7 +318,7 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}, [thumbnail, resetThumbnailStatus]);
|
||||
|
||||
// Save current name of the editing claim
|
||||
// Save previous name of the editing claim
|
||||
useEffect(() => {
|
||||
if (isStillEditing && (!prevName || !prevName.trim() === '')) {
|
||||
if (name !== prevName) {
|
||||
|
@ -263,17 +360,18 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}, [name, activeChannelName, resolveUri, updatePublishForm, checkAvailability]);
|
||||
|
||||
// because publish editingUri is channel_short/claim_long and we don't have that, resolve it.
|
||||
useEffect(() => {
|
||||
// because editingURI is lbry://channel_short/claim_long and that particular shape won't map to the claimId yet
|
||||
if (editingURI) {
|
||||
resolveUri(editingURI);
|
||||
}
|
||||
}, [editingURI, resolveUri]);
|
||||
|
||||
// set isMarkdownPost in publish form if so, also update isLivestreamPublish
|
||||
useEffect(() => {
|
||||
updatePublishForm({
|
||||
isMarkdownPost: mode === PUBLISH_MODES.POST,
|
||||
isLivestreamPublish: isLivestream,
|
||||
isLivestreamPublish: isLivestreamMode,
|
||||
});
|
||||
}, [mode, updatePublishForm]);
|
||||
|
||||
|
@ -282,14 +380,15 @@ function PublishForm(props: Props) {
|
|||
updatePublishForm({ channel: undefined });
|
||||
|
||||
// Anonymous livestreams aren't supported
|
||||
if (isLivestream) {
|
||||
if (isLivestreamMode) {
|
||||
setMode(PUBLISH_MODES.FILE);
|
||||
}
|
||||
} else if (activeChannelName) {
|
||||
updatePublishForm({ channel: activeChannelName });
|
||||
}
|
||||
}, [activeChannelName, incognito, updatePublishForm]);
|
||||
}, [activeChannelName, incognito, updatePublishForm, isLivestreamMode]);
|
||||
|
||||
// set mode based on urlParams 'type'
|
||||
useEffect(() => {
|
||||
const _uploadType = uploadType && uploadType.toLowerCase();
|
||||
|
||||
|
@ -322,7 +421,7 @@ function PublishForm(props: Props) {
|
|||
useEffect(() => {
|
||||
if (!uploadType) return;
|
||||
const newParams = new URLSearchParams();
|
||||
newParams.set('type', mode.toLowerCase());
|
||||
newParams.set(TYPE_PARAM, mode.toLowerCase());
|
||||
replace({ search: newParams.toString() });
|
||||
}, [mode, uploadType]);
|
||||
|
||||
|
@ -391,7 +490,7 @@ function PublishForm(props: Props) {
|
|||
}
|
||||
}
|
||||
// Publish file
|
||||
if (mode === PUBLISH_MODES.FILE || isLivestream) {
|
||||
if (mode === PUBLISH_MODES.FILE || isLivestreamMode) {
|
||||
runPublish = true;
|
||||
}
|
||||
|
||||
|
@ -428,19 +527,23 @@ function PublishForm(props: Props) {
|
|||
// Editing claim uri
|
||||
return (
|
||||
<div className="card-stack">
|
||||
<ChannelSelect hideAnon={isLivestream} disabled={disabled} />
|
||||
<ChannelSelect hideAnon={isLivestreamMode} disabled={disabled} />
|
||||
|
||||
<PublishFile
|
||||
uri={uri}
|
||||
uri={permanentUrl}
|
||||
mode={mode}
|
||||
fileMimeType={fileMimeType}
|
||||
disabled={disabled || publishing}
|
||||
inProgress={isInProgress}
|
||||
setPublishMode={setMode}
|
||||
setPrevFileText={setPrevFileText}
|
||||
livestreamData={livestreamData}
|
||||
subtitle={customSubtitle}
|
||||
isCheckingLivestreams={isCheckingLivestreams}
|
||||
checkLivestreams={checkLivestreams}
|
||||
header={
|
||||
<>
|
||||
{MODES.map((modeName) => (
|
||||
{AVAILABLE_MODES.map((modeName) => (
|
||||
<Button
|
||||
key={String(modeName)}
|
||||
icon={modeName}
|
||||
|
@ -460,7 +563,7 @@ function PublishForm(props: Props) {
|
|||
{!publishing && (
|
||||
<div className={classnames({ 'card--disabled': formDisabled })}>
|
||||
{mode === PUBLISH_MODES.FILE && <PublishDescription disabled={formDisabled} />}
|
||||
<Card actions={<SelectThumbnail />} />
|
||||
<Card actions={<SelectThumbnail livestreamdData={livestreamData} />} />
|
||||
<TagsSelect
|
||||
suggestMature={!SIMPLE_SITE}
|
||||
disableAutoFocus
|
||||
|
@ -489,7 +592,7 @@ function PublishForm(props: Props) {
|
|||
/>
|
||||
|
||||
<PublishBid disabled={isStillEditing || formDisabled} />
|
||||
{!isLivestream && <PublishPrice disabled={formDisabled} />}
|
||||
{!isLivestreamMode && <PublishPrice disabled={formDisabled} />}
|
||||
<PublishAdditionalOptions disabled={formDisabled} />
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -6,22 +6,29 @@ import {
|
|||
selectPublishFormValues,
|
||||
selectIsStillEditing,
|
||||
selectMyChannelClaims,
|
||||
makeSelectClaimIsStreamPlaceholder,
|
||||
SETTINGS,
|
||||
} from 'lbry-redux';
|
||||
import { selectFfmpegStatus, makeSelectClientSetting } from 'redux/selectors/settings';
|
||||
import { doPublishDesktop } from 'redux/actions/publish';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
|
||||
const select = (state) => ({
|
||||
...selectPublishFormValues(state),
|
||||
myChannels: selectMyChannelClaims(state),
|
||||
isVid: makeSelectPublishFormValue('fileVid')(state),
|
||||
publishSuccess: makeSelectPublishFormValue('publishSuccess')(state),
|
||||
publishing: makeSelectPublishFormValue('publishing')(state),
|
||||
isStillEditing: selectIsStillEditing(state),
|
||||
ffmpegStatus: selectFfmpegStatus(state),
|
||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||
});
|
||||
const select = (state, props) => {
|
||||
const editingUri = makeSelectPublishFormValue('editingURI')(state);
|
||||
|
||||
return {
|
||||
...selectPublishFormValues(state),
|
||||
myChannels: selectMyChannelClaims(state),
|
||||
isVid: makeSelectPublishFormValue('fileVid')(state),
|
||||
publishSuccess: makeSelectPublishFormValue('publishSuccess')(state),
|
||||
publishing: makeSelectPublishFormValue('publishing')(state),
|
||||
remoteFile: makeSelectPublishFormValue('remoteFileUrl')(state),
|
||||
isStillEditing: selectIsStillEditing(state),
|
||||
ffmpegStatus: selectFfmpegStatus(state),
|
||||
enablePublishPreview: makeSelectClientSetting(SETTINGS.ENABLE_PUBLISH_PREVIEW)(state),
|
||||
isLivestreamClaim: makeSelectClaimIsStreamPlaceholder(editingUri)(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
publish: (filePath, preview) => dispatch(doPublishDesktop(filePath, preview)),
|
||||
|
|
|
@ -43,6 +43,8 @@ type Props = {
|
|||
myChannels: ?Array<ChannelClaim>,
|
||||
publishSuccess: boolean,
|
||||
publishing: boolean,
|
||||
isLivestreamClaim: boolean,
|
||||
remoteFile: string,
|
||||
};
|
||||
|
||||
// class ModalPublishPreview extends React.PureComponent<Props> {
|
||||
|
@ -74,10 +76,14 @@ const ModalPublishPreview = (props: Props) => {
|
|||
publishing,
|
||||
publish,
|
||||
closeModal,
|
||||
isLivestreamClaim,
|
||||
remoteFile,
|
||||
} = props;
|
||||
|
||||
const livestream =
|
||||
(uri && isLivestreamClaim) ||
|
||||
// $FlowFixMe
|
||||
previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source;
|
||||
(previewResponse.outputs[0] && previewResponse.outputs[0].value && !previewResponse.outputs[0].value.source);
|
||||
// leave the confirm modal up if we're not going straight to upload/reflecting
|
||||
// @if TARGET='web'
|
||||
React.useEffect(() => {
|
||||
|
@ -122,7 +128,11 @@ const ModalPublishPreview = (props: Props) => {
|
|||
const isOptimizeAvail = filePath && filePath !== '' && isVid && ffmpegStatus.available;
|
||||
let modalTitle;
|
||||
if (isStillEditing) {
|
||||
modalTitle = __('Confirm Edit');
|
||||
if (livestream) {
|
||||
modalTitle = __('Confirm Update');
|
||||
} else {
|
||||
modalTitle = __('Confirm Edit');
|
||||
}
|
||||
} else if (livestream) {
|
||||
modalTitle = __('Create Livestream');
|
||||
} else {
|
||||
|
@ -208,6 +218,7 @@ const ModalPublishPreview = (props: Props) => {
|
|||
<table className="table table--condensed table--publish-preview">
|
||||
<tbody>
|
||||
{!livestream && !isMarkdownPost && createRow(__('File'), getFilePathName(filePath))}
|
||||
{livestream && remoteFile && createRow(__('Replay'), __('Remote File Selected'))}
|
||||
{isOptimizeAvail && createRow(__('Transcode'), optimize ? __('Yes') : __('No'))}
|
||||
{createRow(__('Title'), title)}
|
||||
{createRow(__('Description'), descriptionValue)}
|
||||
|
|
|
@ -205,3 +205,79 @@
|
|||
.livestream__publish-intro {
|
||||
margin-top: var(--spacing-l);
|
||||
}
|
||||
|
||||
.table--livestream-data {
|
||||
td:nth-of-type(1) {
|
||||
width: 10%;
|
||||
}
|
||||
td:nth-of-type(2) {
|
||||
width: 40%;
|
||||
}
|
||||
td:nth-of-type(3) {
|
||||
width: 20%;
|
||||
}
|
||||
td:nth-of-type(4) {
|
||||
width: 30%;
|
||||
}
|
||||
@media (max-width: $breakpoint-small) {
|
||||
td:nth-of-type(1) {
|
||||
max-width: 10%;
|
||||
}
|
||||
td:nth-of-type(2) {
|
||||
width: 30%;
|
||||
}
|
||||
td:nth-of-type(3) {
|
||||
width: 50%;
|
||||
}
|
||||
td:nth-of-type(4) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livestream_thumb_container {
|
||||
height: 4rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.livestream___thumb {
|
||||
padding: var(--spacing-xxs);
|
||||
}
|
||||
|
||||
.livestream__data-row {
|
||||
cursor: pointer;
|
||||
.radio {
|
||||
cursor: pointer;
|
||||
}
|
||||
td {
|
||||
padding-right: var(--spacing-m) !important;
|
||||
|
||||
@media (max-width: $breakpoint-small) {
|
||||
padding: var(--spacing-xs) !important;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
td {
|
||||
.radio {
|
||||
label::before {
|
||||
cursor: pointer !important;
|
||||
background-color: var(--color-input-toggle-bg-hover) !important;
|
||||
}
|
||||
}
|
||||
label {
|
||||
cursor: pointer !important;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
cursor: pointer;
|
||||
background-color: var(--color-input-bg-selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.livestream__data-row--selected {
|
||||
background-color: var(--color-input-bg-selected) !important;
|
||||
}
|
||||
|
|
|
@ -40,12 +40,28 @@
|
|||
td {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: var(--font-xsmall);
|
||||
color: var(--color-text-help);
|
||||
padding-bottom: var(--spacing-xxxs);
|
||||
@media (max-width: $breakpoint-small) {
|
||||
padding-left: var(--spacing-xs) !important;
|
||||
padding-right: var(--spacing-xs) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
td {
|
||||
@media (max-width: $breakpoint-small) {
|
||||
font-size: var(--font-small);
|
||||
}
|
||||
}
|
||||
|
||||
.table--fixed {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
|
|
@ -42,8 +42,10 @@ export default function apiPublishCallViaWeb(
|
|||
if (fileField) {
|
||||
body.append('file', fileField);
|
||||
params.file_path = '__POST_FILE__';
|
||||
delete params['remote_url'];
|
||||
} else if (remoteUrl) {
|
||||
body.append('remote_url', remoteUrl);
|
||||
delete params['remote_url'];
|
||||
}
|
||||
|
||||
const jsonPayload = JSON.stringify({
|
||||
|
|
Loading…
Add table
Reference in a new issue