CameraScreen.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /* eslint-disable jsx-a11y/media-has-caption */
  2. import type { I18n } from '@uppy/utils/lib/Translator'
  3. import { h, Component, type ComponentChild } from 'preact'
  4. import type { HTMLAttributes } from 'preact/compat'
  5. import SnapshotButton from './SnapshotButton.tsx'
  6. import RecordButton from './RecordButton.tsx'
  7. import RecordingLength from './RecordingLength.tsx'
  8. import VideoSourceSelect, {
  9. type VideoSourceSelectProps,
  10. } from './VideoSourceSelect.tsx'
  11. import SubmitButton from './SubmitButton.tsx'
  12. import DiscardButton from './DiscardButton.tsx'
  13. function isModeAvailable<T>(modes: T[], mode: any): mode is T {
  14. return modes.includes(mode)
  15. }
  16. interface CameraScreenProps extends VideoSourceSelectProps {
  17. onFocus: () => void
  18. onStop: () => void
  19. src: MediaStream | null
  20. recording: boolean
  21. recordedVideo: string | null
  22. modes: string[]
  23. supportsRecording: boolean
  24. showVideoSourceDropdown: boolean
  25. showRecordingLength: boolean
  26. onSubmit: () => void
  27. i18n: I18n
  28. mirror: boolean
  29. onSnapshot: () => void
  30. onStartRecording: () => void
  31. onStopRecording: () => void
  32. onDiscardRecordedVideo: () => void
  33. recordingLengthSeconds: number
  34. }
  35. class CameraScreen extends Component<CameraScreenProps> {
  36. private videoElement: HTMLVideoElement
  37. refs: any
  38. componentDidMount(): void {
  39. const { onFocus } = this.props
  40. onFocus()
  41. }
  42. componentWillUnmount(): void {
  43. const { onStop } = this.props
  44. onStop()
  45. }
  46. render(): ComponentChild {
  47. const {
  48. src,
  49. recordedVideo,
  50. recording,
  51. modes,
  52. supportsRecording,
  53. videoSources,
  54. showVideoSourceDropdown,
  55. showRecordingLength,
  56. onSubmit,
  57. i18n,
  58. mirror,
  59. onSnapshot,
  60. onStartRecording,
  61. onStopRecording,
  62. onDiscardRecordedVideo,
  63. recordingLengthSeconds,
  64. } = this.props
  65. const hasRecordedVideo = !!recordedVideo
  66. const shouldShowRecordButton =
  67. !hasRecordedVideo &&
  68. supportsRecording &&
  69. (isModeAvailable(modes, 'video-only') ||
  70. isModeAvailable(modes, 'audio-only') ||
  71. isModeAvailable(modes, 'video-audio'))
  72. const shouldShowSnapshotButton =
  73. !hasRecordedVideo && isModeAvailable(modes, 'picture')
  74. const shouldShowRecordingLength =
  75. supportsRecording && showRecordingLength && !hasRecordedVideo
  76. const shouldShowVideoSourceDropdown =
  77. showVideoSourceDropdown && videoSources && videoSources.length > 1
  78. const videoProps: HTMLAttributes<HTMLVideoElement> = {
  79. playsInline: true,
  80. }
  81. if (recordedVideo) {
  82. videoProps.muted = false
  83. videoProps.controls = true
  84. videoProps.src = recordedVideo
  85. // reset srcObject in dom. If not resetted, stream sticks in element
  86. if (this.videoElement) {
  87. this.videoElement.srcObject = null
  88. }
  89. } else {
  90. videoProps.muted = true
  91. videoProps.autoPlay = true
  92. // @ts-expect-error srcObject does not exist on <video> props
  93. videoProps.srcObject = src
  94. }
  95. return (
  96. <div className="uppy uppy-Webcam-container">
  97. <div className="uppy-Webcam-videoContainer">
  98. <video
  99. /* eslint-disable-next-line no-return-assign */
  100. ref={(videoElement) => (this.videoElement = videoElement!)}
  101. className={`uppy-Webcam-video ${
  102. mirror ? 'uppy-Webcam-video--mirrored' : ''
  103. }`}
  104. /* eslint-disable-next-line react/jsx-props-no-spreading */
  105. {...videoProps}
  106. />
  107. </div>
  108. <div className="uppy-Webcam-footer">
  109. <div className="uppy-Webcam-videoSourceContainer">
  110. {shouldShowVideoSourceDropdown ?
  111. VideoSourceSelect(this.props)
  112. : null}
  113. </div>
  114. <div className="uppy-Webcam-buttonContainer">
  115. {shouldShowSnapshotButton && (
  116. <SnapshotButton onSnapshot={onSnapshot} i18n={i18n} />
  117. )}
  118. {shouldShowRecordButton && (
  119. <RecordButton
  120. recording={recording}
  121. onStartRecording={onStartRecording}
  122. onStopRecording={onStopRecording}
  123. i18n={i18n}
  124. />
  125. )}
  126. {hasRecordedVideo && (
  127. <SubmitButton onSubmit={onSubmit} i18n={i18n} />
  128. )}
  129. {hasRecordedVideo && (
  130. <DiscardButton onDiscard={onDiscardRecordedVideo} i18n={i18n} />
  131. )}
  132. </div>
  133. <div className="uppy-Webcam-recordingLength">
  134. {shouldShowRecordingLength && (
  135. <RecordingLength
  136. recordingLengthSeconds={recordingLengthSeconds}
  137. i18n={i18n}
  138. />
  139. )}
  140. </div>
  141. </div>
  142. </div>
  143. )
  144. }
  145. }
  146. export default CameraScreen