2023-03-04 15:50:06 +01:00
import React , { memo , useCallback , useEffect , useMemo , useState } from 'react' ;
2022-02-13 10:21:51 +01:00
import { motion } from 'framer-motion' ;
import { MdRotate90DegreesCcw } from 'react-icons/md' ;
import { useTranslation } from 'react-i18next' ;
import { IoIosCamera , IoMdKey } from 'react-icons/io' ;
2023-03-10 05:12:48 +01:00
import { FaYinYang , FaTrashAlt , FaStepBackward , FaStepForward , FaCaretLeft , FaCaretRight , FaPause , FaPlay , FaImages , FaKey , FaSun } from 'react-icons/fa' ;
2022-02-13 10:21:51 +01:00
import { GiSoundWaves } from 'react-icons/gi' ;
// import useTraceUpdate from 'use-trace-update';
2023-03-10 05:12:48 +01:00
import { primaryTextColor , primaryColor , darkModeTransition } from './colors' ;
2022-02-13 10:21:51 +01:00
import SegmentCutpointButton from './components/SegmentCutpointButton' ;
import SetCutpointButton from './components/SetCutpointButton' ;
import ExportButton from './components/ExportButton' ;
import ToggleExportConfirm from './components/ToggleExportConfirm' ;
2022-03-01 06:53:44 +01:00
import CaptureFormatButton from './components/CaptureFormatButton' ;
2023-03-10 05:12:48 +01:00
import Select from './components/Select' ;
2022-02-13 10:21:51 +01:00
import SimpleModeButton from './components/SimpleModeButton' ;
2023-02-16 06:14:15 +01:00
import { withBlur , mirrorTransform , checkAppPath } from './util' ;
import { toast } from './swal' ;
2022-02-13 10:21:51 +01:00
import { getSegColor } from './util/colors' ;
2023-03-04 15:50:06 +01:00
import { formatDuration , parseDuration , isExactDurationMatch } from './util/duration' ;
2022-03-01 06:53:44 +01:00
import useUserSettings from './hooks/useUserSettings' ;
2022-02-13 10:21:51 +01:00
2023-03-04 15:50:06 +01:00
const { clipboard } = window . require ( 'electron' ) ;
2022-02-13 10:21:51 +01:00
const zoomOptions = Array ( 13 ) . fill ( ) . map ( ( unused , z ) => 2 * * z ) ;
const leftRightWidth = 100 ;
2023-03-10 05:12:48 +01:00
const CutTimeInput = memo ( ( { darkMode , cutTime , setCutTime , startTimeOffset , seekAbs , currentCutSeg , currentApparentCutSeg , isStart } ) => {
2023-03-04 15:50:06 +01:00
const { t } = useTranslation ( ) ;
const [ cutTimeManual , setCutTimeManual ] = useState ( ) ;
// Clear manual overrides if upstream cut time has changed
useEffect ( ( ) => {
setCutTimeManual ( ) ;
} , [ setCutTimeManual , currentApparentCutSeg . start , currentApparentCutSeg . end ] ) ;
const isCutTimeManualSet = ( ) => cutTimeManual !== undefined ;
2023-03-10 05:12:48 +01:00
const border = ` .15em solid ${ getSegColor ( currentCutSeg , darkMode ) . alpha ( 0.8 ) . string ( ) } ` ;
2023-03-04 15:50:06 +01:00
const cutTimeInputStyle = {
2023-03-10 05:12:48 +01:00
border , borderRadius : 5 , backgroundColor : 'var(--gray5)' , transition : darkModeTransition , fontSize : 13 , textAlign : 'center' , padding : '1px 5px' , marginTop : 0 , marginBottom : 0 , marginLeft : isStart ? 0 : 5 , marginRight : isStart ? 5 : 0 , boxSizing : 'border-box' , fontFamily : 'inherit' , width : 90 , outline : 'none' ,
2023-03-04 15:50:06 +01:00
} ;
const trySetTime = useCallback ( ( timeWithOffset ) => {
const timeWithoutOffset = Math . max ( timeWithOffset - startTimeOffset , 0 ) ;
try {
setCutTime ( isStart ? 'start' : 'end' , timeWithoutOffset ) ;
seekAbs ( timeWithoutOffset ) ;
setCutTimeManual ( ) ;
} catch ( err ) {
console . error ( 'Cannot set cut time' , err ) ;
// If we get an error from setCutTime, remain in the editing state (cutTimeManual)
// https://github.com/mifi/lossless-cut/issues/988
}
} , [ isStart , seekAbs , setCutTime , startTimeOffset ] ) ;
const handleSubmit = useCallback ( ( e ) => {
e . preventDefault ( ) ;
// Don't proceed if not a valid time value
const timeWithOffset = parseDuration ( cutTimeManual ) ;
if ( timeWithOffset === undefined ) return ;
trySetTime ( timeWithOffset ) ;
} , [ cutTimeManual , trySetTime ] ) ;
const parseAndSetCutTime = useCallback ( ( text ) => {
// Don't proceed if not a valid time value
const timeWithOffset = parseDuration ( text ) ;
if ( timeWithOffset === undefined ) return ;
trySetTime ( timeWithOffset ) ;
} , [ trySetTime ] ) ;
function handleCutTimeInput ( text ) {
setCutTimeManual ( text ) ;
if ( isExactDurationMatch ( text ) ) parseAndSetCutTime ( text ) ;
}
const tryPaste = useCallback ( ( clipboardText ) => {
try {
setCutTimeManual ( clipboardText ) ;
parseAndSetCutTime ( clipboardText ) ;
} catch ( err ) {
console . error ( err ) ;
}
} , [ parseAndSetCutTime ] ) ;
const handleCutTimePaste = useCallback ( ( e ) => {
e . preventDefault ( ) ;
try {
const clipboardData = e . clipboardData . getData ( 'Text' ) ;
setCutTimeManual ( clipboardData ) ;
parseAndSetCutTime ( clipboardData ) ;
} catch ( err ) {
console . error ( err ) ;
}
} , [ parseAndSetCutTime ] ) ;
const handleContextMenu = useCallback ( ( ) => {
const text = clipboard . readText ( ) ;
if ( text ) tryPaste ( text ) ;
} , [ tryPaste ] ) ;
return (
< form onSubmit = { handleSubmit } >
< input
2023-03-10 05:12:48 +01:00
style = { { ... cutTimeInputStyle , color : isCutTimeManualSet ( ) ? 'var(--red11)' : 'var(--gray12)' } }
2023-03-04 15:50:06 +01:00
type = "text"
title = { isStart ? t ( 'Manually input current segment\'s start time' ) : t ( 'Manually input current segment\'s end time' ) }
onChange = { e => handleCutTimeInput ( e . target . value ) }
onPaste = { handleCutTimePaste }
onBlur = { ( ) => setCutTimeManual ( ) }
onContextMenu = { handleContextMenu }
value = { isCutTimeManualSet ( )
? cutTimeManual
: formatDuration ( { seconds : cutTime + startTimeOffset } ) }
/ >
< / form >
) ;
} ) ;
2022-02-13 10:21:51 +01:00
const BottomBar = memo ( ( {
2022-03-01 06:53:44 +01:00
zoom , setZoom , timelineToggleComfortZoom ,
isRotationSet , rotation , areWeCutting , increaseRotation , cleanupFilesDialog ,
2022-03-17 16:51:34 +01:00
captureSnapshot , onExportPress , segmentsToExport , hasVideo ,
2022-02-13 10:21:51 +01:00
seekAbs , currentSegIndexSafe , cutSegments , currentCutSeg , setCutStart , setCutEnd ,
2023-03-04 15:50:06 +01:00
setCurrentSegIndex ,
implement hotkeys
jumpTimelineStart (ctrl+home)
jumpTimelineEnd (ctrl+end)
reorderSegsByStartTime,
invertAllCutSegments,
createFixedDurationSegments,
createNumSegments,
shuffleSegments,
clearSegments,
toggleSegmentsList,
toggleStreamsSelector,
extractAllStreams,
convertFormatCurrentFile,
convertFormatBatch,
concatBatch,
toggleKeyframeCutMode,
toggleCaptureFormat,
toggleStripAudio,
setStartTimeOffset,
2022-02-20 11:34:10 +01:00
jumpTimelineStart , jumpTimelineEnd , jumpCutEnd , jumpCutStart , startTimeOffset , setCutTime , currentApparentCutSeg ,
2023-03-12 09:51:15 +01:00
playing , shortStep , togglePlay , toggleLoopSelectedSegments , hasAudio ,
2023-02-17 10:12:15 +01:00
keyframesEnabled , toggleKeyframesEnabled , seekClosestKeyframe , detectedFps , isFileOpened , selectedSegments ,
2023-03-10 05:12:48 +01:00
darkMode , setDarkMode ,
2023-03-12 09:51:15 +01:00
toggleEnableThumbnails , toggleWaveformMode , waveformMode , showThumbnails ,
2022-02-13 10:21:51 +01:00
} ) => {
const { t } = useTranslation ( ) ;
2023-02-17 10:12:15 +01:00
// ok this is a bit over-engineered but what the hell!
const loopSelectedSegmentsButtonStyle = useMemo ( ( ) => {
// cannot have less than 1 gradient element:
const selectedSegmentsSafe = ( selectedSegments . length > 1 ? selectedSegments : [ selectedSegments [ 0 ] , selectedSegments [ 0 ] ] ) . slice ( 0 , 10 ) ;
const gradientColors = selectedSegmentsSafe . map ( ( seg , i ) => {
2023-03-10 05:12:48 +01:00
const segColor = getSegColor ( seg , darkMode ) ;
2023-02-17 10:12:15 +01:00
// make colors stronger, the more segments
return ` ${ segColor . alpha ( Math . max ( 0.4 , Math . min ( 0.8 , selectedSegmentsSafe . length / 3 ) ) ) . string ( ) } ${ ( ( i / ( selectedSegmentsSafe . length - 1 ) ) * 100 ) . toFixed ( 1 ) } % ` ;
} ) . join ( ', ' ) ;
return {
paddingLeft : 2 ,
backgroundOffset : 30 ,
background : ` linear-gradient(90deg, ${ gradientColors } ) ` ,
2023-03-10 05:12:48 +01:00
border : '1px solid var(--gray8)' ,
color : 'white' ,
2023-02-17 10:12:15 +01:00
margin : '2px 4px 0 0px' ,
display : 'flex' ,
alignItems : 'center' ,
justifyContent : 'center' ,
width : 20 ,
height : 24 ,
borderRadius : 4 ,
} ;
2023-03-10 05:12:48 +01:00
} , [ darkMode , selectedSegments ] ) ;
2023-02-17 10:12:15 +01:00
2022-09-29 13:20:24 +02:00
const { invertCutSegments , setInvertCutSegments , simpleMode , toggleSimpleMode , exportConfirmEnabled } = useUserSettings ( ) ;
2022-03-01 06:53:44 +01:00
2022-02-13 10:21:51 +01:00
const onYinYangClick = useCallback ( ( ) => {
setInvertCutSegments ( v => {
const newVal = ! v ;
if ( newVal ) toast . fire ( { title : t ( 'When you export, selected segments on the timeline will be REMOVED - the surrounding areas will be KEPT' ) } ) ;
else toast . fire ( { title : t ( 'When you export, selected segments on the timeline will be KEPT - the surrounding areas will be REMOVED.' ) } ) ;
return newVal ;
} ) ;
} , [ setInvertCutSegments , t ] ) ;
const rotationStr = ` ${ rotation } ° ` ;
2023-01-02 10:17:41 +01:00
useEffect ( ( ) => {
checkAppPath ( ) ;
} , [ ] ) ;
2022-02-13 10:21:51 +01:00
function renderJumpCutpointButton ( direction ) {
const newIndex = currentSegIndexSafe + direction ;
const seg = cutSegments [ newIndex ] ;
2023-03-10 05:12:48 +01:00
const backgroundColor = seg && getSegColor ( seg , darkMode ) . alpha ( 0.5 ) . string ( ) ;
const opacity = seg ? undefined : 0.5 ;
2022-02-18 15:14:16 +01:00
const text = seg ? ` ${ newIndex + 1 } ` : '-' ;
const wide = text . length > 1 ;
2022-02-13 10:21:51 +01:00
const segButtonStyle = {
2023-03-12 09:51:15 +01:00
backgroundColor , opacity , padding : ` 6px ${ wide ? 4 : 6 } px ` , borderRadius : 10 , color : seg ? 'white' : undefined , fontSize : wide ? 12 : 14 , width : 20 , boxSizing : 'border-box' , letterSpacing : - 1 , lineHeight : '10px' , fontWeight : 'bold' , margin : '0 6px' ,
2022-02-13 10:21:51 +01:00
} ;
return (
< div
style = { segButtonStyle }
role = "button"
title = { ` ${ direction > 0 ? t ( 'Select next segment' ) : t ( 'Select previous segment' ) } ( ${ newIndex + 1 } ) ` }
onClick = { ( ) => seg && setCurrentSegIndex ( newIndex ) }
>
2022-02-18 15:14:16 +01:00
{ text }
2022-02-13 10:21:51 +01:00
< / div >
) ;
}
const PlayPause = playing ? FaPause : FaPlay ;
return (
< >
< div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'center' } } >
< div style = { { display : 'flex' , alignItems : 'center' , flexBasis : leftRightWidth } } >
2022-02-18 10:42:35 +01:00
{ ! simpleMode && (
2022-02-13 10:21:51 +01:00
< >
2023-03-10 05:12:48 +01:00
< FaSun color = "var(--gray12)" role = "button" onClick = { ( ) => setDarkMode ( ( v ) => ! v ) } style = { { padding : '0 .2em 0 .3em' } } / >
2022-02-18 10:42:35 +01:00
{ hasAudio && (
< GiSoundWaves
size = { 24 }
2023-03-12 09:51:15 +01:00
style = { { padding : '0 .1em' , color : [ 'big-waveform' , 'waveform' ] . includes ( waveformMode ) ? primaryTextColor : undefined } }
2022-02-18 10:42:35 +01:00
role = "button"
title = { t ( 'Show waveform' ) }
2023-03-12 09:51:15 +01:00
onClick = { ( ) => toggleWaveformMode ( 'waveform' ) }
2022-02-18 10:42:35 +01:00
/ >
) }
{ hasVideo && (
< >
< FaImages
size = { 20 }
2023-03-12 09:51:15 +01:00
style = { { padding : '0 .2em' , color : showThumbnails ? primaryTextColor : undefined } }
2022-02-18 10:42:35 +01:00
role = "button"
title = { t ( 'Show thumbnails' ) }
2023-03-12 09:51:15 +01:00
onClick = { toggleEnableThumbnails }
2022-02-18 10:42:35 +01:00
/ >
< FaKey
size = { 16 }
2023-03-10 05:12:48 +01:00
style = { { padding : '0 .2em' , color : keyframesEnabled ? primaryTextColor : undefined } }
2022-02-18 10:42:35 +01:00
role = "button"
title = { t ( 'Show keyframes' ) }
onClick = { toggleKeyframesEnabled }
/ >
< / >
) }
2022-02-13 10:21:51 +01:00
< / >
) }
< / div >
< div style = { { flexGrow : 1 } } / >
{ ! simpleMode && (
2022-02-18 10:42:35 +01:00
< >
< FaStepBackward
size = { 16 }
2023-02-17 10:12:15 +01:00
style = { { flexShrink : 0 } }
2022-02-18 10:42:35 +01:00
title = { t ( 'Jump to start of video' ) }
role = "button"
implement hotkeys
jumpTimelineStart (ctrl+home)
jumpTimelineEnd (ctrl+end)
reorderSegsByStartTime,
invertAllCutSegments,
createFixedDurationSegments,
createNumSegments,
shuffleSegments,
clearSegments,
toggleSegmentsList,
toggleStreamsSelector,
extractAllStreams,
convertFormatCurrentFile,
convertFormatBatch,
concatBatch,
toggleKeyframeCutMode,
toggleCaptureFormat,
toggleStripAudio,
setStartTimeOffset,
2022-02-20 11:34:10 +01:00
onClick = { jumpTimelineStart }
2022-02-18 10:42:35 +01:00
/ >
2022-02-13 10:21:51 +01:00
2022-02-18 10:42:35 +01:00
{ renderJumpCutpointButton ( - 1 ) }
2022-03-17 17:11:11 +01:00
< SegmentCutpointButton currentCutSeg = { currentCutSeg } side = "start" Icon = { FaStepBackward } onClick = { jumpCutStart } title = { t ( 'Jump to current segment\'s start time' ) } style = { { marginRight : 5 } } / >
2022-02-18 10:42:35 +01:00
< / >
) }
2022-02-13 10:21:51 +01:00
2022-03-17 17:11:11 +01:00
< SetCutpointButton currentCutSeg = { currentCutSeg } side = "start" onClick = { setCutStart } title = { t ( 'Start current segment at current time' ) } style = { { marginRight : 5 } } / >
2022-02-13 10:21:51 +01:00
2023-03-10 05:12:48 +01:00
{ ! simpleMode && < CutTimeInput darkMode = { darkMode } currentCutSeg = { currentCutSeg } currentApparentCutSeg = { currentApparentCutSeg } startTimeOffset = { startTimeOffset } seekAbs = { seekAbs } cutTime = { currentApparentCutSeg . start } setCutTime = { setCutTime } isStart / > }
2022-02-13 10:21:51 +01:00
< IoMdKey
size = { 25 }
role = "button"
title = { t ( 'Seek previous keyframe' ) }
style = { { flexShrink : 0 , marginRight : 2 , transform : mirrorTransform } }
onClick = { ( ) => seekClosestKeyframe ( - 1 ) }
/ >
{ ! simpleMode && (
< FaCaretLeft
style = { { flexShrink : 0 , marginLeft : - 6 , marginRight : - 4 } }
size = { 28 }
role = "button"
title = { t ( 'One frame back' ) }
onClick = { ( ) => shortStep ( - 1 ) }
/ >
) }
2023-03-10 05:12:48 +01:00
< div role = "button" onClick = { ( ) => togglePlay ( ) } style = { { background : primaryColor , margin : '2px 5px 0 5px' , display : 'flex' , alignItems : 'center' , justifyContent : 'center' , width : 34 , height : 34 , borderRadius : 17 , color : 'white' } } >
2022-02-13 10:21:51 +01:00
< PlayPause
2023-02-17 10:12:15 +01:00
style = { { paddingLeft : playing ? 0 : 2 } }
2022-02-13 10:21:51 +01:00
size = { 16 }
/ >
< / div >
2023-02-17 10:12:15 +01:00
< div role = "button" onClick = { toggleLoopSelectedSegments } title = { t ( 'Play selected segments in order' ) } style = { loopSelectedSegmentsButtonStyle } >
< FaPlay
size = { 14 }
/ >
< / div >
2022-02-13 10:21:51 +01:00
{ ! simpleMode && (
< FaCaretRight
style = { { flexShrink : 0 , marginRight : - 6 , marginLeft : - 4 } }
size = { 28 }
role = "button"
title = { t ( 'One frame forward' ) }
onClick = { ( ) => shortStep ( 1 ) }
/ >
) }
< IoMdKey
style = { { flexShrink : 0 , marginLeft : 2 } }
size = { 25 }
role = "button"
title = { t ( 'Seek next keyframe' ) }
onClick = { ( ) => seekClosestKeyframe ( 1 ) }
/ >
2023-03-10 05:12:48 +01:00
{ ! simpleMode && < CutTimeInput darkMode = { darkMode } currentCutSeg = { currentCutSeg } currentApparentCutSeg = { currentApparentCutSeg } startTimeOffset = { startTimeOffset } seekAbs = { seekAbs } cutTime = { currentApparentCutSeg . end } setCutTime = { setCutTime } / > }
2022-02-13 10:21:51 +01:00
2022-03-17 17:11:11 +01:00
< SetCutpointButton currentCutSeg = { currentCutSeg } side = "end" onClick = { setCutEnd } title = { t ( 'End current segment at current time' ) } style = { { marginLeft : 5 } } / >
2022-02-13 10:21:51 +01:00
{ ! simpleMode && (
2022-02-18 10:42:35 +01:00
< >
2022-03-17 17:11:11 +01:00
< SegmentCutpointButton currentCutSeg = { currentCutSeg } side = "end" Icon = { FaStepForward } onClick = { jumpCutEnd } title = { t ( 'Jump to current segment\'s end time' ) } style = { { marginLeft : 5 } } / >
2022-02-18 10:42:35 +01:00
{ renderJumpCutpointButton ( 1 ) }
< FaStepForward
size = { 16 }
2023-02-17 10:12:15 +01:00
style = { { flexShrink : 0 } }
2022-02-18 10:42:35 +01:00
title = { t ( 'Jump to end of video' ) }
role = "button"
implement hotkeys
jumpTimelineStart (ctrl+home)
jumpTimelineEnd (ctrl+end)
reorderSegsByStartTime,
invertAllCutSegments,
createFixedDurationSegments,
createNumSegments,
shuffleSegments,
clearSegments,
toggleSegmentsList,
toggleStreamsSelector,
extractAllStreams,
convertFormatCurrentFile,
convertFormatBatch,
concatBatch,
toggleKeyframeCutMode,
toggleCaptureFormat,
toggleStripAudio,
setStartTimeOffset,
2022-02-20 11:34:10 +01:00
onClick = { jumpTimelineEnd }
2022-02-18 10:42:35 +01:00
/ >
< / >
2022-02-13 10:21:51 +01:00
) }
< div style = { { flexGrow : 1 } } / >
< div style = { { flexBasis : leftRightWidth } } / >
< / div >
< div
className = "no-user-select"
style = { { display : 'flex' , alignItems : 'center' , justifyContent : 'space-between' , padding : '3px 4px' } }
>
2022-03-01 06:53:44 +01:00
< SimpleModeButton style = { { flexShrink : 0 } } / >
2022-02-13 10:21:51 +01:00
{ simpleMode && < div role = "button" onClick = { toggleSimpleMode } style = { { marginLeft : 5 , fontSize : '90%' } } > { t ( 'Toggle advanced view' ) } < / div > }
{ ! simpleMode && (
< >
2022-02-18 10:42:35 +01:00
< div style = { { marginLeft : 5 } } >
< motion.div
style = { { width : 24 , height : 24 } }
animate = { { rotateX : invertCutSegments ? 0 : 180 } }
transition = { { duration : 0.3 } }
>
< FaYinYang
size = { 24 }
role = "button"
title = { invertCutSegments ? t ( 'Discard selected segments' ) : t ( 'Keep selected segments' ) }
2022-03-17 16:28:37 +01:00
style = { { color : invertCutSegments ? primaryTextColor : undefined } }
2022-02-18 10:42:35 +01:00
onClick = { onYinYangClick }
/ >
< / motion.div >
< / div >
2022-02-20 10:23:18 +01:00
< div role = "button" style = { { marginRight : 5 , marginLeft : 10 } } title = { t ( 'Zoom' ) } onClick = { timelineToggleComfortZoom } > { Math . floor ( zoom ) } x < / div >
2022-02-13 10:21:51 +01:00
2023-03-10 05:12:48 +01:00
< Select style = { { height : 20 , flexBasis : 85 , flexGrow : 0 } } value = { zoomOptions . includes ( zoom ) ? zoom . toString ( ) : '' } title = { t ( 'Zoom' ) } onChange = { withBlur ( e => setZoom ( parseInt ( e . target . value , 10 ) ) ) } >
2022-02-13 10:21:51 +01:00
< option key = "" value = "" disabled > { t ( 'Zoom' ) } < / option >
{ zoomOptions . map ( val => (
< option key = { val } value = { String ( val ) } > { t ( 'Zoom' ) } { val } x < / option >
) ) }
< / Select >
2022-02-18 10:42:35 +01:00
2023-03-10 05:12:48 +01:00
{ detectedFps != null && < div title = { t ( 'Video FPS' ) } style = { { color : 'var(--gray11)' , fontSize : '.7em' , marginLeft : 6 } } > { detectedFps . toFixed ( 3 ) } < / div > }
2022-02-13 10:21:51 +01:00
< / >
) }
< div style = { { flexGrow : 1 } } / >
{ hasVideo && (
< >
< span style = { { textAlign : 'right' , display : 'inline-block' } } > { isRotationSet && rotationStr } < / span >
< MdRotate90DegreesCcw
size = { 24 }
style = { { margin : '0px 0px 0 2px' , verticalAlign : 'middle' , color : isRotationSet ? primaryTextColor : undefined } }
title = { ` ${ t ( 'Set output rotation. Current: ' ) } ${ isRotationSet ? rotationStr : t ( 'Don\'t modify' ) } ` }
onClick = { increaseRotation }
role = "button"
/ >
< / >
) }
2023-02-17 10:12:15 +01:00
{ ! simpleMode && isFileOpened && (
2022-02-13 10:21:51 +01:00
< FaTrashAlt
title = { t ( 'Close file and clean up' ) }
style = { { padding : '5px 10px' } }
size = { 16 }
2022-02-20 10:23:18 +01:00
onClick = { cleanupFilesDialog }
2022-02-13 10:21:51 +01:00
role = "button"
/ >
) }
{ hasVideo && (
< >
2022-03-01 06:53:44 +01:00
{ ! simpleMode && < CaptureFormatButton height = { 20 } / > }
2022-02-13 10:21:51 +01:00
< IoIosCamera
style = { { paddingLeft : 5 , paddingRight : 15 } }
size = { 25 }
title = { t ( 'Capture frame' ) }
2022-02-20 10:23:18 +01:00
onClick = { captureSnapshot }
2022-02-13 10:21:51 +01:00
/ >
< / >
) }
2022-09-29 13:20:24 +02:00
{ ( ! simpleMode || ! exportConfirmEnabled ) && < ToggleExportConfirm style = { { marginRight : 5 } } / > }
2022-02-13 10:21:51 +01:00
2022-03-17 16:51:34 +01:00
< ExportButton size = { 1.3 } segmentsToExport = { segmentsToExport } areWeCutting = { areWeCutting } onClick = { onExportPress } / >
2022-02-13 10:21:51 +01:00
< / div >
< / >
) ;
} ) ;
export default BottomBar ;