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' ;
2019-06-28 09:27:55 +02:00
import { regexInvalidURI } from 'lbry-redux' ;
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 ) ;
2020-10-05 20:24:57 +02:00
const UPLOAD _SIZE _MESSAGE = _ _ (
2021-02-11 06:25:44 +01:00
'%SITE_NAME% uploads are limited to %limit% GB. Download the app for unrestricted publishing.' ,
2021-04-14 06:06:11 +02:00
{ SITE _NAME , limit : TV _PUBLISH _SIZE _LIMIT _GB _STR }
2020-10-05 20:24:57 +02:00
) ;
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.' ) } { ' ' }
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 >
) ;
}
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
) } { ' ' }
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 >
) ;
}
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
) } { ' ' }
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
}
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-03-26 22:03:52 +01:00
< Button button = "close" label = { _ _ ( 'New' ) } 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-04-16 04:52:41 +02:00
label = { SIMPLE _SITE ? _ _ ( 'Video/audio file' ) : _ _ ( '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
odysee
fix replay select styling
make meme a link
Fix audio references
get newest livestream claim in livestreamLink
pin crackermilk
fix livestream banner placement
fix live page
fix rebase
fix rebase
fix error nag
fix darkmode blockquote style
break word on livestream comment text
fix dark mode snack
fix live badge
fix lint
small fixes - word wrap, live badge
wip
Fix invisible snack in Odysee Light Theme
Revert "wip"
This reverts commit d17e477fe0e6633709ea30bdc403448825db4c71.
Revert "small fixes - word wrap, live badge"
This reverts commit 0e431d4038d774079c78f0de32238aac7260e4ca.
fix blank
pinned destiny
fix badges and homepage again
only get livestreams live for less than a day
pinned hammy and olivia
multi pin
pin destiny
updated pinned videos
update tagline
Update view.jsx
pins
updated destiny's video
updated pinned videos
removed destiny, added lie likes music
pinned destiny and mason's woodshop
removed hammy and olivia
unpinned mason's woodshop
removed pins
added hammy and olivia
pinned sam seder
unpinned destiny and hammy and olivia
Fix merge on ChannelThumbnails
- sam seder, + hammy & olivia and passion for food
update tagline (#6086)
removed everyone, added kona and suba
Theme color fixes (odysee) (#6089)
* Cherry-pick master's 'base-theme.scss'
* Non-functional cleanup (remove dups, re-order, etc.)
* Dark: update positive Toast to --color-primary as well.
This follows the intention of the refactoring, which I guess was (1) reduce the number of color names (2) reduce the number of customizations needed.
The only issue I have with this is that the current Odysee primary color is pink, which can be intepreted as an error.
The original (pre-refactoring color was green).
For now, I follow the refactoring path. We can tweak this later.
* Fix text color inside '--color-card-background-highlighted'
Light: use base-theme (it was the same value anyway).
Dark: use bright text.
* Dark: add some contrast between the components
The color for the background, header, card, placeholder, etc. is almost identical -- it looks like there are all in the same component. The almost-invisible border doesn't help. One would have to crank up the monitor's contrast setting to see separation of components.
Brighten up the components a bit, somewhat following the same scale as lbry.tv's dark theme.
Overall, I still think it's too dark. The Card's background color can still be brightened up further for better contrast, but I try not to make too drastic of a change for now.
The original lbry.tv's gray theme is the most pleasant theme I've seen so far, but this is all subjective.
changed pins
removed kona and suba
added destiny
changed pins
removed destiny
pinned sgtducky
changed pins
removed sgtducky
added hammy and olivia
added chrissie mayr
added the bite shot
changed pins
removed the bite shot
added heads of tech
changed pins
removed hammy and olivia
removed chrissie mayr
changed pins
removed heads of tech
added crackermilk
changed pins
removed crackermilk
added some ordinary gamer
added passion for food
changed pins
removed some ordinary gamers
removed passion for food
added emmy hucker
changed pins
added game knights
Update view.jsx
Force rebuild
changed pins
removed emmy hucker
changed pins
removed game knights
added crackermilk
changed pins
removed crackermilk
added some ordinary gamer
changed pins
removed some ordinary gamers
added passion for food
added green renaissance
changed pins
removed passion for food
removed green renaissance
added expand love
changed pins
removed expand love
added dr nora
change tagline (#6122)
there's so much room for activities
comment out music
changed pins
removed dr nora
added kona and suba
changed pins
removed kona and suba
added destiny
changed pins
removed destiny
added crackermilk
changed pins
removed crackermilk
added someordinarygamers
change tagline
Drake, where's the door hole?
changed pins
unpinned someordinarygamers
pinned kona and suba
Add message for mature content
changed pin
changed pins
removed creative model
changed pins
added bcpov
added krish mohan
added cigarvixen
changed pins
removed krish mohan
added adrian logan
bump
fix footer
change tagline
just like the simulations
changed pins
removed:
bcpov
cigarvixen
adrian logan
added:
someordinarygamers
quick fix for reposts
oops
fix channel tabs
changed pin
removed someordinarygamers
added kona and suba
changed pins
removed kona and suba
added dirtyworkz
added crackermilk
2021-06-11 19:47:56 +02:00
label = { _ _ ( 'Video file' ) }
2021-01-25 23:08:39 +01:00
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 ;