11import { Dialog , DialogProps } from "components/dialog" ;
22import {
3- Alert ,
43 Button ,
54 ExportIcon ,
6- Link ,
75 Text ,
86 majorScale ,
97 Paragraph ,
108 RecordIcon ,
119 Spinner ,
10+ Pane ,
11+ TextInput ,
12+ Label ,
13+ minorScale ,
1214} from "evergreen-ui" ;
1315import { List } from "immutable" ;
14- import React , { useCallback , useState } from "react" ;
16+ import React , { useCallback , useMemo , useState } from "react" ;
1517import { useListFiles } from "utils/hooks/domain/files/use-list-files" ;
1618import { useListInstruments } from "utils/hooks/domain/instruments/use-list-instruments" ;
1719import { useBoolean } from "utils/hooks/use-boolean" ;
1820import { useToneAudio } from "utils/hooks/use-tone-audio" ;
1921import { useWorkstationState } from "utils/hooks/use-workstation-state" ;
2022import * as Tone from "tone" ;
21- import { formatNow } from "utils/date-utils" ;
2223import { ProjectRecord } from "models/project-record" ;
2324import { useTheme } from "utils/hooks/use-theme" ;
25+ import { MimeType } from "enums/mime-type" ;
26+ import { enumToSelectMenuItems } from "utils/select-menu-utils" ;
27+ import { SelectMenu , SelectMenuItem } from "components/select-menu" ;
28+ import { getExtension } from "utils/mime-type-utils" ;
29+ import slugify from "slugify" ;
30+ import { unixTime } from "utils/core-utils" ;
31+ import { useInput } from "utils/hooks/use-input" ;
32+ import { isEmpty } from "lodash" ;
2433
2534interface ExportDialogProps
2635 extends Pick < DialogProps , "isShown" | "onCloseComplete" > { }
2736
37+ const options : Array < SelectMenuItem < MimeType > > = enumToSelectMenuItems (
38+ MimeType
39+ ) . map ( ( item ) => {
40+ const mimeType = item . value as MimeType ;
41+ return { ...item , label : getExtension ( mimeType ) ! } ;
42+ } ) as Array < SelectMenuItem < MimeType > > ;
43+
2844const title = "Export as Audio" ;
2945
3046const ExportDialog : React . FC < ExportDialogProps > = (
@@ -44,6 +60,12 @@ const ExportDialog: React.FC<ExportDialogProps> = (
4460 setTrue : startRecording ,
4561 setFalse : stopRecording ,
4662 } = useBoolean ( ) ;
63+
64+ const { value : fileName , ...inputProps } = useInput ( {
65+ initialValue : getDefaultFileName ( state . project ) ,
66+ isRequired : true ,
67+ } ) ;
68+ const [ mimeType , setMimeType ] = useState < MimeType > ( MimeType . WAV ) ;
4769 const [ blob , setBlob ] = useState < Blob > ( ) ;
4870 const handleRecordingComplete = useCallback (
4971 ( file : Blob ) => {
@@ -56,6 +78,7 @@ const ExportDialog: React.FC<ExportDialogProps> = (
5678 const { isLoading : isLoadingSamples } = useToneAudio ( {
5779 lengthInMs : state . getLengthInMs ( ) ,
5880 onRecordingComplete : handleRecordingComplete ,
81+ mimeType,
5982 isRecording,
6083 files,
6184 instruments,
@@ -77,6 +100,22 @@ const ExportDialog: React.FC<ExportDialogProps> = (
77100 stopRecording ( ) ;
78101 onCloseComplete ?.( ) ;
79102 } , [ onCloseComplete , stopRecording ] ) ;
103+
104+ const handleSelect = useCallback (
105+ ( item : SelectMenuItem < MimeType > ) => setMimeType ( item . value ) ,
106+ [ ]
107+ ) ;
108+
109+ const fullFileName = useMemo ( ( ) => {
110+ const extension = getExtension ( mimeType ) ! ;
111+ if ( isEmpty ( fileName ) ) {
112+ return `${ getDefaultFileName ( state . project ) } ${ extension } ` ;
113+ }
114+
115+ return `${ fileName } ${ extension } ` ;
116+ } , [ fileName , mimeType , state . project ] ) ;
117+ const hasFile = blob != null ;
118+
80119 return (
81120 < Dialog
82121 confirmLabel = "Close"
@@ -93,54 +132,72 @@ const ExportDialog: React.FC<ExportDialogProps> = (
93132 below. Recording happens in real-time, and you'll be
94133 able to download the file once complete.
95134 </ Paragraph >
96- < Alert
97- marginBottom = { majorScale ( 2 ) }
98- title = "The file will be exported as a .webm file" >
99- < Text >
100- Use a site like{ " " }
101- < Link
102- href = "https://cloudconvert.com/webm-converter"
103- target = "_blank" >
104- CloudConvert
105- </ Link > { " " }
106- to convert it to your desired format.
107- </ Text >
108- </ Alert >
135+ < Pane
136+ display = "flex"
137+ flexDirection = "row"
138+ marginBottom = { majorScale ( 2 ) } >
139+ < Pane
140+ display = "flex"
141+ flexDirection = "column"
142+ marginRight = { majorScale ( 1 ) }
143+ width = "88%" >
144+ < Label marginBottom = { minorScale ( 1 ) } > Name</ Label >
145+ < TextInput
146+ { ...inputProps }
147+ value = { fileName }
148+ width = "100%"
149+ />
150+ </ Pane >
151+ < Pane display = "flex" flexDirection = "column" width = "12%" >
152+ < Label marginBottom = { minorScale ( 1 ) } > Type</ Label >
153+ < SelectMenu
154+ calculateHeight = { true }
155+ closeOnSelect = { true }
156+ hasFilter = { false }
157+ isMultiSelect = { false }
158+ onSelect = { handleSelect }
159+ options = { options }
160+ title = "Type"
161+ width = { majorScale ( 16 ) } >
162+ < Button disabled = { hasFile || isRecording } >
163+ { getExtension ( mimeType ) }
164+ </ Button >
165+ </ SelectMenu >
166+ </ Pane >
167+ </ Pane >
109168 < Button
110169 allowUnsafeHref = { true }
111- appearance = { blob == null ? "default" : "primary" }
112- download = { getFileName ( state . project ) }
113- href = {
114- blob != null ? URL . createObjectURL ( blob ) : undefined
115- }
170+ appearance = { hasFile ? "primary" : "default" }
171+ download = { fullFileName }
172+ href = { hasFile ? URL . createObjectURL ( blob ) : undefined }
116173 iconBefore = {
117- blob == null ? (
174+ hasFile ? (
175+ ExportIcon
176+ ) : (
118177 < RecordIcon
119178 color = {
120179 isRecording ? colors . red600 : undefined
121180 }
122181 />
123- ) : (
124- ExportIcon
125182 )
126183 }
127184 is = "a"
128185 isLoading = { isRecording }
129- onClick = { blob == null ? handleExportClick : undefined }
186+ onClick = { hasFile ? undefined : handleExportClick }
130187 width = "100%" >
131- { blob == null && ! isRecording && "Export" }
188+ { ! hasFile && ! isRecording && "Export" }
132189 { isRecording && (
133190 < Text color = { colors . red600 } > Recording...</ Text >
134191 ) }
135- { blob != null && ! isRecording && "Download file" }
192+ { hasFile && ! isRecording && "Download file" }
136193 </ Button >
137194 </ React . Fragment >
138195 ) }
139196 </ Dialog >
140197 ) ;
141198} ;
142199
143- const getFileName = ( project : ProjectRecord ) : string =>
144- `${ project . name } ${ formatNow ( ) } .webm` ;
200+ const getDefaultFileName = ( project : ProjectRecord ) : string =>
201+ slugify ( `${ project . name } ${ project . bpm } BPM ${ unixTime ( ) } ` ) ;
145202
146203export { ExportDialog } ;
0 commit comments