Skip to content

Commit

Permalink
@uppy/webcam: refactor to TypeScript (#4870)
Browse files Browse the repository at this point in the history
Co-authored-by: Merlijn Vos <merlijn@soverin.net>
  • Loading branch information
aduh95 and Murderlon authored Mar 11, 2024
1 parent 21e4476 commit aefb325
Show file tree
Hide file tree
Showing 29 changed files with 747 additions and 402 deletions.
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ module.exports = {
{
files: [
'*.test.js',
'*.test.ts',
'test/endtoend/*.js',
'bin/**.js',
],
Expand Down
9 changes: 0 additions & 9 deletions packages/@uppy/webcam/src/CameraIcon.jsx

This file was deleted.

19 changes: 19 additions & 0 deletions packages/@uppy/webcam/src/CameraIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { h, type ComponentChild } from 'preact'

export default function CameraIcon(): ComponentChild {
return (
<svg
aria-hidden="true"
focusable="false"
fill="#0097DC"
width="66"
height="55"
viewBox="0 0 66 55"
>
<path
d="M57.3 8.433c4.59 0 8.1 3.51 8.1 8.1v29.7c0 4.59-3.51 8.1-8.1 8.1H8.7c-4.59 0-8.1-3.51-8.1-8.1v-29.7c0-4.59 3.51-8.1 8.1-8.1h9.45l4.59-7.02c.54-.54 1.35-1.08 2.16-1.08h16.2c.81 0 1.62.54 2.16 1.08l4.59 7.02h9.45zM33 14.64c-8.62 0-15.393 6.773-15.393 15.393 0 8.62 6.773 15.393 15.393 15.393 8.62 0 15.393-6.773 15.393-15.393 0-8.62-6.773-15.393-15.393-15.393zM33 40c-5.648 0-9.966-4.319-9.966-9.967 0-5.647 4.318-9.966 9.966-9.966s9.966 4.319 9.966 9.966C42.966 35.681 38.648 40 33 40z"
fillRule="evenodd"
/>
</svg>
)
}
119 changes: 0 additions & 119 deletions packages/@uppy/webcam/src/CameraScreen.jsx

This file was deleted.

164 changes: 164 additions & 0 deletions packages/@uppy/webcam/src/CameraScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/* eslint-disable jsx-a11y/media-has-caption */
import type { I18n } from '@uppy/utils/lib/Translator'
import { h, Component, type ComponentChild } from 'preact'
import type { HTMLAttributes } from 'preact/compat'
import SnapshotButton from './SnapshotButton.tsx'
import RecordButton from './RecordButton.tsx'
import RecordingLength from './RecordingLength.tsx'
import VideoSourceSelect, {
type VideoSourceSelectProps,
} from './VideoSourceSelect.tsx'
import SubmitButton from './SubmitButton.tsx'
import DiscardButton from './DiscardButton.tsx'

function isModeAvailable<T>(modes: T[], mode: any): mode is T {
return modes.includes(mode)
}

interface CameraScreenProps extends VideoSourceSelectProps {
onFocus: () => void
onStop: () => void

src: MediaStream | null
recording: boolean
modes: string[]
supportsRecording: boolean
showVideoSourceDropdown: boolean
showRecordingLength: boolean
onSubmit: () => void
i18n: I18n
mirror: boolean
onSnapshot: () => void
onStartRecording: () => void
onStopRecording: () => void
onDiscardRecordedVideo: () => void
recordingLengthSeconds: number
}

class CameraScreen extends Component<CameraScreenProps> {
private videoElement: HTMLVideoElement

refs: any

componentDidMount(): void {
const { onFocus } = this.props
onFocus()
}

componentWillUnmount(): void {
const { onStop } = this.props
onStop()
}

render(): ComponentChild {
const {
src,
// @ts-expect-error TODO: remove unused
recordedVideo,
recording,
modes,
supportsRecording,
videoSources,
showVideoSourceDropdown,
showRecordingLength,
onSubmit,
i18n,
mirror,
onSnapshot,
onStartRecording,
onStopRecording,
onDiscardRecordedVideo,
recordingLengthSeconds,
} = this.props

const hasRecordedVideo = !!recordedVideo
const shouldShowRecordButton =
!hasRecordedVideo &&
supportsRecording &&
(isModeAvailable(modes, 'video-only') ||
isModeAvailable(modes, 'audio-only') ||
isModeAvailable(modes, 'video-audio'))
const shouldShowSnapshotButton =
!hasRecordedVideo && isModeAvailable(modes, 'picture')
const shouldShowRecordingLength =
supportsRecording && showRecordingLength && !hasRecordedVideo
const shouldShowVideoSourceDropdown =
showVideoSourceDropdown && videoSources && videoSources.length > 1

const videoProps: HTMLAttributes<HTMLVideoElement> = {
playsInline: true,
}

if (recordedVideo) {
videoProps.muted = false
videoProps.controls = true
videoProps.src = recordedVideo

// reset srcObject in dom. If not resetted, stream sticks in element
if (this.videoElement) {
this.videoElement.srcObject = null
}
} else {
videoProps.muted = true
videoProps.autoPlay = true
// @ts-expect-error srcObject does not exist on <video> props
videoProps.srcObject = src
}

return (
<div className="uppy uppy-Webcam-container">
<div className="uppy-Webcam-videoContainer">
<video
/* eslint-disable-next-line no-return-assign */
ref={(videoElement) => (this.videoElement = videoElement!)}
className={`uppy-Webcam-video ${
mirror ? 'uppy-Webcam-video--mirrored' : ''
}`}
/* eslint-disable-next-line react/jsx-props-no-spreading */
{...videoProps}
/>
</div>
<div className="uppy-Webcam-footer">
<div className="uppy-Webcam-videoSourceContainer">
{shouldShowVideoSourceDropdown ?
VideoSourceSelect(this.props)
: null}
</div>
<div className="uppy-Webcam-buttonContainer">
{shouldShowSnapshotButton && (
<SnapshotButton onSnapshot={onSnapshot} i18n={i18n} />
)}

{shouldShowRecordButton && (
<RecordButton
recording={recording}
onStartRecording={onStartRecording}
onStopRecording={onStopRecording}
i18n={i18n}
/>
)}

{hasRecordedVideo && (
<SubmitButton onSubmit={onSubmit} i18n={i18n} />
)}

{hasRecordedVideo && (
<DiscardButton onDiscard={onDiscardRecordedVideo} i18n={i18n} />
)}
</div>

<div className="uppy-Webcam-recordingLength">
{shouldShowRecordingLength && (
<RecordingLength
recordingLengthSeconds={recordingLengthSeconds}
i18n={i18n}
/>
)}
</div>
</div>
</div>
)
}
}

export default CameraScreen
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type { I18n } from '@uppy/utils/lib/Translator'
import { h } from 'preact'

function DiscardButton ({ onDiscard, i18n }) {
interface DiscardButtonProps {
onDiscard: () => void
i18n: I18n
}

function DiscardButton({ onDiscard, i18n }: DiscardButtonProps): JSX.Element {
return (
<button
className="uppy-u-reset uppy-c-btn uppy-Webcam-button uppy-Webcam-button--discard"
Expand Down
11 changes: 0 additions & 11 deletions packages/@uppy/webcam/src/PermissionsScreen.jsx

This file was deleted.

28 changes: 28 additions & 0 deletions packages/@uppy/webcam/src/PermissionsScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { I18n } from '@uppy/utils/lib/Translator'
import { h, type ComponentChild } from 'preact'

interface PermissionScreenProps {
hasCamera: boolean
icon: () => ComponentChild | null
i18n: I18n
}

export default function PermissionsScreen({
icon,
i18n,
hasCamera,
}: PermissionScreenProps): JSX.Element {
return (
<div className="uppy-Webcam-permissons">
<div className="uppy-Webcam-permissonsIcon">{icon()}</div>
<h1 className="uppy-Webcam-title">
{hasCamera ? i18n('allowAccessTitle') : i18n('noCameraTitle')}
</h1>
<p>
{hasCamera ?
i18n('allowAccessDescription')
: i18n('noCameraDescription')}
</p>
</div>
)
}
Loading

0 comments on commit aefb325

Please sign in to comment.