gif content filtering and safari fixes

This commit is contained in:
vineyardbovines
2026-05-07 15:57:56 -04:00
parent 10cb0dbf21
commit bc34f8aa96
6 changed files with 126 additions and 49 deletions
@@ -1,4 +1,4 @@
import {ViewProps} from 'react-native'
import {type ViewProps} from 'react-native'
export interface GifViewStateChangeEvent {
nativeEvent: {
@@ -10,6 +10,12 @@ export interface GifViewStateChangeEvent {
export interface GifViewProps extends ViewProps {
autoplay?: boolean
source?: string
/**
* Web-only ordered list of `<source>` tags rendered inside `<video>`. The
* browser uses `canPlayType` to pick the first one it supports. Ignored on
* native (which uses `source` directly).
*/
sources?: ReadonlyArray<{src: string; type: string}>
placeholderSource?: string
onPlayerStateChange?: (event: GifViewStateChangeEvent) => void
}
@@ -1,17 +1,20 @@
import * as React from 'react'
import {createRef, PureComponent, type RefObject} from 'react'
import {StyleSheet} from 'react-native'
import {GifViewProps} from './GifView.types'
import {type GifViewProps} from './GifView.types'
export class GifView extends React.PureComponent<GifViewProps> {
private readonly videoPlayerRef: React.RefObject<HTMLMediaElement> =
React.createRef()
export class GifView extends PureComponent<GifViewProps> {
private readonly videoPlayerRef: RefObject<HTMLMediaElement> = createRef()
private isLoaded = false
constructor(props: GifViewProps | Readonly<GifViewProps>) {
super(props)
}
componentDidMount() {
document.addEventListener('visibilitychange', this.onVisibilityChange)
}
componentDidUpdate(prevProps: Readonly<GifViewProps>) {
if (prevProps.autoplay !== this.props.autoplay) {
if (this.props.autoplay) {
@@ -22,10 +25,28 @@ export class GifView extends React.PureComponent<GifViewProps> {
}
}
componentWillUnmount() {
document.removeEventListener('visibilitychange', this.onVisibilityChange)
}
static async prefetchAsync(_: string[]): Promise<void> {
console.warn('prefetchAsync is not supported on web')
}
// Safari pauses backgrounded `<video>` elements when the tab becomes
// inactive and does not resume them automatically when the tab is shown
// again, leaving GIFs frozen on a still frame. Resume playback when the
// page becomes visible if the consumer expects autoplay.
private onVisibilityChange = () => {
if (
document.visibilityState === 'visible' &&
this.props.autoplay &&
this.videoPlayerRef.current?.paused
) {
void this.playAsync()
}
}
private firePlayerStateChangeEvent = () => {
this.props.onPlayerStateChange?.({
nativeEvent: {
@@ -62,21 +83,29 @@ export class GifView extends React.PureComponent<GifViewProps> {
}
render() {
const {sources, source, autoplay, accessibilityLabel, style} = this.props
const useSources = sources && sources.length > 0
return (
<video
src={this.props.source}
autoPlay={this.props.autoplay ? 'autoplay' : undefined}
preload={this.props.autoplay ? 'auto' : undefined}
// When `<source>` children are present, omit `src` so the browser
// walks the source list and picks via canPlayType.
src={useSources ? undefined : source}
autoPlay={autoplay ? 'autoplay' : undefined}
preload={autoplay ? 'auto' : undefined}
playsInline={true}
loop="loop"
muted="muted"
style={StyleSheet.flatten(this.props.style)}
style={StyleSheet.flatten(style)}
onCanPlay={this.onLoad}
onPlay={this.firePlayerStateChangeEvent}
onPause={this.firePlayerStateChangeEvent}
aria-label={this.props.accessibilityLabel}
ref={this.videoPlayerRef}
/>
aria-label={accessibilityLabel}
ref={this.videoPlayerRef}>
{useSources
? sources.map(s => <source key={s.src} src={s.src} type={s.type} />)
: null}
</video>
)
}
}