2019-06-28 09:27:55 +02:00
// @flow
2021-04-14 19:45:45 +02:00
import { SITE _NAME , WEB _PUBLISH _SIZE _LIMIT _GB , SIMPLE _SITE } from 'config' ;
2020-08-05 19:19:15 +02:00
import type { Node } from 'react' ;
2019-09-27 20:56:15 +02:00
import * as ICONS from 'constants/icons' ;
2020-03-07 00:11:16 +01:00
import React , { useState , useEffect } from 'react' ;
2021-10-17 10:36:14 +02:00
import { regexInvalidURI } from 'util/lbryURI' ;
2020-07-29 22:30:26 +02:00
import PostEditor from 'component/postEditor' ;
2019-06-28 09:27:55 +02:00
import FileSelector from 'component/common/file-selector' ;
2019-07-17 05:23:45 +02:00
import Button from 'component/button' ;
2019-09-27 20:56:15 +02:00
import Card from 'component/common/card' ;
2020-03-24 18:57:17 +01:00
import { FormField } from 'component/common/form' ;
2019-10-11 02:37:18 +02:00
import Spinner from 'component/spinner' ;
2020-07-01 17:35:05 +02:00
import I18nMessage from 'component/i18nMessage' ;
import usePersistedState from 'effects/use-persisted-state' ;
2020-07-28 01:12:59 +02:00
import * as PUBLISH _MODES from 'constants/publish_types' ;
2021-02-09 17:05:56 +01:00
import PublishName from 'component/publishName' ;
2021-04-14 06:06:11 +02:00
import CopyableText from 'component/copyableText' ;
import Empty from 'component/common/empty' ;
import moment from 'moment' ;
import classnames from 'classnames' ;
import ReactPaginate from 'react-paginate' ;
2019-06-28 09:27:55 +02:00
type Props = {
2020-07-28 01:12:59 +02:00
uri : ? string ,
mode : ? string ,
2019-06-28 09:27:55 +02:00
name : ? string ,
2020-07-28 01:12:59 +02:00
title : ? string ,
2019-10-07 22:02:32 +02:00
filePath : string | WebFile ,
2020-07-29 04:56:07 +02:00
fileMimeType : ? string ,
2019-06-28 09:27:55 +02:00
isStillEditing : boolean ,
balance : number ,
updatePublishForm : ( { } ) => void ,
2019-09-27 20:56:15 +02:00
disabled : boolean ,
2019-10-11 02:37:18 +02:00
publishing : boolean ,
2021-02-16 03:33:34 +01:00
showToast : ( string ) => void ,
2019-12-26 16:37:26 +01:00
inProgress : boolean ,
clearPublish : ( ) => void ,
2020-03-24 18:57:17 +01:00
ffmpegStatus : any ,
optimize : boolean ,
2020-03-30 20:19:32 +02:00
size : number ,
duration : number ,
isVid : boolean ,
2021-04-14 06:06:11 +02:00
subtitle : string ,
2021-02-16 03:33:34 +01:00
setPublishMode : ( string ) => void ,
setPrevFileText : ( string ) => void ,
2020-08-05 19:19:15 +02:00
header : Node ,
2021-04-14 06:06:11 +02:00
livestreamData : LivestreamReplayData ,
isLivestreamClaim : boolean ,
checkLivestreams : ( string , ? string , ? string ) => void ,
channelId : string ,
channelSignature : { signature ? : string , signing _ts ? : string } ,
isCheckingLivestreams : boolean ,
setWaitForFile : ( boolean ) => void ,
2019-06-28 09:27:55 +02:00
} ;
function PublishFile ( props : Props ) {
2019-12-26 16:37:26 +01:00
const {
2020-07-28 01:12:59 +02:00
uri ,
mode ,
2019-12-26 16:37:26 +01:00
name ,
2020-07-28 01:12:59 +02:00
title ,
2019-12-26 16:37:26 +01:00
balance ,
filePath ,
2020-07-29 04:56:07 +02:00
fileMimeType ,
2019-12-26 16:37:26 +01:00
isStillEditing ,
updatePublishForm ,
disabled ,
publishing ,
inProgress ,
clearPublish ,
2020-03-24 18:57:17 +01:00
optimize ,
ffmpegStatus = { } ,
2020-03-30 20:19:32 +02:00
size ,
duration ,
isVid ,
2020-07-28 01:12:59 +02:00
setPublishMode ,
setPrevFileText ,
2020-08-05 19:06:24 +02:00
header ,
2021-04-14 06:06:11 +02:00
livestreamData ,
isLivestreamClaim ,
subtitle ,
checkLivestreams ,
channelId ,
channelSignature ,
isCheckingLivestreams ,
setWaitForFile ,
2019-12-26 16:37:26 +01:00
} = props ;
2019-10-11 02:37:18 +02:00
2021-04-14 06:06:11 +02:00
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 ) ;
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 ;
2020-07-01 16:17:07 +02:00
const ffmpegAvail = ffmpegStatus . available ;
2020-03-07 00:11:16 +01:00
const [ oversized , setOversized ] = useState ( false ) ;
2020-05-25 16:27:36 +02:00
const [ currentFile , setCurrentFile ] = useState ( null ) ;
2020-07-28 01:12:59 +02:00
const [ currentFileType , setCurrentFileType ] = useState ( null ) ;
2020-07-01 17:35:05 +02:00
const [ optimizeAvail , setOptimizeAvail ] = useState ( false ) ;
const [ userOptimize , setUserOptimize ] = usePersistedState ( 'publish-file-user-optimize' , false ) ;
2021-11-11 03:16:31 +01:00
const UPLOAD _SIZE _MESSAGE = _ _ ( '%SITE_NAME% uploads are limited to %limit% GB.' , {
SITE _NAME ,
limit : TV _PUBLISH _SIZE _LIMIT _GB _STR ,
} ) ;
2020-03-24 18:57:17 +01:00
2021-04-14 06:06:11 +02:00
const fileSelectorModes = [
{ label : _ _ ( 'Upload' ) , actionName : SOURCE _UPLOAD , icon : ICONS . PUBLISH } ,
2021-04-14 17:56:45 +02:00
{ label : _ _ ( 'Choose Replay' ) , actionName : SOURCE _SELECT , icon : ICONS . MENU } ,
2021-04-14 06:06:11 +02:00
{ label : _ _ ( 'None' ) , actionName : SOURCE _NONE } ,
] ;
const livestreamDataStr = JSON . stringify ( livestreamData ) ;
const hasLivestreamData = livestreamData && Boolean ( livestreamData . length ) ;
2021-04-14 17:56:45 +02:00
const showSourceSelector = isLivestreamClaim || ( hasLivestreamData && mode === PUBLISH _MODES . FILE ) ;
2021-04-14 06:06:11 +02:00
const [ fileSelectSource , setFileSelectSource ] = useState (
2021-04-14 17:56:45 +02:00
IS _WEB && showSourceSelector && name ? SOURCE _SELECT : SOURCE _UPLOAD
2021-04-14 06:06:11 +02:00
) ;
// const [showFileUpdate, setShowFileUpdate] = useState(false);
const [ selectedFileIndex , setSelectedFileIndex ] = useState ( null ) ;
const PAGE _SIZE = 4 ;
const [ currentPage , setCurrentPage ] = useState ( 1 ) ;
const totalPages =
hasLivestreamData && livestreamData . length > PAGE _SIZE ? Math . ceil ( livestreamData . length / PAGE _SIZE ) : 1 ;
2020-03-07 00:11:16 +01:00
2020-07-28 01:12:59 +02:00
// Reset filePath if publish mode changed
useEffect ( ( ) => {
2020-07-29 22:30:26 +02:00
if ( mode === PUBLISH _MODES . POST ) {
2020-07-28 04:19:00 +02:00
if ( currentFileType !== 'text/markdown' && ! isStillEditing ) {
2020-07-30 00:55:48 +02:00
updatePublishForm ( { filePath : '' } ) ;
2020-07-28 01:12:59 +02:00
}
2021-04-14 06:06:11 +02:00
} else if ( mode === PUBLISH _MODES . LIVESTREAM ) {
updatePublishForm ( { filePath : '' } ) ;
2020-07-28 01:12:59 +02:00
}
2020-07-28 04:19:00 +02:00
} , [ currentFileType , mode , isStillEditing , updatePublishForm ] ) ;
2020-07-28 01:12:59 +02:00
2021-04-14 06:06:11 +02:00
// set default file source to select if necessary
useEffect ( ( ) => {
if ( hasLivestreamData && isLivestreamClaim ) {
setWaitForFile ( true ) ;
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 ( ( ) => {
const livestreamData = JSON . parse ( livestreamDataStr ) ;
if ( selectedFileIndex !== null && livestreamData && livestreamData . length ) {
updatePublishForm ( {
remoteFileUrl : normalizeUrlForProtocol ( livestreamData [ selectedFileIndex ] . data . fileLocation ) ,
} ) ;
}
} , [ selectedFileIndex , updatePublishForm , livestreamDataStr ] ) ;
2020-03-07 00:11:16 +01:00
useEffect ( ( ) => {
2020-03-30 20:19:32 +02:00
if ( ! filePath || filePath === '' ) {
2020-05-25 16:27:36 +02:00
setCurrentFile ( '' ) ;
2020-03-07 00:11:16 +01:00
setOversized ( false ) ;
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , 0 , false ) ;
2020-05-25 16:27:36 +02:00
} else if ( typeof filePath !== 'string' ) {
// Update currentFile file
if ( filePath . name !== currentFile && filePath . path !== currentFile ) {
handleFileChange ( filePath ) ;
}
2020-03-07 00:11:16 +01:00
}
2020-07-01 16:17:07 +02:00
} , [ filePath , currentFile , handleFileChange , updateFileInfo ] ) ;
2019-10-07 22:02:32 +02:00
2020-07-01 17:35:05 +02:00
useEffect ( ( ) => {
const isOptimizeAvail = currentFile && currentFile !== '' && isVid && ffmpegAvail ;
const finalOptimizeState = isOptimizeAvail && userOptimize ;
setOptimizeAvail ( isOptimizeAvail ) ;
updatePublishForm ( { optimize : finalOptimizeState } ) ;
2020-07-28 01:12:59 +02:00
} , [ currentFile , filePath , isVid , ffmpegAvail , userOptimize , updatePublishForm ] ) ;
2020-07-01 17:35:05 +02:00
2020-07-01 16:17:07 +02:00
function updateFileInfo ( duration , size , isvid ) {
2020-03-30 20:19:32 +02:00
updatePublishForm ( { fileDur : duration , fileSize : size , fileVid : isvid } ) ;
}
2021-04-14 06:06:11 +02:00
function handlePaginateReplays ( page ) {
setCurrentPage ( page ) ;
}
2020-03-07 00:11:16 +01:00
function getBitrate ( size , duration ) {
const s = Number ( size ) ;
const d = Number ( duration ) ;
if ( s && d ) {
return ( s * 8 ) / d ;
} else {
return 0 ;
}
}
2020-03-24 18:57:17 +01:00
function getTimeForMB ( s ) {
if ( s < MINUTES _THRESHOLD ) {
return Math . floor ( secondsToProcess ) ;
} else if ( s >= MINUTES _THRESHOLD && s < HOURS _THRESHOLD ) {
return Math . floor ( secondsToProcess / 60 ) ;
} else {
return Math . floor ( secondsToProcess / 60 / 60 ) ;
}
}
function getUnitsForMB ( s ) {
if ( s < MINUTES _THRESHOLD ) {
2020-05-28 22:38:54 +02:00
if ( secondsToProcess > 1 ) return _ _ ( 'seconds' ) ;
2020-07-01 16:17:07 +02:00
return _ _ ( 'second' ) ;
2020-03-24 18:57:17 +01:00
} else if ( s >= MINUTES _THRESHOLD && s < HOURS _THRESHOLD ) {
2020-05-28 22:38:54 +02:00
if ( Math . floor ( secondsToProcess / 60 ) > 1 ) return _ _ ( 'minutes' ) ;
return _ _ ( 'minute' ) ;
2020-03-24 18:57:17 +01:00
} else {
2020-05-28 22:38:54 +02:00
if ( Math . floor ( secondsToProcess / 3600 ) > 1 ) return _ _ ( 'hours' ) ;
return _ _ ( 'hour' ) ;
2020-03-24 18:57:17 +01:00
}
}
2021-04-14 06:06:11 +02:00
function getUploadMessage ( ) {
2020-03-07 00:11:16 +01:00
// @if TARGET='web'
if ( oversized ) {
return (
< p className = "help--error" >
2020-10-05 20:24:57 +02:00
{ UPLOAD _SIZE _MESSAGE } { ' ' }
2020-07-23 19:02:07 +02:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://lbry.com/faq/video-publishing-guide" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
// @endif
if ( isVid && duration && getBitrate ( size , duration ) > RECOMMENDED _BITRATE ) {
return (
< p className = "help--warning" >
2020-05-12 20:57:02 +02:00
{ _ _ ( 'Your video has a bitrate over 5 Mbps. We suggest transcoding to provide viewers the best experience.' ) } { ' ' }
2021-10-21 22:21:51 +02:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
if ( isVid && ! duration ) {
return (
< p className = "help--warning" >
{ _ _ (
2020-05-12 20:57:02 +02:00
'Your video may not be the best format. Use MP4s in H264/AAC format and a friendly bitrate (under 5 Mbps) and resolution (720p) for more reliable streaming.'
2020-03-07 00:11:16 +01:00
) } { ' ' }
2021-10-21 22:21:51 +02:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
if ( ! ! isStillEditing && name ) {
2021-04-14 06:06:11 +02:00
if ( isLivestreamClaim ) {
return (
< p className = "help" > { _ _ ( 'You can upload your own recording or select a replay when your stream is over' ) } < / p >
) ;
}
2020-03-07 00:11:16 +01:00
return (
< p className = "help" >
{ _ _ ( "If you don't choose a file, the file from your existing claim %name% will be used" , { name : name } ) }
< / p >
) ;
}
// @if TARGET='web'
if ( ! isStillEditing ) {
return (
< p className = "help" >
{ _ _ (
2021-02-11 06:25:44 +01:00
'For video content, use MP4s in H264/AAC format and a friendly bitrate (under 5 Mbps) and resolution (720p) for more reliable streaming. %SITE_NAME% uploads are restricted to %limit% GB.' ,
2021-04-14 06:06:11 +02:00
{ SITE _NAME , limit : TV _PUBLISH _SIZE _LIMIT _GB _STR }
2020-03-07 00:11:16 +01:00
) } { ' ' }
2020-07-23 19:02:07 +02:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://lbry.com/faq/video-publishing-guide" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
// @endif
// @if TARGET='app'
if ( ! isStillEditing ) {
return (
< p className = "help" >
{ _ _ (
2020-05-12 20:57:02 +02:00
'For video content, use MP4s in H264/AAC format and a friendly bitrate (under 5 Mbps) and resolution (720p) for more reliable streaming.'
2020-03-07 00:11:16 +01:00
) } { ' ' }
2021-10-21 22:21:51 +02:00
< Button button = "link" label = { _ _ ( 'Upload Guide' ) } href = "https://odysee.com/@OdyseeHelp:b/uploadguide:1" / >
2020-03-07 00:11:16 +01:00
< / p >
) ;
}
// @endif
}
2020-07-30 00:55:48 +02:00
function parseName ( newName ) {
let INVALID _URI _CHARS = new RegExp ( regexInvalidURI , 'gu' ) ;
return newName . replace ( INVALID _URI _CHARS , '-' ) ;
}
2021-04-14 06:06:11 +02:00
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
2021-04-23 18:57:51 +02:00
handleFileChange ( false , false ) ;
2021-04-14 06:06:11 +02:00
updatePublishForm ( { remoteFileUrl : undefined } ) ;
} else if ( source === SOURCE _UPLOAD ) {
updatePublishForm ( { remoteFileUrl : undefined } ) ;
} else if ( source === SOURCE _SELECT ) {
// $FlowFixMe
2021-04-23 18:57:51 +02:00
handleFileChange ( false , false ) ;
2021-04-14 06:06:11 +02:00
if ( selectedFileIndex !== null ) {
updatePublishForm ( { remoteFileUrl : livestreamData [ selectedFileIndex ] . data . fileLocation } ) ;
}
}
setFileSelectSource ( source ) ;
setWaitForFile ( source !== SOURCE _NONE ) ;
}
2020-07-30 06:03:56 +02:00
function handleTitleChange ( event ) {
const title = event . target . value ;
2020-07-30 00:55:48 +02:00
// Update title
2020-07-30 06:03:56 +02:00
updatePublishForm ( { title } ) ;
2020-07-30 00:55:48 +02:00
}
2020-08-11 04:08:03 +02:00
function handleFileReaderLoaded ( event : ProgressEvent ) {
// See: https://github.com/facebook/flow/issues/3470
if ( event . target instanceof FileReader ) {
const text = event . target . result ;
updatePublishForm ( { fileText : text } ) ;
setPublishMode ( PUBLISH _MODES . POST ) ;
}
}
2021-04-23 18:57:51 +02:00
function handleFileChange ( file : WebFile , clearName = true ) {
2019-12-09 19:51:00 +01:00
const { showToast } = props ;
2020-03-07 00:11:16 +01:00
window . URL = window . URL || window . webkitURL ;
setOversized ( false ) ;
// select file, start to select a new one, then cancel
2020-05-28 16:45:56 +02:00
if ( ! file ) {
2021-04-23 18:57:51 +02:00
if ( isStillEditing || ! clearName ) {
2021-04-14 06:06:11 +02:00
updatePublishForm ( { filePath : '' } ) ;
} else {
updatePublishForm ( { filePath : '' , name : '' } ) ;
}
2020-03-07 00:11:16 +01:00
return ;
}
2020-05-25 16:27:36 +02:00
// if video, extract duration so we can warn about bitrateif (typeof file !== 'string') {
2020-05-28 16:45:56 +02:00
const contentType = file . type && file . type . split ( '/' ) ;
const isVideo = contentType && contentType [ 0 ] === 'video' ;
const isMp4 = contentType && contentType [ 1 ] === 'mp4' ;
2020-07-28 01:12:59 +02:00
2020-08-24 22:45:08 +02:00
let isTextPost = false ;
2020-07-28 01:12:59 +02:00
2020-08-24 22:45:08 +02:00
if ( contentType && contentType [ 0 ] === 'text' ) {
isTextPost = contentType [ 1 ] === 'plain' || contentType [ 1 ] === 'markdown' ;
2020-07-28 01:12:59 +02:00
setCurrentFileType ( contentType ) ;
} else if ( file . name ) {
// If user's machine is missign a valid content type registration
// for markdown content: text/markdown, file extension will be used instead
const extension = file . name . split ( '.' ) . pop ( ) ;
2020-08-24 22:45:08 +02:00
isTextPost = MARKDOWN _FILE _EXTENSIONS . includes ( extension ) ;
2020-07-28 01:12:59 +02:00
}
2020-03-07 00:11:16 +01:00
if ( isVideo ) {
if ( isMp4 ) {
const video = document . createElement ( 'video' ) ;
video . preload = 'metadata' ;
2021-02-16 03:33:34 +01:00
video . onloadedmetadata = ( ) => {
2020-07-01 16:17:07 +02:00
updateFileInfo ( video . duration , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
window . URL . revokeObjectURL ( video . src ) ;
} ;
2021-02-16 03:33:34 +01:00
video . onerror = ( ) => {
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
} ;
video . src = window . URL . createObjectURL ( file ) ;
} else {
2020-07-01 16:17:07 +02:00
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
}
2020-07-14 12:32:08 +02:00
} else {
updateFileInfo ( 0 , file . size , isVideo ) ;
2020-03-07 00:11:16 +01:00
}
2019-10-11 02:37:18 +02:00
2020-08-24 22:45:08 +02:00
if ( isTextPost ) {
2020-07-28 01:12:59 +02:00
// Create reader
const reader = new FileReader ( ) ;
// Handler for file reader
2020-08-11 04:08:03 +02:00
reader . addEventListener ( 'load' , handleFileReaderLoaded ) ;
2020-07-28 01:12:59 +02:00
// Read file contents
reader . readAsText ( file ) ;
setCurrentFileType ( 'text/markdown' ) ;
} else {
setPublishMode ( PUBLISH _MODES . FILE ) ;
}
2019-10-11 02:37:18 +02:00
// @if TARGET='web'
// we only need to enforce file sizes on 'web'
2021-04-14 06:06:11 +02:00
if ( file . size && Number ( file . size ) > TV _PUBLISH _SIZE _LIMIT _BYTES ) {
2020-05-25 16:27:36 +02:00
setOversized ( true ) ;
showToast ( _ _ ( UPLOAD _SIZE _MESSAGE ) ) ;
updatePublishForm ( { filePath : '' , name : '' } ) ;
return ;
2019-10-11 02:37:18 +02:00
}
// @endif
2019-12-09 19:51:00 +01:00
2020-03-24 18:57:17 +01:00
const publishFormParams : { filePath : string | WebFile , name ? : string , optimize ? : boolean } = {
2020-05-25 16:27:36 +02:00
// if electron, we'll set filePath to the path string because SDK is handling publishing.
// File.path will be undefined from web due to browser security, so it will default to the File Object.
2019-10-07 22:02:32 +02:00
filePath : file . path || file ,
} ;
2019-11-01 18:27:01 +01:00
// Strip off extention and replace invalid characters
2020-08-05 19:07:52 +02:00
let fileName = name || ( file . name && file . name . substr ( 0 , file . name . lastIndexOf ( '.' ) ) ) || '' ;
2020-07-30 00:55:48 +02:00
2019-12-14 20:35:55 +01:00
if ( ! isStillEditing ) {
2020-07-30 00:55:48 +02:00
publishFormParams . name = parseName ( fileName ) ;
2019-12-14 20:35:55 +01:00
}
2020-07-30 06:03:56 +02:00
2020-05-25 16:27:36 +02:00
// File path is not supported on web for security reasons so we use the name instead.
setCurrentFile ( file . path || file . name ) ;
2019-06-28 09:27:55 +02:00
updatePublishForm ( publishFormParams ) ;
}
2021-04-14 17:56:45 +02:00
const showFileUpload = mode === PUBLISH _MODES . FILE ;
2020-07-29 22:30:26 +02:00
const isPublishPost = mode === PUBLISH _MODES . POST ;
2020-07-28 01:12:59 +02:00
2019-06-28 09:27:55 +02:00
return (
2019-09-27 20:56:15 +02:00
< Card
2020-08-26 18:24:07 +02:00
className = { disabled || balance === 0 ? 'card--disabled' : '' }
2019-12-26 16:37:26 +01:00
title = {
2020-08-05 19:19:15 +02:00
< div >
2021-04-14 06:06:11 +02:00
{ header } { /* display mode buttons from parent */ }
2020-08-05 19:06:24 +02:00
{ publishing && < Spinner type = { 'small' } / > }
2020-08-05 19:19:15 +02:00
{ inProgress && (
< div >
2021-09-21 10:47:45 +02:00
< Button
button = "close"
label = { _ _ ( 'New --[clears Publish Form]--' ) }
icon = { ICONS . REFRESH }
onClick = { clearPublish }
/ >
2020-08-05 19:19:15 +02:00
< / div >
) }
< / div >
2019-12-26 16:37:26 +01:00
}
2021-04-14 06:06:11 +02:00
subtitle = { subtitle || ( isStillEditing && _ _ ( 'You are currently editing your upload.' ) ) }
2019-09-27 20:56:15 +02:00
actions = {
< React.Fragment >
2021-04-14 06:06:11 +02:00
< PublishName uri = { uri } / >
2020-03-24 18:57:17 +01:00
< FormField
2020-07-28 01:12:59 +02:00
type = "text"
name = "content_title"
label = { _ _ ( 'Title' ) }
placeholder = { _ _ ( 'Descriptive titles work best' ) }
disabled = { disabled }
value = { title }
2020-07-30 00:55:48 +02:00
onChange = { handleTitleChange }
2020-03-24 18:57:17 +01:00
/ >
2021-04-14 06:06:11 +02:00
{ /* Decide whether to show file upload or replay selector */ }
{ /* @if TARGET='web' */ }
< >
{ showSourceSelector && (
< fieldset - section >
< div className = "section__actions--between section__actions--align-bottom" >
< div >
2021-04-14 17:56:45 +02:00
< label > { _ _ ( 'Replay video available' ) } < / label >
2021-04-14 06:06:11 +02:00
< div className = "button-group" >
{ fileSelectorModes . map ( ( fmode ) => (
< Button
key = { fmode . label }
icon = { fmode . icon || undefined }
iconSize = { 18 }
label = { fmode . label }
button = "alt"
onClick = { ( ) => {
// $FlowFixMe
handleFileSource ( fmode . actionName ) ;
} }
className = { classnames ( 'button-toggle' , {
'button-toggle--active' : fileSelectSource === fmode . actionName ,
} ) }
/ >
) ) }
< / div >
< / div >
{ fileSelectSource === SOURCE _SELECT && (
< Button
button = "secondary"
label = { _ _ ( 'Check for Replays' ) }
disabled = { isCheckingLivestreams }
icon = { ICONS . REFRESH }
onClick = { ( ) =>
checkLivestreams ( channelId , channelSignature . signature , channelSignature . signing _ts )
}
/ >
) }
< / div >
< / f i e l d s e t - s e c t i o n >
) }
{ fileSelectSource === SOURCE _UPLOAD && showFileUpload && (
< >
< FileSelector
2021-07-21 22:15:56 +02:00
label = { _ _ ( 'File' ) }
2021-04-14 06:06:11 +02:00
disabled = { disabled }
currentPath = { currentFile }
onFileChosen = { handleFileChange }
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
2021-04-16 04:52:41 +02:00
accept = { SIMPLE _SITE ? 'video/mp4,video/x-m4v,video/*,audio/*' : undefined }
placeholder = { SIMPLE _SITE ? _ _ ( 'Select video or audio file to upload' ) : _ _ ( 'Select a file to upload' ) }
2021-04-14 06:06:11 +02:00
/ >
{ 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 >
< / f i e l d s e t - s e c t i o n >
< 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"
/ >
< / f i e l d s e t - s e c t i o n >
< / f i e l d s e t - g r o u p >
< / >
) }
{ fileSelectSource === SOURCE _SELECT && showFileUpload && ! hasLivestreamData && ! isCheckingLivestreams && (
< div className = "main--empty empty" >
< Empty text = { _ _ ( 'No replays found.' ) } / >
< / div >
) }
{ fileSelectSource === SOURCE _SELECT && showFileUpload && isCheckingLivestreams && (
< div className = "main--empty empty" >
< Spinner small / >
< / div >
) }
< / >
{ /* @endif */ }
{ /* @if TARGET='app' */ }
{ showFileUpload && (
2021-01-25 23:08:39 +01:00
< FileSelector
label = { _ _ ( 'File' ) }
disabled = { disabled }
currentPath = { currentFile }
onFileChosen = { handleFileChange }
2021-04-14 06:06:11 +02:00
// https://stackoverflow.com/questions/19107685/safari-input-type-file-accept-video-ignores-mp4-files
2021-04-14 19:45:45 +02:00
placeholder = { _ _ ( 'Select file to upload' ) }
2021-01-25 23:08:39 +01:00
/ >
2020-07-28 01:12:59 +02:00
) }
2021-04-14 06:06:11 +02:00
{ showFileUpload && (
2020-07-28 01:12:59 +02:00
< FormField
type = "checkbox"
checked = { userOptimize }
disabled = { ! optimizeAvail }
onChange = { ( ) => setUserOptimize ( ! userOptimize ) }
label = { _ _ ( 'Optimize and transcode video' ) }
name = "optimize"
/ >
) }
2021-04-14 06:06:11 +02:00
{ showFileUpload && ! ffmpegAvail && (
2020-03-24 18:57:17 +01:00
< p className = "help" >
< I18nMessage
tokens = { {
settings _link : < Button button = "link" navigate = "/$/settings" label = { _ _ ( 'Settings' ) } / > ,
} }
>
FFmpeg not configured . More in % settings _link % .
< / I18nMessage >
< / p >
) }
2021-04-14 06:06:11 +02:00
{ showFileUpload && Boolean ( size ) && ffmpegAvail && optimize && isVid && (
2020-03-24 18:57:17 +01:00
< p className = "help" >
< I18nMessage
tokens = { {
size : Math . ceil ( sizeInMB ) ,
processTime : getTimeForMB ( sizeInMB ) ,
units : getUnitsForMB ( sizeInMB ) ,
} }
>
2020-05-12 20:57:02 +02:00
Transcoding this % size % MB file should take under % processTime % % units % .
2020-03-24 18:57:17 +01:00
< / I18nMessage >
< / p >
) }
{ /* @endif */ }
2021-04-14 06:06:11 +02:00
{ isPublishPost && (
< PostEditor
label = { _ _ ( 'Post --[noun, markdown post tab button]--' ) }
uri = { uri }
disabled = { disabled }
fileMimeType = { fileMimeType }
setPrevFileText = { setPrevFileText }
setCurrentFileType = { setCurrentFileType }
/ >
) }
2019-09-27 20:56:15 +02:00
< / React.Fragment >
}
/ >
2019-06-28 09:27:55 +02:00
) ;
}
export default PublishFile ;