Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@uppy/webcam: refactor to TypeScript #4870

Merged
merged 18 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions packages/@uppy/core/src/BasePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ export default class BasePlugin<

VERSION: string

constructor(uppy: Uppy<M, B>, opts: Opts) {
constructor(uppy: Uppy<M, B>, opts?: Partial<Opts>) {
this.uppy = uppy
this.opts = opts ?? {}
this.opts = (opts ?? {}) as Opts
}

getPluginState(): PluginState {
Expand Down
17 changes: 13 additions & 4 deletions packages/@uppy/core/src/UIPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ class UIPlugin<

title: string

getTargetPlugin(target: unknown): UIPlugin<any, any, any> | undefined {
getTargetPlugin<Me extends Meta, Bo extends Body>(
target: PluginTarget<Me, Bo>, // eslint-disable-line no-use-before-define
): UIPlugin<any, Me, Bo> | undefined {
let targetPlugin
if (typeof target === 'object' && target instanceof UIPlugin) {
// Targeting a plugin *instance*
Expand All @@ -84,9 +86,9 @@ class UIPlugin<
* If it’s an object — target is a plugin, and we search `plugins`
* for a plugin with same name and return its target.
*/
mount(
target: HTMLElement | string,
plugin: UIPlugin<any, any, any>,
mount<Me extends Meta, Bo extends Body>(
target: PluginTarget<Me, Bo>, // eslint-disable-line no-use-before-define
plugin: UIPlugin<any, Me, Bo>,
): HTMLElement {
const callerPluginName = plugin.id

Expand Down Expand Up @@ -196,3 +198,10 @@ class UIPlugin<
}

export default UIPlugin

export type PluginTarget<M extends Meta, B extends Body> =
| string
| Element
| typeof BasePlugin
| typeof UIPlugin
| BasePlugin<any, M, B>
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 } from 'preact'

export default (): JSX.Element => {
aduh95 marked this conversation as resolved.
Show resolved Hide resolved
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.

163 changes: 163 additions & 0 deletions packages/@uppy/webcam/src/CameraScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/* eslint-disable jsx-a11y/media-has-caption */
import type { I18n } from '@uppy/utils/lib/Translator'
import { h, Component } from 'preact'
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(): JSX.Element {
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: React.VideoHTMLAttributes<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!)}

Check warning on line 112 in packages/@uppy/webcam/src/CameraScreen.tsx

View workflow job for this annotation

GitHub Actions / Lint JavaScript/TypeScript

Forbidden non-null assertion
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.

Loading
Loading